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 }