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