1 // 2 // Copyright (c) 2010-2023 Antmicro 3 // 4 // This file is licensed under the MIT License. 5 // Full license text is available in 'licenses/MIT.txt'. 6 // 7 using System; 8 using System.Linq; 9 using Antmicro.Renode.Core; 10 using Antmicro.Renode.Core.Structure; 11 using Antmicro.Renode.Core.Structure.Registers; 12 using Antmicro.Renode.Logging; 13 using Antmicro.Renode.Exceptions; 14 using Antmicro.Renode.Peripherals.Bus; 15 using Antmicro.Renode.Utilities.Packets; 16 17 namespace Antmicro.Renode.Peripherals.Storage 18 { 19 // Based on JESD223E, Universal Flash Storage Host Controller Interface (UFSHCI), Version 4.0 20 public class UFSHostController : NullRegistrationPointPeripheralContainer<UFSDevice>, IProvidesRegisterCollection<DoubleWordRegisterCollection>, IDoubleWordPeripheral, IKnownSize 21 { UFSHostController(IMachine machine, int transferRequestSlots = 32, int readyToTransferRequests = 16, int taskManagementRequestSlots = 8)22 public UFSHostController(IMachine machine, int transferRequestSlots = 32, int readyToTransferRequests = 16, int taskManagementRequestSlots = 8) : base(machine) 23 { 24 if(transferRequestSlots <= 0 || transferRequestSlots > 32) 25 { 26 // Linux driver requires at least 2 slots 27 throw new ConstructionException("Minimum one and maximum 32 UTP Transfer Request slots are supported."); 28 } 29 30 if(readyToTransferRequests < 2 || readyToTransferRequests > 256) 31 { 32 throw new ConstructionException("Minimum two and maximum 256 Ready To Transfer (RTT) requests are supported."); 33 } 34 35 if(taskManagementRequestSlots <= 0 || taskManagementRequestSlots > 8) 36 { 37 throw new ConstructionException("Minimum one and maximum 8 UTP Task Management Request slots are supported."); 38 } 39 40 // Host Capabilities 41 TransferRequestSlots = transferRequestSlots; 42 ReadyToTransferRequests = readyToTransferRequests; 43 TaskManagementRequestSlots = taskManagementRequestSlots; 44 45 ExtraHeaderSegmentsLengthInUTRD = true; 46 AutoHibernation = true; 47 Addressing64Bit = true; 48 OutOfOrderDataDelivery = true; 49 50 // The following capabilities shouldn't be modified, because they are specific to this model 51 DMETestModeCommand = false; 52 CryptoSupport = false; 53 LegacySingleDoorBellRemoved = true; 54 MultiQueue = false; // Multi-Circular Queue is not supported yet 55 EventSpecificInterrupt = false; 56 57 IRQ = new GPIO(); 58 RegistersCollection = new DoubleWordRegisterCollection(this); 59 sysbus = machine.GetSystemBus(this); 60 61 DefineRegisters(); 62 Reset(); 63 } 64 Reset()65 public override void Reset() 66 { 67 aggregationCounter = 0; 68 RegistersCollection.Reset(); 69 UpdateInterrupts(); 70 } 71 ReadDoubleWord(long offset)72 public uint ReadDoubleWord(long offset) 73 { 74 return RegistersCollection.Read(offset); 75 } 76 WriteDoubleWord(long offset, uint value)77 public void WriteDoubleWord(long offset, uint value) 78 { 79 RegistersCollection.Write(offset, value); 80 } 81 82 // IKnownSize 83 public long Size => 0x2000; 84 85 public GPIO IRQ { get; } 86 87 public DoubleWordRegisterCollection RegistersCollection { get; } 88 DefineRegisters()89 private void DefineRegisters() 90 { 91 // Host Capabilities 92 Registers.HostControllerCapabilities.Define(this) 93 .WithValueField(0, 8, FieldMode.Read, valueProviderCallback: _ => (ulong)TransferRequestSlots - 1, name: "NUTRS") // '0' based value 94 .WithValueField(8, 8, FieldMode.Read, valueProviderCallback: _ => (ulong)ReadyToTransferRequests - 1, name: "NORTT") // '0' based value 95 .WithValueField(16, 3, FieldMode.Read, valueProviderCallback: _ => (ulong)TaskManagementRequestSlots - 1, name: "NUTMRS") // '0' based value 96 .WithReservedBits(19, 3) 97 .WithFlag(22, FieldMode.Read, valueProviderCallback: _ => ExtraHeaderSegmentsLengthInUTRD, name: "EHSLUTRDS") 98 .WithFlag(23, FieldMode.Read, valueProviderCallback: _ => AutoHibernation, name: "AUTOH8") 99 .WithFlag(24, FieldMode.Read, valueProviderCallback: _ => Addressing64Bit, name: "64AS") 100 .WithFlag(25, FieldMode.Read, valueProviderCallback: _ => OutOfOrderDataDelivery, name: "OODDS") 101 .WithFlag(26, FieldMode.Read, valueProviderCallback: _ => DMETestModeCommand, name: "UICDMETMS") 102 .WithTaggedFlag(" Unified Memory Extension", 27) 103 .WithFlag(28, FieldMode.Read, valueProviderCallback: _ => CryptoSupport, name: "CS") 104 .WithFlag(29, FieldMode.Read, valueProviderCallback: _ => LegacySingleDoorBellRemoved, name: "LSDBS") 105 .WithFlag(30, FieldMode.Read, valueProviderCallback: _ => MultiQueue, name: "MCQS") 106 .WithFlag(31, FieldMode.Read, valueProviderCallback: _ => EventSpecificInterrupt, name: "ESI"); 107 Registers.MCQCapabilities.Define(this) // MCQ not implemented 108 .WithTag("MAXQ", 0, 8) 109 .WithTaggedFlag("SP", 8) 110 .WithTaggedFlag("RRP", 9) 111 .WithTaggedFlag("EIS", 10) 112 .WithReservedBits(11, 5) 113 .WithTag("QCFGPTR", 16, 8) 114 .WithTag("MIAG", 24, 8); 115 Registers.UFSVersion.Define(this) 116 .WithValueField(0, 4, FieldMode.Read, valueProviderCallback: _ => VersionSuffix, name: "VS") 117 .WithValueField(4, 4, FieldMode.Read, valueProviderCallback: _ => MinorVersionNumber, name: "MNR") 118 .WithValueField(8, 8, FieldMode.Read, valueProviderCallback: _ => MajorVersionNumber, name: "MJR") 119 .WithReservedBits(16, 16); 120 Registers.ExtendedControllerCapabilities.Define(this) 121 .WithTag("wHostHintCacheSize", 0, 16) 122 .WithReservedBits(16, 16); 123 Registers.HostControllerIdentificationDescriptorProductId.Define(this) 124 .WithTag("PID", 0, 32); 125 Registers.HostControllerIdentificationDescriptorManufacturerId.Define(this) 126 .WithTag("MIC", 0, 8) 127 .WithTag("BI", 8, 8) 128 .WithReservedBits(16, 16); 129 Registers.AutoHibernateIdleTimer.Define(this) 130 .WithTag("AH8ITV", 0, 10) 131 .WithTag("TS", 10, 3) 132 .WithReservedBits(13, 19); 133 134 // Operation and Runtime 135 Registers.InterruptStatus.Define(this) 136 .WithFlags(0, 19, out interruptStatusFlags, FieldMode.Read | FieldMode.WriteOneToClear) 137 .WithTag("MCQ", 19, 3) // MCQ not implemented 138 .WithReservedBits(22, 10) 139 .WithWriteCallback((_, __) => UpdateInterrupts()); 140 Registers.InterruptEnable.Define(this) 141 .WithFlags(0, 19, out interruptEnableFlags) 142 .WithTag("MCQ", 19, 3) // MCQ not implemented 143 .WithReservedBits(22, 10) 144 .WithWriteCallback((_, __) => UpdateInterrupts()); 145 Registers.HostControllerStatusExtended.Define(this) 146 .WithValueField(0, 4, out utpErrorIID, FieldMode.Read, name: "IIDUTPE") 147 .WithValueField(4, 4, out utpErrorExtIID, FieldMode.Read, name: "EXT_IIDUTPE") 148 .WithReservedBits(8,24); 149 Registers.HostControllerStatus.Define(this) 150 .WithFlag(0, out devicePresent, FieldMode.Read, name: "DP") 151 .WithFlag(1, out transferRequestListReady, FieldMode.Read, name: "UTRLRDY") 152 .WithFlag(2, out taskManagementRequestListReady, FieldMode.Read, name: "UTMRLRDY") 153 .WithFlag(3, out uicCommandReady, FieldMode.Read, name: "UCRDY") 154 .WithReservedBits(4,4) 155 .WithEnumField(8, 3, out uicPowerModeChangeRequestStatus, FieldMode.Read, name: "UPMCRS") 156 .WithReservedBits(11, 1) 157 .WithValueField(12, 4, out utpErrorCode, FieldMode.Read, name: "UTPEC") 158 .WithValueField(16, 8, out utpErrorTaskTag, FieldMode.Read, name: "TTAGUTPE") 159 .WithValueField(24, 8, out utpErrorTargetLUN, FieldMode.Read, name: "TLUNUTPE"); 160 Registers.HostControllerEnable.Define(this) 161 .WithFlag(0, out hostControllerEnable, name: "hostControllerEnable", changeCallback: (_, val) => 162 { 163 if(val) 164 { 165 // UFS Device is ready for a link startup 166 interruptStatusFlags[(int)UFSInterruptFlag.UICLinkStartupStatus].Value = true; 167 uicCommandReady.Value = true; 168 169 ExecuteDMECommand(DMECommand.Reset); 170 ExecuteDMECommand(DMECommand.Enable); 171 172 transferRequestListReady.Value = true; 173 taskManagementRequestListReady.Value = true; 174 } 175 }) 176 .WithTaggedFlag("cryptoGeneralEnable", 1) // Crypto Engine not implemented 177 .WithReservedBits(2, 30); 178 Registers.UICErrorCodePHYAdapterLayer.Define(this) 179 .WithTag("EC", 0, 5) 180 .WithReservedBits(5, 26) 181 .WithTaggedFlag("ERR", 31); 182 Registers.UICErrorCodeDataLinkLayer.Define(this) 183 .WithTag("EC", 0, 16) 184 .WithReservedBits(16, 15) 185 .WithTaggedFlag("ERR", 31); 186 Registers.UICErrorCodeNetworkLayer.Define(this) 187 .WithTag("EC", 0, 3) 188 .WithReservedBits(3, 28) 189 .WithTaggedFlag("ERR", 31); 190 Registers.UICErrorCodeTransportLayer.Define(this) 191 .WithTag("EC", 0, 7) 192 .WithReservedBits(7, 24) 193 .WithTaggedFlag("ERR", 31); 194 Registers.UICErrorCodeDME.Define(this) 195 .WithTag("EC", 0, 4) 196 .WithReservedBits(4, 27) 197 .WithTaggedFlag("ERR", 31); 198 Registers.UTPTransferRequestInterruptAggregationControl.Define(this) 199 .WithTag("IATOVAL", 0, 8) 200 .WithValueField(8, 5, out interruptAggregationCounterThreshold, name: "IACTH") 201 .WithReservedBits(13, 3) 202 .WithFlag(16, FieldMode.Write, writeCallback: (_, newVal) => 203 { 204 if(!newVal) 205 { 206 return; 207 } 208 aggregationCounter = 0; 209 }, name: "CTR") 210 .WithReservedBits(17, 3) 211 .WithFlag(20, FieldMode.Read, valueProviderCallback: _ => aggregationCounter > 0, name: "IASB") 212 .WithReservedBits(21, 3) 213 .WithTaggedFlag("IAPWEN", 24) 214 .WithReservedBits(25, 6) 215 .WithFlag(31, out interruptAggregationEnable, name: "IAEN"); 216 217 // UTP Transfer 218 Registers.UTPTransferRequestListBaseAddressLow.Define(this) 219 .WithReservedBits(0, 10) 220 .WithValueField(10, 22, out transferRequestListBaseAddressLow, name: "UTRLBA"); 221 Registers.UTPTransferRequestListBaseAddressHigh.Define(this) 222 .WithValueField(0, 32, out transferRequestListBaseAddressHigh, name: "UTRLBAU"); 223 Registers.UTPTransferRequestDoorBell.Define(this) 224 .WithFlags(0, 32, out transferRequestListDoorBellFlags, name: "UTRLDBR") 225 .WithWriteCallback((_, __) => ProcessTransferRequestList()); 226 Registers.UTPTransferRequestListClear.Define(this) 227 .WithFlags(0, 32, out transferRequestListClearFlags, writeCallback: (idx, _, newVal) => 228 { 229 if(newVal) 230 { 231 return; 232 } 233 transferRequestListDoorBellFlags[idx].Value = false; 234 }, name: "UTRLCLR"); 235 Registers.UTPTransferRequestListRunStop.Define(this) 236 .WithFlag(0, out transferRequestListRunStop, changeCallback: (_, newVal) => 237 { 238 if(!newVal) 239 { 240 return; 241 } 242 for(int i = 0; i < transferRequestListCompletionNotifications.Length; i++) 243 { 244 transferRequestListCompletionNotifications[i].Value = false; 245 } 246 ProcessTransferRequestList(); 247 }, name: "UTRLRSR") 248 .WithReservedBits(1, 31); 249 Registers.UTPTransferRequestListCompletionNotification.Define(this) 250 .WithFlags(0, 32, out transferRequestListCompletionNotifications, FieldMode.WriteOneToClear, name: "UTRLCNR"); 251 252 // UTP Task Management 253 Registers.UTPTaskManagementRequestListBaseAddressLow.Define(this) 254 .WithReservedBits(0, 10) 255 .WithValueField(10, 22, out taskManagementRequestListBaseAddressLow, name: "UTMRLBA"); 256 Registers.UTPTaskManagementRequestListBaseAddressHigh.Define(this) 257 .WithValueField(0, 32, out taskManagementRequestListBaseAddressHigh, name: "UTMRLBAU"); 258 Registers.UTPTaskManagementRequestDoorBell.Define(this) 259 .WithFlags(0, 8, out taskManagementRequestListDoorBellFlags, name: "UTMRLDBR") 260 .WithReservedBits(8, 24) 261 .WithWriteCallback((_, __) => ProcessTaskManagementRequestList()); 262 Registers.UTPTaskManagementRequestListClear.Define(this) 263 .WithFlags(0, 8, out taskManagementRequestListClearFlags, writeCallback: (idx, _, newVal) => 264 { 265 if(newVal) 266 { 267 return; 268 } 269 taskManagementRequestListDoorBellFlags[idx].Value = false; 270 }, name: "UTMRLCLR") 271 .WithReservedBits(8, 24); 272 Registers.UTPTaskManagementRequestListRunStop.Define(this) 273 .WithFlag(0, out taskManagementRequestListRunStop, changeCallback: (_, newVal) => 274 { 275 if(!newVal) 276 { 277 return; 278 } 279 ProcessTaskManagementRequestList(); 280 }, name: "UTMRLRSR") 281 .WithReservedBits(1, 31); 282 283 // UIC Command 284 Registers.UICCommand.Define(this) 285 .WithEnumField(0, 8, out commandOpcode, writeCallback: (_, val) => ExecuteDMECommand(val), name: "CMDOP") 286 .WithReservedBits(8, 24); 287 Registers.UICCommandArg1.Define(this) 288 .WithValueField(0, 32, out arg1, name: "ARG1"); 289 Registers.UICCommandArg2.Define(this) 290 .WithValueField(0, 32, out arg2, name: "ARG2"); 291 Registers.UICCommandArg3.Define(this) 292 .WithValueField(0, 32, out arg3, name: "ARG3"); 293 294 // Crypto 295 Registers.CryptoCapability.Define(this) // Crypto Engine not implemented 296 .WithTag("CC", 0, 7) 297 .WithReservedBits(7, 1) 298 .WithTag("CFGC", 8, 8) 299 .WithReservedBits(16, 8) 300 .WithTag("CFGPTR", 24, 8); 301 302 // Config 303 Registers.GlobalConfiguration.Define(this) 304 .WithEnumField(0, 1, out queueType, writeCallback: (_, newVal) => 305 { 306 if(newVal == QueueType.MultiCircular) 307 { 308 this.Log(LogLevel.Warning, "Multi-Circular, Multi Doorbell Queue Mode is not supported"); 309 newVal = QueueType.LegacySingleDoorbell; // Legacy Single Doorbell mode is supported 310 } 311 }, name: "QT") 312 .WithFlag(1, writeCallback: (_, newVal) => 313 { 314 if(!newVal) 315 { 316 return; 317 } 318 this.Log(LogLevel.Warning, "Event Specific Interrupt topology is not supported"); 319 }, name: "ESIE") 320 .WithFlag(2, out prdtFormatEnable, name: "2DWPRDTEN") 321 .WithReservedBits(3, 29); 322 323 // MCQ Configuration 324 Registers.MCQConfig.Define(this) // MCQ not implemented 325 .WithTag("AS", 0, 2) 326 .WithReservedBits(2, 6) 327 .WithTag("MAC", 8, 9) 328 .WithReservedBits(17, 15); 329 } 330 331 /* UPIU - UFS Protocol Information Units 332 CDB - Command Descriptor Block 333 UTRD - UFS Transfer Request Descriptor */ ProcessTransferRequestList()334 private void ProcessTransferRequestList() 335 { 336 var utrlBaseAddress = transferRequestListBaseAddressHigh.Value << 32 | transferRequestListBaseAddressLow.Value << 10; 337 var utrl = sysbus.ReadBytes(utrlBaseAddress, count: UTPTransferRequestDescriptorSizeInBytes * TransferRequestSlots, onlyMemory: true); 338 // In batch mode, commands are executed from the lowest index 339 for(int i = 0; i < TransferRequestSlots; i++) 340 { 341 var doorBell = transferRequestListDoorBellFlags[i]; 342 if(!doorBell.Value) 343 { 344 continue; 345 } 346 var utrdOffset = UTPTransferRequestDescriptorSizeInBytes * i; 347 var data = utrl.Skip(utrdOffset).Take(UTPTransferRequestDescriptorSizeInBytes).ToArray(); 348 var utrd = Packet.Decode<UTPTransferRequest>(data); 349 350 var ucdBaseAddress = (ulong)utrd.UTPCommandDescriptorBaseAddressUpper << 32 | utrd.UTPCommandDescriptorBaseAddressLower << 7; 351 var requestLength = 4 * utrd.ResponseUPIUOffset; // dword offset 352 if(requestLength < UTPTransferRequestBaseUPIUSizeInBytes) 353 { 354 this.Log(LogLevel.Warning, "UFS Protocol Information Unit is too short."); 355 continue; 356 } 357 var upiuBytes = sysbus.ReadBytes(ucdBaseAddress, requestLength, onlyMemory: true); 358 359 var prdtBytes = new byte[0]; 360 if(utrd.PRDTLength > 0) 361 { 362 var prtdBaseAddress = ucdBaseAddress + 4 * (ulong)utrd.PRDTOffset; 363 var prdtBytesCount = utrd.PRDTLength * PRDTLengthInBytes; 364 prdtBytes = sysbus.ReadBytes(prtdBaseAddress, prdtBytesCount, onlyMemory: true); 365 } 366 367 var responseLength = 4 * utrd.ResponseUPIULength; 368 var responseBytes = HandleUTPTransaction(utrd, upiuBytes, prdtBytes); 369 responseBytes = responseBytes.Take(responseLength).ToArray(); 370 371 var responseUPIUBaseAddress = ucdBaseAddress + 4 * (ulong)utrd.ResponseUPIUOffset; 372 sysbus.WriteBytes(responseBytes, responseUPIUBaseAddress, onlyMemory: true); 373 374 var responseBasicHeaderData = responseBytes.Take(BasicHeaderLength).ToArray(); 375 var responseBasicHeader = Packet.Decode<BasicUPIUHeader>(responseBasicHeaderData); 376 377 // Update Overall Command Status field of utrd 378 utrd.OverallCommandStatus = (UTPTransferStatus)responseBasicHeader.Response; 379 var utrdUpdated = Packet.Encode<UTPTransferRequest>(utrd); 380 sysbus.WriteBytes(utrdUpdated, utrlBaseAddress + (ulong)utrdOffset); 381 382 doorBell.Value = false; 383 transferRequestListCompletionNotifications[i].Value = true; 384 385 if(queueType.Value == QueueType.LegacySingleDoorbell && 386 (utrd.Interrupt || utrd.OverallCommandStatus != UTPTransferStatus.Success || 387 (aggregationCounter >= interruptAggregationCounterThreshold.Value && interruptAggregationEnable.Value))) 388 { 389 interruptStatusFlags[(int)UFSInterruptFlag.UTPTransferRequestCompletionStatus].Value = true; 390 UpdateInterrupts(); 391 } 392 } 393 } 394 HandleUTPTransaction(UTPTransferRequest utrd, byte[] requestBytes, byte[] prdtBytes)395 private byte[] HandleUTPTransaction(UTPTransferRequest utrd, byte[] requestBytes, byte[] prdtBytes) 396 { 397 var dataOutTransfer = PrepareDataOutTransfer(utrd, prdtBytes); 398 // The UniPro Service Data Unit requires no additional headers or trailer wrapped around the UPIU structure. 399 var responseBytes = RegisteredPeripheral.HandleRequest(requestBytes, dataOutTransfer, out var dataInTransfer); 400 401 if(dataInTransfer != null) 402 { 403 HandleDataInTransfer(utrd, prdtBytes, dataInTransfer); 404 } 405 406 var basicHeaderBytes = responseBytes.Take(BasicHeaderLength).ToArray(); 407 var basicHeader = Packet.Decode<BasicUPIUHeader>(basicHeaderBytes); 408 var transactionCode = (UPIUTransactionCodeTargetToInitiator)basicHeader.TransactionCode; 409 410 switch(transactionCode) 411 { 412 case UPIUTransactionCodeTargetToInitiator.Response: 413 { 414 if(!utrd.Interrupt && interruptAggregationEnable.Value) 415 { 416 aggregationCounter++; 417 } 418 break; 419 } 420 case UPIUTransactionCodeTargetToInitiator.TaskManagementResponse: 421 case UPIUTransactionCodeTargetToInitiator.QueryResponse: 422 case UPIUTransactionCodeTargetToInitiator.NopIn: 423 { 424 // Shouldn't be counted towards interrupt aggregation 425 break; // finish UTP transaction 426 } 427 default: 428 // DataIn / ReadyToTransfer should be handled within dataInTransfer / dataOutTransfer blocks 429 // because we control both Host Controller and Device implementation 430 this.Log(LogLevel.Warning, "Unexpected response received from UFS device"); 431 break; 432 } 433 return responseBytes; 434 } 435 PrepareDataOutTransfer(UTPTransferRequest utrd, byte[] prdtBytes)436 private byte[] PrepareDataOutTransfer(UTPTransferRequest utrd, byte[] prdtBytes) 437 { 438 var prdtLength = utrd.PRDTLength; 439 var dataOutTransfer = new byte[prdtLength][]; 440 var totalDataLength = 0; 441 442 for(int i = 0; i < prdtLength; i++) 443 { 444 var prdtEntryBytes = prdtBytes.Skip(i * PRDTLengthInBytes).Take(PRDTLengthInBytes).ToArray(); 445 var dataSegmentLength = GetMemoryBlockDescription(utrd, prdtEntryBytes, i == prdtLength - 1, out var dataBaseAddress); 446 var dataSegment = sysbus.ReadBytes(dataBaseAddress, dataSegmentLength, onlyMemory: true); 447 dataOutTransfer[i] = dataSegment; 448 totalDataLength += dataSegmentLength; 449 } 450 451 var dataOut = new byte[totalDataLength]; 452 var offset = 0; 453 454 for(int i = 0; i < prdtLength; i++) 455 { 456 var dataSegmentLength = dataOutTransfer[i].Length; 457 Array.Copy(dataOutTransfer[i], 0, dataOut, offset, dataSegmentLength); 458 offset += dataSegmentLength; 459 } 460 461 return dataOut; 462 } 463 HandleDataInTransfer(UTPTransferRequest utrd, byte[] prdtBytes, byte[] dataInTransfer)464 private void HandleDataInTransfer(UTPTransferRequest utrd, byte[] prdtBytes, byte[] dataInTransfer) 465 { 466 var prdtLength = utrd.PRDTLength; 467 var dataTransferLength = dataInTransfer.Length; 468 var remainingBytes = dataTransferLength; 469 var offset = 0; 470 for(int i = 0; i < prdtLength; i++) 471 { 472 var prdtEntryBytes = prdtBytes.Skip(i * PRDTLengthInBytes).Take(PRDTLengthInBytes).ToArray(); 473 var dataSegmentLength = GetMemoryBlockDescription(utrd, prdtEntryBytes, i == prdtLength-1, out var dataBaseAddress); 474 if(i < prdtLength - 1 && remainingBytes < dataSegmentLength) 475 { 476 this.Log(LogLevel.Warning, $"Data In transfer too short - it's not the last packet. Expected {dataSegmentLength}, got {dataTransferLength}"); 477 break; 478 } 479 var dataSegment = dataInTransfer.Skip(offset).Take(Math.Min(dataSegmentLength, remainingBytes)).ToArray(); 480 sysbus.WriteBytes(dataSegment, dataBaseAddress, onlyMemory: true); 481 offset += dataSegmentLength; 482 remainingBytes -= dataSegmentLength; 483 } 484 } 485 GetMemoryBlockDescription(UTPTransferRequest utrd, byte[] prdtEntryBytes, bool isLastBlock, out ulong dataBaseAddress)486 private int GetMemoryBlockDescription(UTPTransferRequest utrd, byte[] prdtEntryBytes, bool isLastBlock, out ulong dataBaseAddress) 487 { 488 uint dataSegmentLength; 489 if(prdtFormatEnable.Value) 490 { 491 var prdt2dw = Packet.Decode<PRDT2DW>(prdtEntryBytes); 492 dataBaseAddress = prdt2dw.DataBaseAddressUpper << 32 | (prdt2dw.DataBaseAddress << 2); 493 dataSegmentLength = isLastBlock ? (4u * utrd.LastDataByteCount) : (4096u * utrd.CommonDataSize); 494 } 495 else 496 { 497 var prdt4dw = Packet.Decode<PRDT4DW>(prdtEntryBytes); 498 dataBaseAddress = prdt4dw.DataBaseAddressUpper << 32 | (prdt4dw.DataBaseAddress << 2); 499 dataSegmentLength = prdt4dw.DataByteCount + 1; // dataByteCount is 0 based value - 0 means 1, 1 means 2 etc 500 } 501 return (int)dataSegmentLength; 502 } 503 ProcessTaskManagementRequestList()504 private void ProcessTaskManagementRequestList() 505 { 506 var utmrlBaseAddress = taskManagementRequestListBaseAddressHigh.Value << 32 | taskManagementRequestListBaseAddressLow.Value << 10; 507 var utmrl = sysbus.ReadBytes(utmrlBaseAddress, UTPTaskManagementRequestDescriptorSizeInBytes * TaskManagementRequestSlots, onlyMemory: true); 508 // In batch mode, commands are executed from the lowest index 509 for(int i = 0; i < TaskManagementRequestSlots; i++) 510 { 511 if(!taskManagementRequestListDoorBellFlags[i].Value) 512 { 513 continue; 514 } 515 var utmrdOffset = UTPTaskManagementRequestDescriptorSizeInBytes * i; 516 517 var data = utmrl.Skip(count: utmrdOffset).Take(UTPTaskManagementRequestDescriptorSizeInBytes).ToArray(); 518 var headerBytes = data.Take(UTPTaskManagementRequestHeaderSizeInBytes).ToArray(); 519 var utmrd = Packet.Decode<UTPTaskManagementRequestHeader>(headerBytes); 520 521 var upiuBytes = data.Skip(UTPTaskManagementRequestHeaderSizeInBytes).Take(UTPTaskManagementRequestUPIUSizeInBytes).ToArray(); 522 523 var responseBytes = RegisteredPeripheral.HandleRequest(upiuBytes, null, out var _); 524 responseBytes = responseBytes.Take(UTPTaskManagementResponseUPIUSizeInBytes).ToArray(); 525 sysbus.WriteBytes(responseBytes, utmrlBaseAddress + (ulong)utmrdOffset + UTPTaskManagementRequestHeaderSizeInBytes + UTPTaskManagementRequestUPIUSizeInBytes, onlyMemory: true); 526 527 var responseBasicHeaderData = responseBytes.Take(BasicHeaderLength).ToArray(); 528 var responseBasicHeader = Packet.Decode<BasicUPIUHeader>(responseBasicHeaderData); 529 530 // Update Overall Command Status field 531 utmrd.OverallCommandStatus = (UTPTaskManagementStatus)responseBasicHeader.Response; 532 var utmrdUpdated = Packet.Encode<UTPTaskManagementRequestHeader>(utmrd); 533 sysbus.WriteBytes(utmrdUpdated, utmrlBaseAddress + (ulong)utmrdOffset); 534 taskManagementRequestListDoorBellFlags[i].Value = false; 535 536 if(queueType.Value == QueueType.LegacySingleDoorbell && utmrd.Interrupt) 537 { 538 interruptStatusFlags[(int)UFSInterruptFlag.UTPTaskManagementRequestCompletionStatus].Value = true; 539 UpdateInterrupts(); 540 } 541 } 542 } 543 ExecuteDMECommand(DMECommand command)544 private void ExecuteDMECommand(DMECommand command) 545 { 546 // DME commands are part of MIPI UniPro specification 547 switch(command) 548 { 549 case DMECommand.Get: 550 var mibAttr = arg1.Value >> 16; 551 if(mibAttr == UniProPowerStateAttribute) 552 { 553 arg3.Value = (ulong)LinkStatus.Up; 554 } 555 interruptStatusFlags[(int)UFSInterruptFlag.UICCommandCompletionStatus].Value = true; 556 UpdateInterrupts(); 557 break; 558 case DMECommand.Set: 559 case DMECommand.PeerSet: 560 interruptStatusFlags[(int)UFSInterruptFlag.UICCommandCompletionStatus].Value = true; 561 UpdateInterrupts(); 562 break; 563 case DMECommand.LinkStartup: 564 interruptStatusFlags[(int)UFSInterruptFlag.UICCommandCompletionStatus].Value = true; 565 UpdateInterrupts(); 566 if(RegisteredPeripheral != null) 567 { 568 devicePresent.Value = true; 569 } 570 break; 571 case DMECommand.Enable: 572 case DMECommand.Reset: 573 this.Log(LogLevel.Debug, $"Part of auto-initialization: {Enum.GetName(typeof(DMECommand), command)}"); 574 break; 575 default: 576 this.Log(LogLevel.Warning, $"Unhandled UIC Command 0x{command:X}"); 577 break; 578 } 579 } 580 UpdateInterrupts()581 private void UpdateInterrupts() 582 { 583 var flag = false; 584 for(int i = 0; i < interruptStatusFlags.Length; i++) 585 { 586 flag |= interruptStatusFlags[i].Value && interruptEnableFlags[i].Value; 587 } 588 589 IRQ.Set(flag); 590 } 591 592 public int TransferRequestSlots { get; } 593 public int ReadyToTransferRequests { get; } 594 public int TaskManagementRequestSlots { get; } 595 596 public bool ExtraHeaderSegmentsLengthInUTRD { get; } 597 public bool AutoHibernation { get; } 598 public bool Addressing64Bit { get; } 599 public bool OutOfOrderDataDelivery { get; } 600 public bool DMETestModeCommand { get; } 601 public bool CryptoSupport { get; } 602 public bool LegacySingleDoorBellRemoved { get; } 603 public bool MultiQueue { get; } 604 public bool EventSpecificInterrupt { get; } 605 public int PRDTLengthInBytes => prdtFormatEnable.Value ? 8 : 16; 606 public static string UFSVersion => $"{MajorVersionNumber}.{MinorVersionNumber}{VersionSuffix}"; 607 608 private IFlagRegisterField hostControllerEnable; 609 610 // UTPTransferRequestInterruptAggregationControl 611 private IValueRegisterField interruptAggregationCounterThreshold; 612 private IFlagRegisterField interruptAggregationEnable; 613 614 private IEnumRegisterField<DMECommand> commandOpcode; 615 private IValueRegisterField arg1; 616 private IValueRegisterField arg2; 617 private IValueRegisterField arg3; 618 619 private IFlagRegisterField[] interruptEnableFlags; 620 private IFlagRegisterField[] interruptStatusFlags; 621 // HostControllerStatusExtended 622 private IValueRegisterField utpErrorIID; 623 private IValueRegisterField utpErrorExtIID; 624 // HostControllerStatus 625 private IFlagRegisterField devicePresent; 626 private IFlagRegisterField transferRequestListReady; 627 private IFlagRegisterField taskManagementRequestListReady; 628 private IFlagRegisterField uicCommandReady; 629 private IEnumRegisterField<UICPowerModeChangeRequestStatus> uicPowerModeChangeRequestStatus; 630 private IValueRegisterField utpErrorCode; 631 private IValueRegisterField utpErrorTaskTag; 632 private IValueRegisterField utpErrorTargetLUN; 633 // UTP Transfer 634 private IValueRegisterField transferRequestListBaseAddressLow; 635 private IValueRegisterField transferRequestListBaseAddressHigh; 636 private IFlagRegisterField[] transferRequestListDoorBellFlags; 637 private IFlagRegisterField[] transferRequestListClearFlags; 638 private IFlagRegisterField transferRequestListRunStop; 639 private IFlagRegisterField[] transferRequestListCompletionNotifications; 640 // UTP Task Management 641 private IValueRegisterField taskManagementRequestListBaseAddressLow; 642 private IValueRegisterField taskManagementRequestListBaseAddressHigh; 643 private IFlagRegisterField[] taskManagementRequestListDoorBellFlags; 644 private IFlagRegisterField[] taskManagementRequestListClearFlags; 645 private IFlagRegisterField taskManagementRequestListRunStop; 646 //GlobalConfiguration 647 private IEnumRegisterField<QueueType> queueType; 648 private IFlagRegisterField prdtFormatEnable; 649 650 private ulong aggregationCounter; 651 private readonly IBusController sysbus; 652 653 private const ushort UniProPowerStateAttribute = 0xd083; 654 private const int MajorVersionNumber = 4; 655 private const int MinorVersionNumber = 0; 656 private const int VersionSuffix = 0; 657 private const int UTPTaskManagementRequestDescriptorSizeInBytes = 80; 658 private const int UTPTaskManagementRequestUPIUSizeInBytes = 32; 659 private const int UTPTaskManagementResponseUPIUSizeInBytes = 32; 660 private const int UTPTaskManagementRequestHeaderSizeInBytes = 16; 661 private const int UTPTransferRequestDescriptorSizeInBytes = 32; 662 private const int UTPTransferRequestBaseUPIUSizeInBytes = 32; 663 private const int BasicHeaderLength = 12; 664 665 private enum DMECommand: byte 666 { 667 // Configuration commands 668 Get = 0x01, 669 Set = 0x02, 670 PeerGet = 0x03, 671 PeerSet = 0x04, 672 // Control commands 673 PowerOn = 0x10, // optional 674 PowerOff = 0x11, //optional 675 Enable = 0x12, 676 Reset = 0x14, 677 EndpointReset = 0x15, 678 LinkStartup = 0x16, 679 HibernateEnter = 0x17, 680 HibernateExit = 0x18, 681 TestMode = 0x1a, // optional 682 } 683 684 private enum UICPowerModeChangeRequestStatus: byte 685 { 686 PowerOk = 0x0, 687 PowerLocal = 0x1, 688 PowerRemote = 0x2, 689 PowerBusy = 0x3, 690 PowerErrorCapability = 0x4, 691 PowerFatalError = 0x5, 692 } 693 694 private enum UFSInterruptFlag 695 { 696 UTPTransferRequestCompletionStatus = 0, // UTRCS 697 UICDMEEndpointResetIndication = 1, // UDEPRI 698 UICError = 2, // UE 699 UICTestModeStatus = 3, // UTMS 700 UICPowerModeStatus = 4, // UPMS 701 UICHibernateExitStatus = 5, // UHXS 702 UICHibernateEnterStatus = 6, // UHES 703 UICLinkLostStatus = 7, // ULLS 704 UICLinkStartupStatus = 8, // ULSS 705 UTPTaskManagementRequestCompletionStatus = 9, // UTMRCS 706 UICCommandCompletionStatus = 10, // UCCS 707 DeviceFatalErrorStatus = 11, // DFES 708 UTPErrorStatus = 12, // UTPES 709 HostControllerFatalErrorStatus = 16, // HCFES 710 SystemBusFatalErrorStatus = 17, // SBFES 711 CryptoEngineFatalErrorStatus = 18, // CEFES 712 } 713 714 private enum LinkStatus 715 { 716 Down = 1, 717 Up = 2, 718 } 719 720 private enum QueueType 721 { 722 LegacySingleDoorbell = 0, 723 MultiCircular = 1 724 } 725 726 private enum Registers : uint 727 { 728 // Host Capabilities 729 HostControllerCapabilities = 0x00, 730 MCQCapabilities = 0x04, 731 UFSVersion = 0x08, 732 ExtendedControllerCapabilities = 0xc, 733 HostControllerIdentificationDescriptorProductId = 0x10, 734 HostControllerIdentificationDescriptorManufacturerId = 0x14, 735 AutoHibernateIdleTimer = 0x18, 736 // Operation and Runtime 737 InterruptStatus = 0x20, 738 InterruptEnable = 0x24, 739 HostControllerStatusExtended = 0x2c, 740 HostControllerStatus = 0x30, 741 HostControllerEnable = 0x34, 742 UICErrorCodePHYAdapterLayer = 0x38, 743 UICErrorCodeDataLinkLayer = 0x3c, 744 UICErrorCodeNetworkLayer = 0x40, 745 UICErrorCodeTransportLayer = 0x44, 746 UICErrorCodeDME = 0x48, 747 UTPTransferRequestInterruptAggregationControl = 0x4c, 748 // UTP Transfer 749 UTPTransferRequestListBaseAddressLow = 0x50, 750 UTPTransferRequestListBaseAddressHigh = 0x54, 751 UTPTransferRequestDoorBell = 0x58, 752 UTPTransferRequestListClear = 0x5c, 753 UTPTransferRequestListRunStop = 0x60, 754 UTPTransferRequestListCompletionNotification = 0x64, 755 // UTP Task Management 756 UTPTaskManagementRequestListBaseAddressLow = 0x70, 757 UTPTaskManagementRequestListBaseAddressHigh = 0x74, 758 UTPTaskManagementRequestDoorBell = 0x78, 759 UTPTaskManagementRequestListClear = 0x7c, 760 UTPTaskManagementRequestListRunStop = 0x80, 761 // UIC Command 762 UICCommand = 0x90, 763 UICCommandArg1 = 0x94, 764 UICCommandArg2 = 0x98, 765 UICCommandArg3 = 0x9c, 766 // Crypto 767 CryptoCapability = 0x100, 768 // Config 769 GlobalConfiguration = 0x300, 770 // MCQ Configuration 771 MCQConfig = 0x380, 772 } 773 } 774 }