1 //
2 // Copyright (c) 2010-2024 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.IO;
9 using System.Linq;
10 using System.Text;
11 using System.Collections.Generic;
12 using Antmicro.Renode.Logging;
13 using Antmicro.Renode.Storage;
14 using Antmicro.Renode.Utilities;
15 using Antmicro.Renode.Exceptions;
16 using Antmicro.Renode.Utilities.Packets;
17 using Antmicro.Renode.Storage.SCSI;
18 using Antmicro.Renode.Storage.SCSI.Commands;
19 
20 namespace Antmicro.Renode.Peripherals.Storage
21 {
22     // Based on JESD220F, Universal Flash Storage (UFS), Version 4.0
23     public class UFSDevice : IPeripheral, IDisposable
24     {
UFSDevice(int logicalUnits, ulong logicalBlockSize, ulong blockCount, string manufacturerName = R, string productName = R, string serialNumber = R, string oemID = R, string productRevisionLevel = R)25         public UFSDevice(int logicalUnits, ulong logicalBlockSize, ulong blockCount,
26                 string manufacturerName = "01234567", string productName = "0123456789ABCDEF", string serialNumber = "SomeSerialNumber",
27                 string oemID = "SomeOemID", string productRevisionLevel = "0123")
28         {
29             if(logicalUnits <= 1 || logicalUnits > MaxLogicalUnits)
30             {
31                 throw new ConstructionException($"Minimum one and maximum {MaxLogicalUnits} logical units are allowed.");
32             }
33 
34             if(!Misc.IsPowerOfTwo(logicalBlockSize) || logicalBlockSize < MinimumLogicalBlockSize)
35             {
36                 throw new ConstructionException($"Logical block size must be a power of two and at least {MinimumLogicalBlockSize}, but got {logicalBlockSize}.");
37             }
38 
39             if(manufacturerName.Length != ManufacturerNameLength)
40             {
41                 manufacturerName = manufacturerName.PadRight(ManufacturerNameLength).Substring(0, ManufacturerNameLength);
42                 this.Log(LogLevel.Warning, $"Manufacturer Name String Descriptor must have exactly {ManufacturerNameLength} characters. String was normalized.");
43             }
44 
45             if(productName.Length != ProductNameLength)
46             {
47                 productName = productName.PadRight(totalWidth: ProductNameLength).Substring(0, ProductNameLength);
48                 this.Log(LogLevel.Warning, $"Product Name String Descriptor must have exactly {ProductNameLength} characters. String was normalized.");
49             }
50 
51             if(productRevisionLevel.Length != ProductRevisionLevelLength)
52             {
53                 productRevisionLevel = productRevisionLevel.PadRight(totalWidth: ProductRevisionLevelLength).Substring(0, ProductRevisionLevelLength);
54                 this.Log(LogLevel.Warning, $"Product Revision Level String Descriptor must have exactly {ProductRevisionLevelLength} characters. String was normalized.");
55             }
56 
57             if(serialNumber.Length > SerialNumberMaxLength)
58             {
59                 serialNumber = serialNumber.Substring(0, SerialNumberMaxLength);
60                 this.Log(LogLevel.Warning, $"Serial Number String Descriptor can consist of up to {SerialNumberMaxLength} characters. String was normalized.");
61             }
62 
63             if(oemID.Length > OemIDMaxLength)
64             {
65                 oemID = oemID.Substring(0, OemIDMaxLength);
66                 this.Log(LogLevel.Warning, $"OEM ID String Descriptor can consist of up to {OemIDMaxLength} characters. String was normalized.");
67             }
68 
69             LogicalUnits = logicalUnits;
70             LogicalBlockCount = blockCount;
71             LogicalBlockSize = logicalBlockSize;
72 
73             MaxInBufferSize = MaxAllowedInBufferSize; // number of 512-byte units
74             MaxOutBufferSize = MaxAllowedOutBufferSize; // number of 512-byte units
75 
76             // Device specific properties - values set by manufacturer
77             IndexManufacturerName = 0;
78             IndexProductName = 1;
79             IndexSerialNumber = 2;
80             IndexOemID = 3;
81             IndexProductRevisionLevel = 4;
82 
83             ManufacturerName = manufacturerName;
84             ProductName = productName;
85             SerialNumber = serialNumber;
86             OemID = oemID;
87             ProductRevisionLevel = productRevisionLevel;
88 
89             dataBackends = new Stream[LogicalUnits];
90             for(int i = 0; i < LogicalUnits; i++)
91             {
92                 dataBackends[i] = DataStorage.Create(size: (long)logicalBlockSize * (long)blockCount);
93             }
94             InitConfiguration();
95         }
96 
LoadFromFile(uint logicalUnitNumber, string file, bool persistent = false)97         public void LoadFromFile(uint logicalUnitNumber, string file, bool persistent = false)
98         {
99             if(logicalUnitNumber >= LogicalUnits)
100             {
101                 this.Log(LogLevel.Error, "Invalid logical unit number");
102                 return;
103             }
104             dataBackends[logicalUnitNumber].Dispose();
105             dataBackends[logicalUnitNumber] = DataStorage.Create(file, persistent: persistent);
106         }
107 
Dispose()108         public void Dispose()
109         {
110             for(int i = 0; i < LogicalUnits; i++)
111             {
112                 dataBackends[i].Dispose();
113             }
114         }
115 
Reset()116         public void Reset()
117         {
118             for(int i = 0; i < LogicalUnits; i++)
119             {
120                 dataBackends[i].Position = 0;
121             }
122             InitConfiguration();
123         }
124 
HandleRequest(byte[] requestData, byte[] dataOutTransfer, out byte[] dataInTransfer)125         public byte[] HandleRequest(byte[] requestData, byte[] dataOutTransfer, out byte[] dataInTransfer)
126         {
127             dataInTransfer = null;
128             var basicUPIUHeader = Packet.Decode<BasicUPIUHeader>(requestData);
129             var transactionCode = (UPIUTransactionCodeInitiatorToTarget)basicUPIUHeader.TransactionCode;
130             switch(transactionCode)
131             {
132                 case UPIUTransactionCodeInitiatorToTarget.NopOut:
133                 {
134                     return HandleNopOutRequest(requestData);
135                 }
136                 case UPIUTransactionCodeInitiatorToTarget.Command:
137                 {
138                     return HandleCommandRequest(requestData, dataOutTransfer, out dataInTransfer);
139                 }
140                 case UPIUTransactionCodeInitiatorToTarget.TaskManagementRequest:
141                 {
142                     return HandleTaskManagementRequest(requestData);
143                 }
144                 case UPIUTransactionCodeInitiatorToTarget.QueryRequest:
145                 {
146                     return HandleQueryRequest(requestData);
147                 }
148                 default:
149                     // Data Out UPIUs are handled as a part of whole request (dataOutTransfer)
150                     this.Log(LogLevel.Warning, "Unhandled UFS UPIU with transaction code {0}", transactionCode);
151                     return new byte[0];
152             }
153         }
154 
HandleNopOutRequest(byte[] requestData)155         private byte[] HandleNopOutRequest(byte[] requestData)
156         {
157             var nopOut = Packet.Decode<NopOutUPIU>(requestData);
158             var nopIn = HandleNopOut(nopOut);
159             var responseData = Packet.Encode<NopInUPIU>(nopIn);
160             return responseData;
161         }
162 
HandleNopOut(NopOutUPIU request)163         private NopInUPIU HandleNopOut(NopOutUPIU request)
164         {
165             var nopIn = new NopInUPIU
166             {
167                 DataSegmentsCRC = false,
168                 HeaderSegmentsCRC = false,
169                 TransactionCode = (byte)UPIUTransactionCodeTargetToInitiator.NopIn,
170                 Flags = 0,
171                 TaskTag = request.TaskTag,
172                 Response = (byte)UTPTransferStatus.Success,
173                 DataSegmentLength = 0,
174             };
175             return nopIn;
176         }
177 
HandleCommandRequest(byte[] requestData, byte[] dataOutTransfer, out byte[] dataInTransfer)178         private byte[] HandleCommandRequest(byte[] requestData, byte[] dataOutTransfer, out byte[] dataInTransfer)
179         {
180             var command = Packet.Decode<CommandUPIU>(requestData);
181             var responseData = HandleCommand(command, dataOutTransfer, out dataInTransfer);
182             return responseData;
183         }
184 
HandleCommand(CommandUPIU request, byte[] dataOutTransfer, out byte[] dataInTransfer)185         private byte[] HandleCommand(CommandUPIU request, byte[] dataOutTransfer, out byte[] dataInTransfer)
186         {
187             dataInTransfer = null;
188 
189             var lun = request.LogicalUnitNumber;
190             var cdbBytes = request.CommandDescriptorBlock;
191             var senseData = new byte[0];
192             var status = SCSIStatus.Good;
193 
194             if(!unitDescriptors.ContainsKey(lun))
195             {
196                 status = SCSIStatus.CheckCondition;
197                 this.Log(LogLevel.Warning, "Command UPIU: Invalid LUN");
198             }
199             else
200             {
201                 HandleSCSICommand(lun, cdbBytes, dataOutTransfer, out dataInTransfer, out senseData);
202             }
203 
204             var response = new ResponseUPIU
205             {
206                 TransactionCode = (byte)UPIUTransactionCodeTargetToInitiator.Response,
207                 LogicalUnitNumber = request.LogicalUnitNumber,
208                 TaskTag = request.TaskTag,
209                 InitiatorID = request.InitiatorID,
210                 CommandSetType = request.CommandSetType,
211                 NexusInitiatorID = request.NexusInitiatorID,
212                 Response = (byte)UTPTransferStatus.Success,
213                 Status = (byte)status,
214                 TotalEHSLength = 0,
215                 DeviceInformation = 0,
216                 DataSegmentLength = (ushort)senseData.Length, // used for error reporting
217                 ResidualTransferCount = 0
218             };
219 
220             var responseBytes = Packet.Encode(response);
221             return responseBytes;
222         }
223 
HandleSCSICommand(byte lun, byte[] cdb, byte[] dataOutTransfer, out byte[] dataInTransfer, out byte[] senseData)224         public void HandleSCSICommand(byte lun, byte[] cdb, byte[] dataOutTransfer, out byte[] dataInTransfer, out byte[] senseData)
225         {
226             senseData = new byte[0];
227             dataInTransfer = null;
228             var wellKnownLUN = IsWellKnownLU(lun);
229             var opcode = (SCSICommand)cdb[0];
230             switch(opcode)
231             {
232                 case SCSICommand.Inquiry:
233                 {
234                     HandleSCSIInquiry(lun, cdb, out dataInTransfer, out senseData);
235                     break;
236                 }
237                 case SCSICommand.Read10:
238                 {
239                     if(wellKnownLUN)
240                     {
241                         this.Log(LogLevel.Warning, "Ignoring Read (10) SCSI command to Well Known LUN");
242                         break;
243                     }
244                     HandleSCSIRead10(lun, cdb, out dataInTransfer, out senseData);
245                     break;
246                 }
247                 case SCSICommand.Write10:
248                 {
249                     if(wellKnownLUN)
250                     {
251                         this.Log(LogLevel.Warning, "Ignoring Write (10) SCSI command to Well Known LUN");
252                         break;
253                     }
254                     HandleSCSIWrite10(lun, cdb, dataOutTransfer, out senseData);
255                     break;
256                 }
257                 case SCSICommand.TestUnitReady:
258                 {
259                     this.Log(LogLevel.Debug, "Unit {0} is ready.", lun);
260                     break;
261                 }
262                 case SCSICommand.ReadCapacity16:
263                 {
264                     HandleSCSIReadCapacity16(lun, cdb, out dataInTransfer, out senseData);
265                     break;
266                 }
267                 case SCSICommand.ReportLUNs:
268                 {
269                     HandleSCSIReportLUNs(lun, cdb, out dataInTransfer, out senseData);
270                     break;
271                 }
272                 default:
273                 {
274                     this.Log(LogLevel.Warning, "Unhandled SCSI command: {0}", Enum.GetName(typeof(SCSICommand), opcode));
275                     break;
276                 }
277             }
278         }
279 
HandleSCSIInquiry(byte lun, byte[] cdb, out byte[] dataInTransfer, out byte[] senseData)280         private void HandleSCSIInquiry(byte lun, byte[] cdb, out byte[] dataInTransfer, out byte[] senseData)
281         {
282             // Error handling for SCSI subsystem is omitted, so sense data is not used yet
283             senseData = new byte[0];
284             dataInTransfer = new byte[0];
285             var scsi = Packet.Decode<Inquiry>(cdb);
286 
287             if(scsi.EnableVitalProductData)
288             {
289                 var vpdPage = new VitalProductDataPageHeader
290                 {
291                     PeripheralQualifier = 0b000,
292                     PeripheralDeviceType = IsWellKnownLU(lun) ? (byte)0x1e : (byte)0x00,
293                     PageCode = scsi.PageCode,
294                 };
295 
296                 switch(scsi.PageCode)
297                 {
298                     case VitalProductDataPageCode.SupportedVPDPages:
299                     {
300                         vpdPage.PageLength = (ushort)SupportedVPDPages.Length;
301                         var vpdPageHeader = Packet.Encode(vpdPage);
302                         dataInTransfer = new byte[vpdPageHeader.Length + vpdPage.PageLength];
303                         Array.Copy(vpdPageHeader, dataInTransfer, vpdPageHeader.Length);
304                         Array.Copy(SupportedVPDPages, 0, dataInTransfer, vpdPageHeader.Length, SupportedVPDPages.Length);
305                         break;
306                     }
307                     case VitalProductDataPageCode.ModePagePolicy:
308                     {
309                         var modePagePolicyDescriptor = new ModePagePolicyDescriptor
310                         {
311                             // Combination of the following PolicyPageCode and PolicySubpageCode means that descriptor
312                             // applies to all mode pages and subpages not described by other mode page policy descriptors.
313                             // Because ModePagePolicy is always set to Shared for UFS device, it allows to simplify returned structure.
314                             PolicyPageCode = 0x3f,
315                             PolicySubpageCode = 0xff,
316                             ModePagePolicy = ModePagePolicy.Shared,
317                             MultipleLogicalUnitsShare = false
318                         };
319 
320                         var descriptor = Packet.Encode(modePagePolicyDescriptor);
321                         vpdPage.PageLength = (ushort)descriptor.Length;
322                         var vpdPageHeader = Packet.Encode(vpdPage);
323                         dataInTransfer = new byte[vpdPageHeader.Length + vpdPage.PageLength];
324                         Array.Copy(vpdPageHeader, dataInTransfer, vpdPageHeader.Length);
325                         Array.Copy(descriptor, 0, dataInTransfer, vpdPageHeader.Length, descriptor.Length);
326                         break;
327                     }
328                     default:
329                     {
330                         // Support for other VPD pages is optional in UFS device
331                         this.Log(LogLevel.Warning, "Inquiry: vital product data was requested for page {0} but not supported", scsi.PageCode);
332                         break;
333                     }
334                 }
335             }
336             else if(!scsi.EnableVitalProductData && scsi.PageCode == 0)
337             {
338                 var inquiryResponse = new StandardInquiryResponse
339                 {
340                     PeripheralQualifier = 0b000,
341                     PeripheralDeviceType = IsWellKnownLU(lun) ? (byte)0x1e : (byte)0x00,
342                     RemovableMedium = false,
343                     Version = 0x06,
344                     ResponseDataFormat = 0b0010,
345                     AdditionalLength = 31,
346                     CommandQueue = true,
347                     VendorIdentification = Encoding.ASCII.GetBytes(stringDescriptors[IndexManufacturerName]),
348                     ProductIdentification = Encoding.ASCII.GetBytes(stringDescriptors[IndexProductName]),
349                     ProductRevisionLevel = Encoding.ASCII.GetBytes(stringDescriptors[IndexProductRevisionLevel])
350                 };
351 
352                 var inquiryResponseBytes = Packet.Encode(inquiryResponse);
353                 dataInTransfer = inquiryResponseBytes;
354             }
355             else
356             {
357                 this.Log(LogLevel.Warning, "Invalid parameters for SCSI Inquiry command");
358             }
359         }
360 
HandleSCSIRead10(byte lun, byte[] cdb, out byte[] dataInTransfer, out byte[] senseData)361         private void HandleSCSIRead10(byte lun, byte[] cdb, out byte[] dataInTransfer, out byte[] senseData)
362         {
363             // Error handling for SCSI subsystem is omitted, so sense data is not used yet
364             senseData = new byte[0];
365             dataInTransfer = new byte[0];
366             var scsi = Packet.Decode<Read10>(cdb);
367             var bytesCount = scsi.TransferLength * LogicalBlockSize;
368             var readPosition = scsi.LogicalBlockAddress * LogicalBlockSize;
369 
370             if(dataBackends[lun].Length <= (long)readPosition || dataBackends[lun].Length < (long)(readPosition + bytesCount))
371             {
372                 this.Log(LogLevel.Warning, "Trying to read invalid range of the disk.");
373                 return;
374             }
375 
376             dataBackends[lun].Position = (long)readPosition;
377             var dataSegment = dataBackends[lun].ReadBytes((int)bytesCount);
378             this.Log(LogLevel.Debug, "Reading {0} bytes from address 0x{1:x}", bytesCount, readPosition);
379             dataInTransfer = dataSegment;
380         }
381 
HandleSCSIWrite10(byte lun, byte[] cdb, byte[] dataOutTransfer, out byte[] senseData)382         private void HandleSCSIWrite10(byte lun, byte[] cdb, byte[] dataOutTransfer, out byte[] senseData)
383         {
384             // Error handling for SCSI subsystem is omitted, so sense data is not used yet
385             senseData = new byte[0];
386             var scsi = Packet.Decode<Write10>(cdb);
387             var bytesCount = (int)(scsi.TransferLength * LogicalBlockSize);
388             var writePosition = scsi.LogicalBlockAddress * LogicalBlockSize;
389 
390             var dataTransferLength = dataOutTransfer.Length;
391 
392             if(bytesCount != dataTransferLength)
393             {
394                 this.Log(LogLevel.Warning, "SCSI command bytes count {0} is not equal to data transfer length {1}", bytesCount, dataTransferLength);
395             }
396 
397             if(dataBackends[lun].Length <= (long)writePosition || dataBackends[lun].Length < (long)writePosition + dataTransferLength)
398             {
399                 this.Log(LogLevel.Warning, "Writing beyond the disk is unsupported.");
400                 return;
401             }
402 
403             dataBackends[lun].Position = (long)writePosition;
404             dataBackends[lun].Write(dataOutTransfer, 0, dataTransferLength);
405             this.Log(LogLevel.Debug, "Writing {0} bytes to address 0x{1:x}", dataTransferLength, writePosition);
406         }
407 
HandleSCSIReadCapacity16(byte lun, byte[] cdb, out byte[] dataInTransfer, out byte[] senseData)408         private void HandleSCSIReadCapacity16(byte lun, byte[] cdb, out byte[] dataInTransfer, out byte[] senseData)
409         {
410             // Error handling for SCSI subsystem is omitted, so sense data is not used yet
411             senseData = new byte[0];
412             var scsi = Packet.Decode<ReadCapacity16>(cdb);
413 
414             var luDescriptor = unitDescriptors[lun];
415 
416             var readCapacity16Response = new ReadCapacity16ParameterData
417             {
418                 ReturnedLogicalBlockAddress = luDescriptor.LogicalBlockCount - 1,
419                 LogicalBlockLengthInBytes = (uint)(1 << luDescriptor.LogicalBlockSize),
420                 ProtectionEnable = false,
421                 ProtectionType = 0,
422                 LogicalBlocksPerPhysicalBlockExponent = 0,
423                 ThinProvisioningEnable = luDescriptor.ProvisioningType != 0x00,
424             };
425 
426             var readCapacity16ResponseBytes = Packet.Encode(readCapacity16Response);
427             dataInTransfer = readCapacity16ResponseBytes;
428         }
429 
HandleSCSIReportLUNs(byte lun, byte[] cdb, out byte[] dataInTransfer, out byte[] senseData)430         private void HandleSCSIReportLUNs(byte lun, byte[] cdb, out byte[] dataInTransfer, out byte[] senseData)
431         {
432             // Error handling for SCSI subsystem is omitted, so sense data is not used yet
433             senseData = new byte[0];
434             var scsi = Packet.Decode<ReportLUNs>(cdb);
435 
436             var selectReport = (SelectReport)scsi.SelectReport;
437 
438             var lunListLength = 0;
439             switch(selectReport)
440             {
441                 case SelectReport.LogicalUnits:
442                     lunListLength = 8 * LogicalUnits;
443                     break;
444                 case SelectReport.WellKnownLogicalUnits:
445                     lunListLength = 8 * WellKnownLUNsNumber;
446                     break;
447                 case SelectReport.AllLogicalUnits:
448                     lunListLength = 8 * LogicalUnits + 8 * WellKnownLUNsNumber;
449                     break;
450                 default:
451                     this.Log(LogLevel.Warning, "Reserved SELECT REPORT field.");
452                     break;
453             }
454 
455             // The first 4 bytes contain LUN list length itself and the next 4 bytes are reserved fields.
456             // In total, the first 8 bytes are not counted towards LUN list length itself.
457             var reportLUNSParameterData = new byte[lunListLength + 8];
458             var lunListLengthBytes = lunListLength.AsRawBytes().Reverse().ToArray();
459             Array.Copy(lunListLengthBytes, 0, reportLUNSParameterData, 0, lunListLengthBytes.Length);
460 
461             var lunOffset = 8;
462 
463             if(selectReport == SelectReport.LogicalUnits || selectReport == SelectReport.AllLogicalUnits)
464             {
465                 for(int i = 0; i < LogicalUnits; i++)
466                 {
467                     reportLUNSParameterData[lunOffset] = ReportLUNStandardLogicalUnitAddressing;
468                     reportLUNSParameterData[lunOffset + 1] = (byte)i;
469                     lunOffset += 8;
470                 }
471             }
472 
473             if(selectReport == SelectReport.WellKnownLogicalUnits || selectReport == SelectReport.AllLogicalUnits)
474             {
475                 var wellKnownLUs = Enum.GetValues(typeof(WellKnownLUNId)).Cast<byte>().ToArray();
476                 for(int i = 0; i < WellKnownLUNsNumber; i++)
477                 {
478                     reportLUNSParameterData[lunOffset] = ReportLUNWellKnownLogicalUnitAddressing;
479                     reportLUNSParameterData[lunOffset + 1] = wellKnownLUs[i];
480                     lunOffset += 8;
481                 }
482             }
483 
484             reportLUNSParameterData = reportLUNSParameterData.Take((int)scsi.AllocationLength).ToArray();
485             dataInTransfer = reportLUNSParameterData;
486         }
487 
HandleTaskManagementRequest(byte[] requestData)488         private byte[] HandleTaskManagementRequest(byte[] requestData)
489         {
490             var command = Packet.Decode<TaskManagementRequestUPIU>(requestData);
491             var response = HandleTaskManagement(command);
492             var responseData = Packet.Encode<TaskManagementResponseUPIU>(response);
493             return responseData;
494         }
495 
HandleTaskManagement(TaskManagementRequestUPIU request)496         private TaskManagementResponseUPIU HandleTaskManagement(TaskManagementRequestUPIU request)
497         {
498             // UFS Task Management Functions include Abort Task, Abort Task Set, Clear Task Set,
499             // Logical Unit Reset, Query Task, Query Task Set and have meaning if there are
500             // ongoing tasks in the task queue list. UFS is emulated in a synchronous way
501             // and all tasks finish immediately, so task management should never be needed.
502             var response = new TaskManagementResponseUPIU
503             {
504                 TransactionCode = (byte)UPIUTransactionCodeTargetToInitiator.TaskManagementResponse,
505                 HeaderSegmentsCRC = false,
506                 DataSegmentsCRC = false,
507                 InitiatorID = request.InitiatorID,
508                 NexusInitiatorID = request.NexusInitiatorID,
509                 Response = (byte)UTPTaskManagementStatus.Success, // Target Success
510                 TotalEHSLength = 0,
511                 DataSegmentLength = 0,
512                 OutputParameter1 = 0x00 // Task Management Function Complete
513             };
514             return response;
515         }
516 
HandleQueryRequest(byte[] requestData)517         private byte[] HandleQueryRequest(byte[] requestData)
518         {
519             var command = Packet.Decode<QueryRequestUPIU>(requestData);
520             var dataInSegmentOffset = command.HeaderSegmentsCRC ? BasicUPIULength + 4 : BasicUPIULength;
521             var dataSegmentIn = requestData.Skip(dataInSegmentOffset).Take(command.DataSegmentLength).ToArray();
522 
523             var response = HandleQuery(command, dataSegmentIn, out var dataSegmentOut);
524             var responseLength = 32 + (response.HeaderSegmentsCRC ? 4 : 0) + (response.DataSegmentLength > 0 ? dataSegmentOut.Length : 0) + (response.DataSegmentsCRC ? 4 : 0);
525             var responseData = new byte[responseLength];
526 
527             var responseHeader = Packet.Encode<QueryResponseUPIU>(response);
528             Array.Copy(responseHeader, responseData, responseHeader.Length);
529             if(response.HeaderSegmentsCRC)
530             {
531                 var e2eCRCHeader = GetE2ECRC(responseHeader);
532                 var crcBytes = e2eCRCHeader.AsRawBytes();
533                 Array.Copy(crcBytes, 0, responseData, 32, crcBytes.Length);
534             }
535             var dataSegmentOffset = command.HeaderSegmentsCRC ? 36 : 32;
536             if(response.DataSegmentLength > 0 && dataSegmentOut.Length > 0)
537             {
538                 Array.Copy(dataSegmentOut, 0, responseData, dataSegmentOffset, dataSegmentOut.Length);
539                 if(response.DataSegmentsCRC)
540                 {
541                     var e2eCRCData = GetE2ECRC(dataSegmentOut);
542                     var crcBytes = e2eCRCData.AsRawBytes();
543                     Array.Copy(crcBytes, 0, responseData, dataSegmentOffset + response.DataSegmentLength, crcBytes.Length);
544                 }
545             }
546             return responseData;
547         }
548 
HandleQuery(QueryRequestUPIU request, byte[] dataSegmentIn, out byte[] dataSegmentOut)549         private QueryResponseUPIU HandleQuery(QueryRequestUPIU request, byte[] dataSegmentIn, out byte[] dataSegmentOut)
550         {
551             dataSegmentOut = new byte[0];
552             var transactionSpecificFieldsResponse = new byte[16];
553             var transactionSpecificFieldsRequest = request.TransactionSpecificFields;
554             var opcode = (QueryFunctionOpcode)transactionSpecificFieldsRequest[0];
555             switch(opcode)
556             {
557                 case QueryFunctionOpcode.Nop:
558                 {
559                     transactionSpecificFieldsResponse = HandleQueryFunctionNop(transactionSpecificFieldsRequest);
560                     break;
561                 }
562                 case QueryFunctionOpcode.ReadDescriptor:
563                 {
564                     transactionSpecificFieldsResponse = HandleQueryFunctionReadDescriptor(transactionSpecificFieldsRequest, out dataSegmentOut);
565                     break;
566                 }
567                 case QueryFunctionOpcode.WriteDescriptor:
568                 {
569                     transactionSpecificFieldsResponse = HandleQueryFunctionWriteDescriptor(transactionSpecificFieldsRequest, dataSegmentIn);
570                     break;
571                 }
572                 case QueryFunctionOpcode.ReadAttribute:
573                 {
574                     transactionSpecificFieldsResponse = HandleQueryFunctionReadAttribute(transactionSpecificFieldsRequest);
575                     break;
576                 }
577                 case QueryFunctionOpcode.WriteAttribute:
578                 {
579                     transactionSpecificFieldsResponse = HandleQueryFunctionWriteAttribute(transactionSpecificFieldsRequest);
580                     break;
581                 }
582                 case QueryFunctionOpcode.ReadFlag:
583                 case QueryFunctionOpcode.SetFlag:
584                 case QueryFunctionOpcode.ClearFlag:
585                 case QueryFunctionOpcode.ToggleFlag:
586                 {
587                     transactionSpecificFieldsResponse = HandleQueryFunctionFlagOperation(transactionSpecificFieldsRequest, opcode);
588                     break;
589                 }
590                 default:
591                 {
592                     this.Log(LogLevel.Warning, "Invalid query function opcode");
593                     break;
594                 }
595             }
596 
597             // first four byte fields match the request
598             transactionSpecificFieldsResponse[0] = transactionSpecificFieldsRequest[0];
599             transactionSpecificFieldsResponse[1] = transactionSpecificFieldsRequest[1];
600             transactionSpecificFieldsResponse[2] = transactionSpecificFieldsRequest[2];
601             transactionSpecificFieldsResponse[3] = transactionSpecificFieldsRequest[3];
602 
603             var response = new QueryResponseUPIU
604             {
605                 TransactionCode = (byte)UPIUTransactionCodeTargetToInitiator.QueryResponse,
606                 Flags = request.Flags,
607                 TaskTag = request.TaskTag,
608                 QueryFunction = request.QueryFunction,
609                 QueryResponse = IsValidQueryOpcode(request.QueryFunction, opcode) ? QueryResponseCode.Success : QueryResponseCode.InvalidOpcode,
610                 TotalEHSLength = 0x00,
611                 DeviceInformation = 0,
612                 DataSegmentLength = (ushort)dataSegmentOut.Length,
613                 TransactionSpecficFields = transactionSpecificFieldsResponse
614             };
615             return response;
616         }
617 
HandleQueryFunctionNop(byte[] request)618         private byte[] HandleQueryFunctionNop(byte[] request)
619         {
620             var response = new byte[16];
621             return response;
622         }
623 
HandleQueryFunctionReadDescriptor(byte[] request, out byte[] dataSegmentOut)624         private byte[] HandleQueryFunctionReadDescriptor(byte[] request, out byte[] dataSegmentOut)
625         {
626             var descrIdn = (DescriptorTypeIdentification)request[1];
627             var index = request[2];
628             var length = BitHelper.ToUInt16(request, 6, reverse: false);
629 
630             var data = new byte[0];
631             switch(descrIdn)
632             {
633                 case DescriptorTypeIdentification.Device:
634                 {
635                     data = Packet.Encode(deviceDescriptor);
636                     break;
637                 }
638                 case DescriptorTypeIdentification.Configuration:
639                 {
640                     if(index < configurationDescriptors.Length)
641                     {
642                         data = Packet.Encode(configurationDescriptors[index]);
643                     }
644                     else
645                     {
646                         this.Log(LogLevel.Warning, "Requested configuration descriptor for invalid index.");
647                     }
648                     break;
649                 }
650                 case DescriptorTypeIdentification.Unit:
651                 {
652                     if(unitDescriptors.Keys.Contains(index))
653                     {
654                         data = Packet.Encode(unitDescriptors[index]);
655                     }
656                     else
657                     {
658                         this.Log(LogLevel.Warning, "Requested unit descriptor for invalid index.");
659                     }
660                     break;
661                 }
662                 case DescriptorTypeIdentification.Interconnect:
663                 {
664                     data = Packet.Encode(interconnectDescriptor);
665                     break;
666                 }
667                 case DescriptorTypeIdentification.String:
668                 {
669                     if(index <= MaxNumberOfStringDescriptors-1)
670                     {
671                         data = GetStringDescriptor(index);
672                     }
673                     else
674                     {
675                         this.Log(LogLevel.Warning, "Requested string descriptor for invalid index.");
676                     }
677                     break;
678                 }
679                 case DescriptorTypeIdentification.Geometry:
680                 {
681                     data = Packet.Encode(geometryDescriptor);
682                     break;
683                 }
684                 case DescriptorTypeIdentification.Power:
685                 {
686                     data = Packet.Encode(powerParametersDescriptor);
687                     break;
688                 }
689                 case DescriptorTypeIdentification.DeviceHealth:
690                 {
691                     data = Packet.Encode(deviceHealthDescriptor);
692                     break;
693                 }
694                 default:
695                     break;
696             }
697 
698             dataSegmentOut = data.Take(Math.Min((ushort)data.Length, length)).ToArray();
699             var actualLength = (ushort)dataSegmentOut.Length;
700             var actualLengthBytes = actualLength.AsRawBytes().Reverse().ToArray();
701 
702             var response = new byte[16];
703             response[6] = actualLengthBytes[0]; // LENGTH MSB
704             response[7] = actualLengthBytes[1]; // LENGTH LSB
705             return response;
706         }
707 
HandleQueryFunctionWriteDescriptor(byte[] request, byte[] dataSegmentIn)708         private byte[] HandleQueryFunctionWriteDescriptor(byte[] request, byte[] dataSegmentIn)
709         {
710             var response = new byte[16];
711             // Current implementation doesn't allow to dynamically modify descriptors.
712             // There is no partial writes so it's either 0 or entire descriptor.
713             response[6] = 0; // LENGTH MSB
714             response[7] = 0; // LENGTH LSB
715 
716             this.Log(LogLevel.Warning, "Write Descriptor function not implemented yet");
717             return response;
718         }
719 
HandleQueryFunctionReadAttribute(byte[] request)720         private byte[] HandleQueryFunctionReadAttribute(byte[] request)
721         {
722             var attrIdn = (UFSDeviceAttribute)request[1];
723             var index = request[2];
724             var selector = request[3];
725 
726             if (attrIdn == UFSDeviceAttribute.DynCapNeeded || attrIdn == UFSDeviceAttribute.ContextConf)
727             {
728                 this.Log(LogLevel.Warning, "Trying to read an array attribute {0} for logical unit {1} and selector {2}. Assuming the same value for all logical units.", Enum.GetName(typeof(UFSDeviceAttribute), attrIdn), index, selector);
729             }
730 
731             ulong attrValue;
732             if (!attributes.TryGetValue(attrIdn, out attrValue))
733             {
734                 this.Log(LogLevel.Warning, "Cannot read attribute: {0}", attrIdn);
735             }
736 
737             var valueBytes = attrValue.AsRawBytes().Reverse().ToArray();
738             var response = new byte[16];
739             Array.Copy(valueBytes, 0, response, 4, valueBytes.Length);
740             return response;
741         }
742 
HandleQueryFunctionWriteAttribute(byte[] request)743         private byte[] HandleQueryFunctionWriteAttribute(byte[] request)
744         {
745             var attrIdn = (UFSDeviceAttribute)request[1];
746             var index = request[2];
747             var selector = request[3];
748 
749             var attrValue = BitConverter.ToUInt64(request, 4);
750 
751             if(attrIdn == UFSDeviceAttribute.DynCapNeeded || attrIdn == UFSDeviceAttribute.ContextConf)
752             {
753                 this.Log(LogLevel.Warning, "Trying to write to an array attribute {0} for logical unit {1} and selector {2}. Assuming the same value for all logical units.", Enum.GetName(typeof(UFSDeviceAttribute), attrIdn), index, selector);
754             }
755 
756             if(attributes.ContainsKey(attrIdn))
757             {
758                 attributes[attrIdn] = attrValue;
759             }
760             else
761             {
762                 this.Log(LogLevel.Warning, "Cannot write attribute: {0}", attrIdn);
763             }
764             var response = request;
765             return response;
766         }
767 
HandleQueryFunctionFlagOperation(byte[] request, QueryFunctionOpcode op)768         private byte[] HandleQueryFunctionFlagOperation(byte[] request, QueryFunctionOpcode op)
769         {
770             var flagIdn = (UFSDeviceFlag)request[1];
771             var flagValue = false;
772 
773             if(flags.ContainsKey(flagIdn))
774             {
775                 switch(op)
776                 {
777                     case QueryFunctionOpcode.ReadFlag:
778                     {
779                         flagValue = flags[flagIdn];
780                         break;
781                     }
782                     case QueryFunctionOpcode.SetFlag:
783                     {
784                         flagValue = true;
785                         flags[flagIdn] = flagValue;
786                         if(flagIdn == UFSDeviceFlag.DeviceInit)
787                         {
788                             flags[flagIdn] = false;
789                             this.Log(LogLevel.Debug, "Initialization complete. Setting fDeviceInit flag to false.");
790                         }
791                         break;
792                     }
793                     case QueryFunctionOpcode.ClearFlag:
794                     {
795                         flagValue = false;
796                         flags[flagIdn] = flagValue;
797                         break;
798                     }
799                     case QueryFunctionOpcode.ToggleFlag:
800                     {
801                         flags[flagIdn] = !flags[flagIdn];
802                         flagValue = flags[flagIdn];
803                         break;
804                     }
805                 }
806             }
807             else
808             {
809                 this.Log(LogLevel.Warning, "Flag is not available");
810             }
811 
812             var response = new byte[16];
813             response[11] = flagValue ? (byte)1 : (byte)0; // FLAG VALUE
814             return response;
815         }
816 
IsValidQueryOpcode(QueryFunction function, QueryFunctionOpcode opcode)817         private bool IsValidQueryOpcode(QueryFunction function, QueryFunctionOpcode opcode)
818         {
819             if(function == QueryFunction.StandardReadRequest
820                 && (opcode == QueryFunctionOpcode.WriteDescriptor
821                     || opcode == QueryFunctionOpcode.WriteAttribute
822                     || opcode == QueryFunctionOpcode.SetFlag
823                     || opcode == QueryFunctionOpcode.ClearFlag
824                     || opcode == QueryFunctionOpcode.ToggleFlag))
825             {
826                 return false;
827             }
828             if(function == QueryFunction.StandardWriteRequest
829                 && (opcode == QueryFunctionOpcode.ReadDescriptor
830                     || opcode == QueryFunctionOpcode.ReadAttribute
831                     || opcode == QueryFunctionOpcode.ReadFlag))
832             {
833                 return false;
834             }
835             return true;
836         }
837 
GetE2ECRC(byte[] data)838         private uint GetE2ECRC(byte[] data)
839         {
840             this.Log(LogLevel.Warning, "End-to-end CRC is not supported in this version of the standard");
841             return 0;
842         }
843 
InitConfiguration()844         private void InitConfiguration()
845         {
846             InitDeviceDescriptor(
847                 iManufacturerName: IndexManufacturerName,
848                 iProductName: IndexProductName,
849                 iSerialNumber: IndexSerialNumber,
850                 iOemID: IndexOemID,
851                 iProductRevisionLevel: IndexProductRevisionLevel
852             );
853             InitConfigurationDescriptors();
854             InitGeometryDescriptor(
855                 bMaxInBufferSize: MaxInBufferSize,
856                 bMaxOutBufferSize: MaxOutBufferSize,
857                 bMaxNumberLU: LogicalUnits <= 8 ? (byte)0x0 : (byte)0x1
858             );
859             InitUnitDescriptors(
860                 qPhyMemResourceCount: LogicalBlockCount,
861                 bLogicalBlockSize: LogicalBlockSizeExponentBase2
862             );
863             InitRPMBUnitDescriptor();
864             InitPowerParametersDescriptor();
865             InitInterconnectDescriptor();
866             InitDeviceHealthDescriptor();
867             InitStringDescriptors();
868             InitFlags();
869             InitAttributes(
870                 bMaxDataInSize: MaxInBufferSize,
871                 bMaxDataOutSize: MaxOutBufferSize
872             );
873         }
874 
InitDeviceDescriptor( byte bDeviceSubClass = 0x01, byte bBackgroundOpsTermLat = 0x00, ushort wManufactureDate = 0x0810, byte iManufacturerName = 0, byte iProductName = 1, byte iSerialNumber = 2, byte iOemID = 3, ushort wManufacturerID = 0x0000, byte bDeviceRTTCap = 8, byte bUFSFeaturesSupport = 0b00000001, byte bFFUTimeout = 0, byte bQueueDepth = 0, ushort wDeviceVersion = 0x0000, byte bNumSecureWPArea = 32, uint dPSAMaxDataSize = 0x01, byte bPSAStateTimeout = 0x00, byte iProductRevisionLevel = 4, uint dExtendedUFSFeaturesSupport = 0b00000001)875         private void InitDeviceDescriptor(
876             byte bDeviceSubClass = 0x01, // Embedded Non-Bootable
877             byte bBackgroundOpsTermLat = 0x00, // Latency undefined
878             ushort wManufactureDate = 0x0810, // August 2010
879             byte iManufacturerName = 0, // Index to the string which contains the Manufacturer Name
880             byte iProductName = 1, // Index to the string which contains the Product Name
881             byte iSerialNumber = 2, // Index to the string which contains the Serial Number
882             byte iOemID = 3, // Index to the string which contains the OEM ID
883             ushort wManufacturerID = 0x0000, // Manufacturer ID as defined in JEDEC JEP106, Standard Manufacturer’s Identification Code.
884             byte bDeviceRTTCap = 8, // Maximum number of outstanding RTTs supported by device
885             byte bUFSFeaturesSupport = 0b00000001, // UFS Features Support, Bit 0 shall be set to one
886             byte bFFUTimeout = 0, // No Timeout
887             byte bQueueDepth = 0, // The device implements the per-LU queueing architecture
888             ushort wDeviceVersion = 0x0000, // Device Version
889             byte bNumSecureWPArea = 32, // Number of Secure Write Protect Areas
890             uint dPSAMaxDataSize = 0x01, // PSA Maximum Data Size, 4Kbyte
891             byte bPSAStateTimeout = 0x00, // PSA State Timeout, undefined
892             byte iProductRevisionLevel = 4, // Index to the string which contains the Product Revision Level
893             uint dExtendedUFSFeaturesSupport = 0b00000001) // shall be the same as bUFSFeaturesSupport
894         {
895             deviceDescriptor = new DeviceDescriptor
896             {
897                 Length = 0x59,
898                 DescriptorIDN = 0x00,
899                 Device = 0x00, // Device
900                 DeviceClass = 0x00, // Mass Storage
901                 DeviceSubClass = bDeviceSubClass,
902                 Protocol = 0x00, // SCSI
903                 NumberLU = 0x00, // NumberLU field value is calculated by the device based on bLUEnable field value in the Unit Descriptors. NumberLU does not include well known logical units
904                 NumberWLU = 0x04,
905                 BootEnable = 0x00,
906                 DescrAccessEn = 0x00,
907                 InitPowerMode = 0x01,
908                 HighPriorityLUN = 0x7f,
909                 SecureRemovalType = 0x00,
910                 SecurityLU = 0x01,
911                 BackgroundOpsTermLat = bBackgroundOpsTermLat,
912                 InitActiveICCLevel = 0x00,
913                 SpecVersion = 0x0400,
914                 ManufactureDate = wManufactureDate,
915                 ManufacturerName = iManufacturerName,
916                 ProductName = iProductName,
917                 SerialNumber = iSerialNumber,
918                 OemID = iOemID,
919                 ManufacturerID = wManufacturerID,
920                 UD0BaseOffset = 0x16,
921                 UDConfigPLength = 0x1a,
922                 DeviceRTTCap = bDeviceRTTCap,
923                 PeriodicRTCUpdate = 0x0000,
924                 UFSFeaturesSupport = bUFSFeaturesSupport,
925                 FFUTimeout = bFFUTimeout,
926                 QueueDepth = bQueueDepth,
927                 DeviceVersion = wDeviceVersion,
928                 NumSecureWPArea = bNumSecureWPArea,
929                 PSAMaxDataSize = dPSAMaxDataSize,
930                 PSAStateTimeout = bPSAStateTimeout,
931                 ProductRevisionLevel = iProductRevisionLevel,
932                 ExtendedUFSFeaturesSupport = dExtendedUFSFeaturesSupport,
933                 WriteBoosterBufferPreserveUserSpaceEn = 0x00,
934                 WriteBoosterBufferType = 0x00,
935                 NumSharedWriteBoosterBufferAllocUnits = 0x00
936             };
937         }
938 
InitConfigurationDescriptors()939         private void InitConfigurationDescriptors()
940         {
941             // Configuration Descriptors are only partially supported, because model doesn't support dynamic modification of device configuration.
942             // It should be extended together with support for Write Descriptor function.
943             var numberOfConfDescr = 1 + (LogicalUnits - 1) / 8;
944             configurationDescriptors = new ConfigurationDescriptorHeader[numberOfConfDescr];
945 
946             for(int i = 0; i < numberOfConfDescr; i++)
947             {
948                 var confDescrHeader = new ConfigurationDescriptorHeader
949                 {
950                     Length = 0xe6, // size of this descriptor
951                     DescriptorIDN = 0x01,
952                     ConfDescContinue = 0x00,
953                     WriteBoosterBufferPreserveUserSpaceEn = 0x00,
954                     WriteBoosterBufferType = 0x00,
955                     NumSharedWriteBoosterBufferAllocUnits = 0x00,
956                 };
957                 configurationDescriptors[i] = confDescrHeader;
958             }
959         }
960 
InitRPMBUnitDescriptor( byte bLUQueueDepth = 0x00, byte bPSASensitive = 0x00, ulong qLogicalBlockCount = 512, byte bRPMBRegion0Size = 0x00)961         private void InitRPMBUnitDescriptor(
962             byte bLUQueueDepth = 0x00,
963             byte bPSASensitive = 0x00,
964             ulong qLogicalBlockCount = 512,
965             byte bRPMBRegion0Size = 0x00)
966         {
967             rpmbUnitDescriptor = new RPMBUnitDescriptor
968             {
969                 Length = 0x23,
970                 DescriptorIDN = 0x2,
971                 UnitIndex = 0xc4,
972                 LUEnable = 0x01,
973                 BootLunID = 0x00,
974                 LUWriteProtect = 0x00,
975                 LUQueueDepth = bLUQueueDepth,
976                 PSASensitive = bPSASensitive,
977                 MemoryType = 0x0f,
978                 RPMBRegionEnable = 0x00,
979                 LogicalBlockSize = 0x08,
980                 LogicalBlockCount = qLogicalBlockCount,
981                 RPMBRegion0Size = bRPMBRegion0Size,
982                 RPMBRegion1Size = 0x00,
983                 RPMBRegion2Size = 0x00,
984                 RPMBRegion3Size = 0x00,
985                 ProvisioningType = 0x00,
986                 PhyMemResourceCount = qLogicalBlockCount,
987             };
988         }
989 
InitUnitDescriptors( byte bLUQueueDepth = 0x00, byte bPSASensitive = 0x00, ulong qPhyMemResourceCount = 8, byte bLargeUnitGranularity_M1 = 0, byte bLogicalBlockSize = 0x0c )990         private void InitUnitDescriptors(
991             byte bLUQueueDepth = 0x00, // LU queue not available (shared queuing is used)
992             byte bPSASensitive = 0x00, // LU is not sensitive to soldering
993             ulong qPhyMemResourceCount = 8, // Physical Memory Resource Count in units of Logical Block Size
994             byte bLargeUnitGranularity_M1 = 0,
995             byte bLogicalBlockSize = 0x0c
996         )
997         {
998             unitDescriptors = new Dictionary<byte, UnitDescriptor>(capacity: LogicalUnits + WellKnownLUNsNumber);
999             for(int i = 0; i < LogicalUnits; i++)
1000             {
1001                 var unitDescr = new UnitDescriptor
1002                 {
1003                     Length = 0x2d,
1004                     DescriptorIDN = 0x02,
1005                     UnitIndex = (byte)i,
1006                     LUEnable = 0x00,
1007                     BootLunID = 0x00,
1008                     LUWriteProtect = 0x00,
1009                     LUQueueDepth = bLUQueueDepth,
1010                     PSASensitive = bPSASensitive,
1011                     MemoryType = 0x00,
1012                     DataReliability = 0x00,
1013                     LogicalBlockSize = bLogicalBlockSize,
1014                     LogicalBlockCount = qPhyMemResourceCount,
1015                     EraseBlockSize = 0x00,
1016                     ProvisioningType = 0x00,
1017                     PhyMemResourceCount = qPhyMemResourceCount,
1018                     ContextCapabilities = 0x00,
1019                     LargeUnitGranularity_M1 = bLargeUnitGranularity_M1,
1020                     LUNumWriteBoosterBufferAllocUnits = 0x00
1021                 };
1022                 unitDescriptors[(byte)i] = unitDescr;
1023             }
1024 
1025             // Add well known units description
1026             foreach(byte wlun in Enum.GetValues(typeof(WellKnownLUNId)))
1027             {
1028                 var wellKnownUnitDescr = new UnitDescriptor
1029                 {
1030                     Length = 0x2d,
1031                     DescriptorIDN = 0x02,
1032                     UnitIndex = wlun,
1033                     LUEnable = 0x00,
1034                     BootLunID = 0x00,
1035                     LUWriteProtect = 0x00,
1036                     LUQueueDepth = bLUQueueDepth,
1037                     PSASensitive = bPSASensitive,
1038                     MemoryType = 0x00,
1039                     DataReliability = 0x00,
1040                     LogicalBlockSize = bLogicalBlockSize,
1041                     LogicalBlockCount = qPhyMemResourceCount,
1042                     EraseBlockSize = 0x00,
1043                     ProvisioningType = 0x00,
1044                     PhyMemResourceCount = qPhyMemResourceCount,
1045                     ContextCapabilities = 0x00,
1046                     LargeUnitGranularity_M1 = bLargeUnitGranularity_M1,
1047                     LUNumWriteBoosterBufferAllocUnits = 0x00
1048                 };
1049 
1050                 // Set the highest bit to obtain well known logical unit identifier
1051                 var lun = (byte)(0x80 | wlun);
1052                 unitDescriptors[lun] = wellKnownUnitDescr;
1053             }
1054         }
1055 
InitPowerParametersDescriptor()1056         private void InitPowerParametersDescriptor()
1057         {
1058             powerParametersDescriptor = new PowerParametersDescriptor
1059             {
1060                 Length = 0x62,
1061                 DescriptorIDN = 0x08,
1062                 ActiveICCLevelsVCC = new byte[32],
1063                 ActiveICCLevelsVCCQ = new byte[32],
1064                 ActiveICCLevelsVCCQ2 = new byte[32]
1065             };
1066         }
1067 
InitInterconnectDescriptor()1068         private void InitInterconnectDescriptor()
1069         {
1070             interconnectDescriptor = new InterconnectDescriptor
1071             {
1072                 Length = 0x06,
1073                 DescriptorIDN = 0x04,
1074                 BCDUniproVersion = 0x0200,
1075                 BCDMphyVersion = 0x0500
1076             };
1077         }
1078 
InitGeometryDescriptor( ulong qTotalRawDeviceCapacity = 1024, byte bMaxNumberLU = 0x00, uint dSegmentSize = 16, byte bAllocationUnitSize = 0, byte bMinAddrBlockSize = 0x08, byte bOptimalReadBlockSize = 0, byte bOptimalWriteBlockSize = 0x08, byte bMaxInBufferSize = 0x08, byte bMaxOutBufferSize = 0x08, byte bRPMB_ReadWriteSize = 0, byte bDynamicCapacityResourcePolicy = 0x00, byte bDataOrdering = 0x00, byte bMaxContexIDNumber = 5, byte bSysDataTagUnitSize = 0x00, byte bSysDataTagResSize = 0, byte bSupportedSecRTypes = 0b00000011, ushort wSupportedMemoryTypes = 0xff, uint dSystemCodeMaxNAllocU = 0x08, ushort wSystemCodeCapAdjFac = 256*8, uint dNonPersistMaxNAllocU = 0x08, ushort wNonPersistCapAdjFac = 256*8, uint dEnhanced1MaxNAllocU = 0x08, ushort wEnhanced1CapAdjFac = 256*8, uint dEnhanced2MaxNAllocU = 0x08, ushort wEnhanced2CapAdjFac = 256*8, uint dEnhanced3MaxNAllocU = 0x08, ushort wEnhanced3CapAdjFac = 256*8, uint dEnhanced4MaxNAllocU = 0x08, ushort wEnhanced4CapAdjFac = 256*8, uint dOptimalLogicalBlockSize = 0x00, uint dWriteBoosterBufferMaxNAllocUnits = 0, byte bDeviceMaxWriteBoosterLUs = 1, byte bWriteBoosterBufferCapAdjFac = 2, byte bSupportedWriteBoosterBufferUserSpaceReductionTypes = 0x00, byte bSupportedWriteBoosterBufferTypes = 0 )1079         private void InitGeometryDescriptor(
1080             ulong qTotalRawDeviceCapacity = 1024, // Total Raw Device Capacity in unit of 512 bytes
1081             byte bMaxNumberLU = 0x00, // 0x00 = 8 logical units, 0x01 == 32 logical units
1082             uint dSegmentSize = 16, // Segment Size in unit of 512 bytes
1083             byte bAllocationUnitSize = 0, // Multiple of Allocation Units.
1084             byte bMinAddrBlockSize = 0x08, // Minimum allowed value -> 4 Kbyte, in unit of 512 bytes
1085             byte bOptimalReadBlockSize = 0, // Information Not Available
1086             byte bOptimalWriteBlockSize = 0x08, // Shall be equal to or greater than bMinAddrBlockSize
1087             byte bMaxInBufferSize = 0x08, // Minimum allowed value, in unit of 512 bytes
1088             byte bMaxOutBufferSize = 0x08, // Minimum allowed value, in unit of 512 bytes
1089             byte bRPMB_ReadWriteSize = 0, // Max RPMB frames in Security Protocol
1090             byte bDynamicCapacityResourcePolicy = 0x00, // Spare blocks resource management policy is per logical unit
1091             byte bDataOrdering = 0x00, // Out-of-order data transfer is not supported by the device
1092             byte bMaxContexIDNumber = 5, // Minimum number of supported contexts
1093             byte bSysDataTagUnitSize = 0x00, // Minimum value
1094             byte bSysDataTagResSize = 0, // Valid values are from 0 to 6
1095             byte bSupportedSecRTypes = 0b00000011, // Supported Secure Removal Types
1096             ushort wSupportedMemoryTypes = 0xff, // Supported Memory Types - all
1097             uint dSystemCodeMaxNAllocU = 0x08, // Max Number of Allocation Units for the System Code memory type
1098             ushort wSystemCodeCapAdjFac = 256*8,
1099             uint dNonPersistMaxNAllocU = 0x08,
1100             ushort wNonPersistCapAdjFac = 256*8,
1101             uint dEnhanced1MaxNAllocU = 0x08,
1102             ushort wEnhanced1CapAdjFac = 256*8,
1103             uint dEnhanced2MaxNAllocU = 0x08,
1104             ushort wEnhanced2CapAdjFac = 256*8,
1105             uint dEnhanced3MaxNAllocU = 0x08,
1106             ushort wEnhanced3CapAdjFac = 256*8,
1107             uint dEnhanced4MaxNAllocU = 0x08,
1108             ushort wEnhanced4CapAdjFac = 256*8,
1109             uint dOptimalLogicalBlockSize = 0x00,
1110             uint dWriteBoosterBufferMaxNAllocUnits = 0,
1111             byte bDeviceMaxWriteBoosterLUs = 1, // In JESD220F, the valid value of this field is 1.
1112             byte bWriteBoosterBufferCapAdjFac = 2, // MLC NAND
1113             byte bSupportedWriteBoosterBufferUserSpaceReductionTypes = 0x00, // WriteBooster Buffer can be configured only in user space reduction type
1114             byte bSupportedWriteBoosterBufferTypes = 0 //  LU based WriteBooster Buffer configuration
1115         )
1116         {
1117             geometryDescriptor = new GeometryDescriptor
1118             {
1119                 Length = 0x57,
1120                 DescriptorIDN = 0x07,
1121                 MediaTechnology = 0x00,
1122                 TotalRawDeviceCapacity = qTotalRawDeviceCapacity,
1123                 MaxNumberLU = bMaxNumberLU,
1124                 SegmentSize = dSegmentSize,
1125                 AllocationUnitSize = bAllocationUnitSize,
1126                 MinAddrBlockSize = bMinAddrBlockSize,
1127                 OptimalReadBlockSize = bOptimalReadBlockSize,
1128                 OptimalWriteBlockSize = bOptimalWriteBlockSize,
1129                 MaxInBufferSize = bMaxInBufferSize,
1130                 MaxOutBufferSize = bMaxOutBufferSize,
1131                 RPMBReadWriteSize = bRPMB_ReadWriteSize,
1132                 DynamicCapacityResourcePolicy = bDynamicCapacityResourcePolicy,
1133                 DataOrdering = bDataOrdering,
1134                 MaxContexIDNumber = bMaxContexIDNumber,
1135                 SysDataTagUnitSize = bSysDataTagUnitSize,
1136                 SysDataTagResSize = bSysDataTagResSize,
1137                 SupportedSecRTypes = bSupportedSecRTypes,
1138                 SupportedMemoryTypes = wSupportedMemoryTypes,
1139                 SystemCodeMaxNAllocU = dSystemCodeMaxNAllocU,
1140                 SystemCodeCapAdjFac = wSystemCodeCapAdjFac,
1141                 NonPersistMaxNAllocU = dNonPersistMaxNAllocU,
1142                 NonPersistCapAdjFac = wNonPersistCapAdjFac,
1143                 Enhanced1MaxNAllocU = dEnhanced1MaxNAllocU,
1144                 Enhanced1CapAdjFac = wEnhanced1CapAdjFac,
1145                 Enhanced2MaxNAllocU = dEnhanced2MaxNAllocU,
1146                 Enhanced2CapAdjFac = wEnhanced2CapAdjFac,
1147                 Enhanced3MaxNAllocU = dEnhanced3MaxNAllocU,
1148                 Enhanced3CapAdjFac = wEnhanced3CapAdjFac,
1149                 Enhanced4MaxNAllocU = dEnhanced4MaxNAllocU,
1150                 Enhanced4CapAdjFac = wEnhanced4CapAdjFac,
1151                 OptimalLogicalBlockSize = dOptimalLogicalBlockSize,
1152                 WriteBoosterBufferMaxNAllocUnits = dWriteBoosterBufferMaxNAllocUnits,
1153                 DeviceMaxWriteBoosterLUs = bDeviceMaxWriteBoosterLUs,
1154                 WriteBoosterBufferCapAdjFac = bWriteBoosterBufferCapAdjFac,
1155                 SupportedWriteBoosterBufferUserSpaceReductionTypes = bSupportedWriteBoosterBufferUserSpaceReductionTypes,
1156                 SupportedWriteBoosterBufferTypes = bSupportedWriteBoosterBufferTypes,
1157             };
1158         }
1159 
InitDeviceHealthDescriptor( byte bPreEOLInfo = 0x00, byte bDeviceLifeTimeEstA = 0x00, byte bDeviceLifeTimeEstB = 0x00, byte [] vendorPropInfo = null, uint dRefreshTotalCount = 1, uint dRefreshProgress = 100000 )1160         private void InitDeviceHealthDescriptor(
1161             byte bPreEOLInfo = 0x00, // Not defined
1162             byte bDeviceLifeTimeEstA = 0x00, // Information not available
1163             byte bDeviceLifeTimeEstB = 0x00, // Information not available
1164             byte [] vendorPropInfo = null, // Reserved for Vendor Proprietary Health Report
1165             uint dRefreshTotalCount = 1, // Total Refresh Count
1166             uint dRefreshProgress = 100000 // Refresh Progress (100%)
1167         )
1168         {
1169             deviceHealthDescriptor = new DeviceHealthDescriptor
1170             {
1171                 Length = 0x2d,
1172                 DescriptorIDN = 0x09,
1173                 PreEOLInfo = bPreEOLInfo,
1174                 DeviceLifeTimeEstA = bDeviceLifeTimeEstA,
1175                 DeviceLifeTimeEstB = bDeviceLifeTimeEstB,
1176                 VendorPropInfo = vendorPropInfo ?? (new byte[32]),
1177                 RefreshTotalCount = dRefreshTotalCount,
1178                 RefreshProgress = dRefreshProgress
1179             };
1180         }
1181 
InitStringDescriptors()1182         private void InitStringDescriptors()
1183         {
1184             stringDescriptors = new string[MaxNumberOfStringDescriptors];
1185             stringDescriptors[IndexManufacturerName] = ManufacturerName; // 8 UNICODE characters
1186             stringDescriptors[IndexProductName] = ProductName; // 16 UNICODE characters
1187             stringDescriptors[IndexOemID] = OemID; // up to 126 UNICODE characters
1188             stringDescriptors[IndexSerialNumber] = SerialNumber; // up to 126 UNICODE characters
1189             stringDescriptors[IndexProductRevisionLevel] = ProductRevisionLevel; // 4 UNICODE characters
1190         }
1191 
InitFlags()1192         private void InitFlags()
1193         {
1194             flags = new Dictionary<UFSDeviceFlag, bool>()
1195             {
1196                 { UFSDeviceFlag.DeviceInit, false },
1197                 { UFSDeviceFlag.PermanentWPEn, false },
1198                 { UFSDeviceFlag.PowerOnWPEn, false },
1199                 { UFSDeviceFlag.BackgroundOpsEn, true },
1200                 { UFSDeviceFlag.DeviceLifeSpanModeEn, false },
1201                 { UFSDeviceFlag.PurgeEnable, false },
1202                 { UFSDeviceFlag.RefreshEnable, false },
1203                 { UFSDeviceFlag.PhyResourceRemoval, false },
1204                 { UFSDeviceFlag.BusyRTC, false },
1205                 { UFSDeviceFlag.PermanentlyDisableFwUpdate, false },
1206                 { UFSDeviceFlag.WriteBoosterEn, false },
1207                 { UFSDeviceFlag.WriteBoosterBufferFlushEn, false },
1208                 { UFSDeviceFlag.WriteBoosterBufferFlushDuringHibernate, false }
1209             };
1210         }
1211 
InitAttributes( byte bMaxDataInSize = 0x08, byte bMaxDataOutSize = 0x08 )1212         private void InitAttributes(
1213             byte bMaxDataInSize = 0x08,
1214             byte bMaxDataOutSize = 0x08
1215         )
1216         {
1217             attributes = new Dictionary<UFSDeviceAttribute, ulong>()
1218             {
1219                 { UFSDeviceAttribute.BootLunEn, 0x00 },
1220                 { UFSDeviceAttribute.CurrentPowerMode, 0x11 }, // Active mode
1221                 { UFSDeviceAttribute.ActiveICCLevel, 0x00 },
1222                 { UFSDeviceAttribute.OutOfOrderDataEn, 0x00 },
1223                 { UFSDeviceAttribute.BackgroundOpStatus, 0x00 },
1224                 { UFSDeviceAttribute.PurgeStatus, 0x00 },
1225                 { UFSDeviceAttribute.MaxDataInSize, bMaxDataInSize }, // =bMaxInBufferSize
1226                 { UFSDeviceAttribute.MaxDataOutSize, bMaxDataOutSize }, // =bMaxOutBufferSize
1227                 { UFSDeviceAttribute.DynCapNeeded, 0x00000000 },
1228                 { UFSDeviceAttribute.RefClkFreq, 0x03 }, // 52 MHz default
1229                 { UFSDeviceAttribute.ConfigDescrLock, 0x00 },
1230                 { UFSDeviceAttribute.MaxNumOfRTT, 0x02 },
1231                 { UFSDeviceAttribute.ExceptionEventControl, 0x0000 },
1232                 { UFSDeviceAttribute.ExceptionEventStatus, 0x0000 },
1233                 { UFSDeviceAttribute.SecondsPassed, 0x00000000 },
1234                 { UFSDeviceAttribute.ContextConf, 0x0000 },
1235                 { UFSDeviceAttribute.DeviceFFUStatus, 0x00 },
1236                 { UFSDeviceAttribute.PSAState, 0x00 },
1237                 { UFSDeviceAttribute.PSADataSize, 0x00000000 },
1238                 { UFSDeviceAttribute.RefClkGatingWaitTime, 0x00 },
1239                 { UFSDeviceAttribute.DeviceCaseRoughTemperaure, 0x00 },
1240                 { UFSDeviceAttribute.DeviceTooHighTempBoundary, 0x00 },
1241                 { UFSDeviceAttribute.DeviceTooLowTempBoundary, 0x00 },
1242                 { UFSDeviceAttribute.ThrottlingStatus, 0x00 },
1243                 { UFSDeviceAttribute.WriteBoosterBufferFlushStatus, 0 },
1244                 { UFSDeviceAttribute.AvailableWriteBoosterBufferSize, 0 },
1245                 { UFSDeviceAttribute.WriteBoosterBufferLifeTimeEst, 0x00 },
1246                 { UFSDeviceAttribute.CurrentWriteBoosterBufferSize, 0 },
1247                 { UFSDeviceAttribute.EXTIIDEn, 0x00 },
1248                 { UFSDeviceAttribute.HostHintCacheSize, 0x0000 },
1249                 { UFSDeviceAttribute.RefreshStatus, 0x00 },
1250                 { UFSDeviceAttribute.RefreshFreq, 0x00 },
1251                 { UFSDeviceAttribute.RefreshUnit, 0x00 },
1252                 { UFSDeviceAttribute.RefreshMethod, 0x00 },
1253                 { UFSDeviceAttribute.Timestamp, 0x00 }
1254             };
1255         }
1256 
GetStringDescriptor(byte index)1257         private byte[] GetStringDescriptor(byte index)
1258         {
1259             UnicodeEncoding unicode = new UnicodeEncoding();
1260             var unicodeString = stringDescriptors[index];
1261 
1262             if(unicodeString == null)
1263             {
1264                 var emptyStringDescriptor = new byte[2];
1265                 emptyStringDescriptor[0] = 0;
1266                 emptyStringDescriptor[0] = (byte)DescriptorTypeIdentification.String;
1267 
1268                 return emptyStringDescriptor;
1269             }
1270 
1271             var unicodeBytes = unicode.GetBytes(unicodeString);
1272             var length = 2 + unicodeBytes.Length; // bLength (1 byte) + bDescriptorIDN (1 byte) + Unicode string length
1273             var fullDescriptor = new byte[length];
1274             fullDescriptor[0] = (byte)length;
1275             fullDescriptor[1] = (byte)DescriptorTypeIdentification.String;
1276             Array.Copy(unicodeBytes, 0, fullDescriptor, 2, unicodeBytes.Length);
1277 
1278             return fullDescriptor;
1279         }
1280 
IsWellKnownLU(byte lun)1281         private bool IsWellKnownLU(byte lun)
1282         {
1283             return BitHelper.IsBitSet(lun, 7);
1284         }
1285 
1286         public static readonly VitalProductDataPageCode[] SupportedVPDPages = new VitalProductDataPageCode[]
1287         {
1288             VitalProductDataPageCode.SupportedVPDPages,
1289             VitalProductDataPageCode.ModePagePolicy
1290         };
1291 
1292         public int LogicalUnits { get; }
1293         public byte IndexManufacturerName { get; }
1294         public byte IndexProductName { get; }
1295         public byte IndexSerialNumber { get; }
1296         public byte IndexOemID { get; }
1297         public byte IndexProductRevisionLevel { get; }
1298         public string ManufacturerName { get; }
1299         public string ProductName { get; }
1300         public string SerialNumber { get; }
1301         public string OemID { get; }
1302         public string ProductRevisionLevel { get; }
1303         public byte MaxInBufferSize { get; }
1304         public byte MaxOutBufferSize { get; }
1305         public ulong LogicalBlockSize { get; }
1306         public ulong LogicalBlockCount { get; }
1307         public byte LogicalBlockSizeExponentBase2 => (byte)BitHelper.GetMostSignificantSetBitIndex(LogicalBlockSize);
1308 
1309         private DeviceDescriptor deviceDescriptor;
1310         private ConfigurationDescriptorHeader[] configurationDescriptors;
1311         private RPMBUnitDescriptor rpmbUnitDescriptor;
1312         private PowerParametersDescriptor powerParametersDescriptor;
1313         private InterconnectDescriptor interconnectDescriptor;
1314         private GeometryDescriptor geometryDescriptor;
1315         private DeviceHealthDescriptor deviceHealthDescriptor;
1316         private string[] stringDescriptors;
1317         private Dictionary<UFSDeviceFlag, bool> flags;
1318         private Dictionary<UFSDeviceAttribute, ulong> attributes;
1319         private Dictionary<byte, UnitDescriptor> unitDescriptors;
1320         private Stream[] dataBackends;
1321 
1322         private const int WellKnownLUNsNumber = 4;
1323         private const int MaxLogicalUnits = 32;
1324         private const int MinimumLogicalBlockSize = 4096;
1325         private const int MaxAllowedInBufferSize = 255;
1326         private const int MaxAllowedOutBufferSize = 255;
1327         private const int MaxNumberOfStringDescriptors = 256;
1328         private const int BasicUPIULength = 32; // UPIU length without CRC and data segment
1329         private const byte ReportLUNStandardLogicalUnitAddressing = 0b00000000; // Format used for standard logical unit addressing
1330         private const byte ReportLUNWellKnownLogicalUnitAddressing = 0b11000001; // Format used for well known logical unit addressing
1331         private const int ManufacturerNameLength = 8;
1332         private const int ProductNameLength = 16;
1333         private const int ProductRevisionLevelLength = 4;
1334         private const int OemIDMaxLength = 126;
1335         private const int SerialNumberMaxLength = 126;
1336 
1337         private enum SelectReport : byte
1338         {
1339             LogicalUnits = 0x00,
1340             WellKnownLogicalUnits = 0x01,
1341             AllLogicalUnits = 0x02
1342         }
1343 
1344         private enum WellKnownLUNId : byte
1345         {
1346             ReportLUNs = 0x01,
1347             UFSDevice = 0x50,
1348             Boot = 0x30,
1349             RPMB = 0x44
1350         }
1351 
1352         private enum UFSDeviceFlag : byte
1353         {
1354             Reserved0 = 0x00,
1355             DeviceInit = 0x01,
1356             PermanentWPEn = 0x02,
1357             PowerOnWPEn = 0x03,
1358             BackgroundOpsEn = 0x04,
1359             DeviceLifeSpanModeEn = 0x05,
1360             PurgeEnable = 0x06,
1361             RefreshEnable = 0x07,
1362             PhyResourceRemoval = 0x08,
1363             BusyRTC = 0x09,
1364             Reserved1 = 0x0a,
1365             PermanentlyDisableFwUpdate = 0x0b,
1366             Reserved2 = 0x0c,
1367             Reserved3 = 0x0d,
1368             WriteBoosterEn = 0x0e,
1369             WriteBoosterBufferFlushEn = 0x0f,
1370             WriteBoosterBufferFlushDuringHibernate = 0x10,
1371         }
1372 
1373         private enum UFSDeviceAttribute : byte
1374         {
1375             BootLunEn = 0x00,
1376             Reserved0 = 0x01,
1377             CurrentPowerMode = 0x02,
1378             ActiveICCLevel = 0x03,
1379             OutOfOrderDataEn = 0x04,
1380             BackgroundOpStatus = 0x05,
1381             PurgeStatus = 0x06,
1382             MaxDataInSize = 0x07,
1383             MaxDataOutSize = 0x08,
1384             DynCapNeeded = 0x09,
1385             RefClkFreq = 0x0a,
1386             ConfigDescrLock = 0x0b,
1387             MaxNumOfRTT = 0x0c,
1388             ExceptionEventControl = 0x0d,
1389             ExceptionEventStatus = 0x0e,
1390             SecondsPassed = 0x0f,
1391             ContextConf = 0x10,
1392             Obsolete = 0x11,
1393             Reserved1 = 0x12,
1394             Reserved2 = 0x13,
1395             DeviceFFUStatus = 0x14,
1396             PSAState = 0x15,
1397             PSADataSize = 0x16,
1398             RefClkGatingWaitTime = 0x17,
1399             DeviceCaseRoughTemperaure = 0x18,
1400             DeviceTooHighTempBoundary = 0x19,
1401             DeviceTooLowTempBoundary = 0x1a,
1402             ThrottlingStatus = 0x1b,
1403             WriteBoosterBufferFlushStatus = 0x1c,
1404             AvailableWriteBoosterBufferSize = 0x1d,
1405             WriteBoosterBufferLifeTimeEst = 0x1e,
1406             CurrentWriteBoosterBufferSize = 0x1f,
1407             EXTIIDEn = 0x2a,
1408             HostHintCacheSize = 0x2b,
1409             RefreshStatus = 0x2c,
1410             RefreshFreq = 0x2d,
1411             RefreshUnit = 0x2e,
1412             RefreshMethod = 0x2f,
1413             Timestamp = 0x30,
1414         }
1415 
1416         private enum DescriptorTypeIdentification : byte
1417         {
1418             Device = 0x0,
1419             Configuration = 0x01,
1420             Unit = 0x02,
1421             Interconnect = 0x04,
1422             String = 0x05,
1423             Geometry = 0x07,
1424             Power = 0x08,
1425             DeviceHealth = 0x09,
1426             ReservedFBOExtension = 0x0a,
1427         }
1428     }
1429 }
1430