]> git.proxmox.com Git - mirror_edk2.git/blame - MdeModulePkg/Bus/Usb/UsbMassStorageDxe/UsbMassBoot.c
MdeModulePkg: Replace BSD License with BSD+Patent License
[mirror_edk2.git] / MdeModulePkg / Bus / Usb / UsbMassStorageDxe / UsbMassBoot.c
CommitLineData
e237e7ae 1/** @file\r
d80ed2a7 2 Implementation of the command set of USB Mass Storage Specification\r
3 for Bootability, Revision 1.0.\r
cc5166ff 4\r
e1490553 5Copyright (c) 2007 - 2018, Intel Corporation. All rights reserved.<BR>\r
9d510e61 6SPDX-License-Identifier: BSD-2-Clause-Patent\r
e237e7ae 7\r
cc5166ff 8**/\r
e237e7ae 9\r
39840c50 10#include "UsbMass.h"\r
e237e7ae 11\r
e237e7ae 12/**\r
d80ed2a7 13 Execute REQUEST SENSE Command to retrieve sense data from device.\r
e237e7ae 14\r
d80ed2a7 15 @param UsbMass The device whose sense data is requested.\r
e237e7ae 16\r
ed356b9e 17 @retval EFI_SUCCESS The command is executed successfully.\r
cc5166ff 18 @retval EFI_DEVICE_ERROR Failed to request sense.\r
19 @retval EFI_NO_RESPONSE The device media doesn't response this request.\r
20 @retval EFI_INVALID_PARAMETER The command has some invalid parameters.\r
21 @retval EFI_WRITE_PROTECTED The device is write protected.\r
22 @retval EFI_MEDIA_CHANGED The device media has been changed.\r
e237e7ae 23\r
24**/\r
25EFI_STATUS\r
26UsbBootRequestSense (\r
27 IN USB_MASS_DEVICE *UsbMass\r
28 )\r
29{\r
30 USB_BOOT_REQUEST_SENSE_CMD SenseCmd;\r
31 USB_BOOT_REQUEST_SENSE_DATA SenseData;\r
32 EFI_BLOCK_IO_MEDIA *Media;\r
33 USB_MASS_TRANSPORT *Transport;\r
34 EFI_STATUS Status;\r
35 UINT32 CmdResult;\r
36\r
37 Transport = UsbMass->Transport;\r
38\r
39 //\r
d80ed2a7 40 // Request the sense data from the device\r
e237e7ae 41 //\r
42 ZeroMem (&SenseCmd, sizeof (USB_BOOT_REQUEST_SENSE_CMD));\r
43 ZeroMem (&SenseData, sizeof (USB_BOOT_REQUEST_SENSE_DATA));\r
44\r
45 SenseCmd.OpCode = USB_BOOT_REQUEST_SENSE_OPCODE;\r
c52fa98c 46 SenseCmd.Lun = (UINT8) (USB_BOOT_LUN (UsbMass->Lun));\r
c9325700 47 SenseCmd.AllocLen = (UINT8) sizeof (USB_BOOT_REQUEST_SENSE_DATA);\r
e237e7ae 48\r
49 Status = Transport->ExecCommand (\r
50 UsbMass->Context,\r
51 &SenseCmd,\r
52 sizeof (USB_BOOT_REQUEST_SENSE_CMD),\r
53 EfiUsbDataIn,\r
54 &SenseData,\r
55 sizeof (USB_BOOT_REQUEST_SENSE_DATA),\r
c7e39923 56 UsbMass->Lun,\r
e237e7ae 57 USB_BOOT_GENERAL_CMD_TIMEOUT,\r
58 &CmdResult\r
59 );\r
60 if (EFI_ERROR (Status) || CmdResult != USB_MASS_CMD_SUCCESS) {\r
1c619535 61 DEBUG ((EFI_D_ERROR, "UsbBootRequestSense: (%r) CmdResult=0x%x\n", Status, CmdResult));\r
88e349f1 62 if (!EFI_ERROR (Status)) {\r
63 Status = EFI_DEVICE_ERROR;\r
64 }\r
50fa1b3a 65 return Status;\r
e237e7ae 66 }\r
67\r
68 //\r
d80ed2a7 69 // If sense data is retrieved successfully, interpret the sense data\r
70 // and update the media status if necessary.\r
e237e7ae 71 //\r
72 Media = &UsbMass->BlockIoMedia;\r
73\r
74 switch (USB_BOOT_SENSE_KEY (SenseData.SenseKey)) {\r
75\r
76 case USB_BOOT_SENSE_NO_SENSE:\r
275b96da
MK
77 if (SenseData.Asc == USB_BOOT_ASC_NO_ADDITIONAL_SENSE_INFORMATION) {\r
78 //\r
79 // It is not an error if a device does not have additional sense information\r
80 //\r
81 Status = EFI_SUCCESS;\r
82 } else {\r
83 Status = EFI_NO_RESPONSE;\r
84 }\r
50fa1b3a 85 break;\r
86\r
e237e7ae 87 case USB_BOOT_SENSE_RECOVERED:\r
88 //\r
89 // Suppose hardware can handle this case, and recover later by itself\r
90 //\r
91 Status = EFI_NOT_READY;\r
92 break;\r
93\r
94 case USB_BOOT_SENSE_NOT_READY:\r
50fa1b3a 95 Status = EFI_DEVICE_ERROR;\r
1ccdbf2a 96 if (SenseData.Asc == USB_BOOT_ASC_NO_MEDIA) {\r
d80ed2a7 97 Media->MediaPresent = FALSE;\r
98 Status = EFI_NO_MEDIA;\r
1ccdbf2a 99 } else if (SenseData.Asc == USB_BOOT_ASC_NOT_READY) {\r
d80ed2a7 100 Status = EFI_NOT_READY;\r
e237e7ae 101 }\r
102 break;\r
103\r
104 case USB_BOOT_SENSE_ILLEGAL_REQUEST:\r
105 Status = EFI_INVALID_PARAMETER;\r
106 break;\r
107\r
108 case USB_BOOT_SENSE_UNIT_ATTENTION:\r
109 Status = EFI_DEVICE_ERROR;\r
1ccdbf2a 110 if (SenseData.Asc == USB_BOOT_ASC_MEDIA_CHANGE) {\r
50fa1b3a 111 //\r
d80ed2a7 112 // If MediaChange, reset ReadOnly and new MediaId\r
50fa1b3a 113 //\r
d80ed2a7 114 Status = EFI_MEDIA_CHANGED;\r
50fa1b3a 115 Media->ReadOnly = FALSE;\r
116 Media->MediaId++;\r
d8030c2a
MK
117 } else if (SenseData.Asc == USB_BOOT_ASC_NOT_READY) {\r
118 Status = EFI_NOT_READY;\r
119 } else if (SenseData.Asc == USB_BOOT_ASC_NO_MEDIA) {\r
120 Status = EFI_NOT_READY;\r
e237e7ae 121 }\r
122 break;\r
123\r
d80ed2a7 124 case USB_BOOT_SENSE_DATA_PROTECT:\r
125 Status = EFI_WRITE_PROTECTED;\r
50fa1b3a 126 Media->ReadOnly = TRUE;\r
e237e7ae 127 break;\r
128\r
129 default:\r
130 Status = EFI_DEVICE_ERROR;\r
131 break;\r
132 }\r
133\r
e1490553 134 DEBUG ((EFI_D_INFO, "UsbBootRequestSense: (%r) with error code (%x) sense key %x/%x/%x\n",\r
e237e7ae 135 Status,\r
e1490553 136 SenseData.ErrorCode,\r
e237e7ae 137 USB_BOOT_SENSE_KEY (SenseData.SenseKey),\r
1ccdbf2a 138 SenseData.Asc,\r
139 SenseData.Ascq\r
e237e7ae 140 ));\r
141\r
142 return Status;\r
143}\r
144\r
145\r
146/**\r
d80ed2a7 147 Execute the USB mass storage bootability commands.\r
148\r
149 This function executes the USB mass storage bootability commands.\r
150 If execution failed, retrieve the error by REQUEST_SENSE, then\r
151 update the device's status, such as ReadyOnly.\r
e237e7ae 152\r
153 @param UsbMass The device to issue commands to\r
154 @param Cmd The command to execute\r
155 @param CmdLen The length of the command\r
156 @param DataDir The direction of data transfer\r
157 @param Data The buffer to hold the data\r
158 @param DataLen The length of expected data\r
159 @param Timeout The timeout used to transfer\r
160\r
ed356b9e 161 @retval EFI_SUCCESS Command is executed successfully\r
d80ed2a7 162 @retval Others Command execution failed.\r
e237e7ae 163\r
164**/\r
e237e7ae 165EFI_STATUS\r
166UsbBootExecCmd (\r
167 IN USB_MASS_DEVICE *UsbMass,\r
168 IN VOID *Cmd,\r
169 IN UINT8 CmdLen,\r
170 IN EFI_USB_DATA_DIRECTION DataDir,\r
171 IN VOID *Data,\r
172 IN UINT32 DataLen,\r
173 IN UINT32 Timeout\r
174 )\r
175{\r
176 USB_MASS_TRANSPORT *Transport;\r
177 EFI_STATUS Status;\r
178 UINT32 CmdResult;\r
179\r
180 Transport = UsbMass->Transport;\r
181 Status = Transport->ExecCommand (\r
182 UsbMass->Context,\r
183 Cmd,\r
184 CmdLen,\r
185 DataDir,\r
186 Data,\r
187 DataLen,\r
c7e39923 188 UsbMass->Lun,\r
e237e7ae 189 Timeout,\r
190 &CmdResult\r
191 );\r
19bc8527 192\r
193 if (Status == EFI_TIMEOUT) {\r
e1490553 194 DEBUG ((EFI_D_ERROR, "UsbBootExecCmd: %r to Exec 0x%x Cmd\n", Status, *(UINT8 *)Cmd));\r
19bc8527 195 return EFI_TIMEOUT;\r
196 }\r
197\r
e237e7ae 198 //\r
d80ed2a7 199 // If ExecCommand() returns no error and CmdResult is success,\r
200 // then the commnad transfer is successful.\r
e237e7ae 201 //\r
d80ed2a7 202 if ((CmdResult == USB_MASS_CMD_SUCCESS) && !EFI_ERROR (Status)) {\r
e237e7ae 203 return EFI_SUCCESS;\r
204 }\r
205\r
d80ed2a7 206 //\r
207 // If command execution failed, then retrieve error info via sense request.\r
208 //\r
e1490553 209 DEBUG ((EFI_D_ERROR, "UsbBootExecCmd: %r to Exec 0x%x Cmd (Result = %x)\n", Status, *(UINT8 *)Cmd, CmdResult));\r
e237e7ae 210 return UsbBootRequestSense (UsbMass);\r
211}\r
212\r
213\r
214/**\r
d80ed2a7 215 Execute the USB mass storage bootability commands with retrial.\r
e237e7ae 216\r
d80ed2a7 217 This function executes USB mass storage bootability commands.\r
218 If the device isn't ready, wait for it. If the device is ready\r
219 and error occurs, retry the command again until it exceeds the\r
220 limit of retrial times.\r
d1102dba 221\r
e237e7ae 222 @param UsbMass The device to issue commands to\r
223 @param Cmd The command to execute\r
224 @param CmdLen The length of the command\r
225 @param DataDir The direction of data transfer\r
226 @param Data The buffer to hold the data\r
227 @param DataLen The length of expected data\r
cc5166ff 228 @param Timeout The timeout used to transfer\r
e237e7ae 229\r
d80ed2a7 230 @retval EFI_SUCCESS The command is executed successfully.\r
06e24096 231 @retval EFI_NO_MEDIA The device media is removed.\r
d80ed2a7 232 @retval Others Command execution failed after retrial.\r
e237e7ae 233\r
234**/\r
e237e7ae 235EFI_STATUS\r
236UsbBootExecCmdWithRetry (\r
237 IN USB_MASS_DEVICE *UsbMass,\r
238 IN VOID *Cmd,\r
239 IN UINT8 CmdLen,\r
240 IN EFI_USB_DATA_DIRECTION DataDir,\r
241 IN VOID *Data,\r
242 IN UINT32 DataLen,\r
243 IN UINT32 Timeout\r
244 )\r
245{\r
246 EFI_STATUS Status;\r
d80ed2a7 247 UINTN Retry;\r
19bc8527 248 EFI_EVENT TimeoutEvt;\r
249\r
250 Retry = 0;\r
251 Status = EFI_SUCCESS;\r
252 Status = gBS->CreateEvent (\r
253 EVT_TIMER,\r
254 TPL_CALLBACK,\r
255 NULL,\r
256 NULL,\r
257 &TimeoutEvt\r
258 );\r
259 if (EFI_ERROR (Status)) {\r
260 return Status;\r
261 }\r
e237e7ae 262\r
19bc8527 263 Status = gBS->SetTimer (TimeoutEvt, TimerRelative, EFI_TIMER_PERIOD_SECONDS(60));\r
264 if (EFI_ERROR (Status)) {\r
265 goto EXIT;\r
266 }\r
e237e7ae 267\r
19bc8527 268 //\r
269 // Execute the cmd and retry if it fails.\r
270 //\r
271 while (EFI_ERROR (gBS->CheckEvent (TimeoutEvt))) {\r
e237e7ae 272 Status = UsbBootExecCmd (\r
273 UsbMass,\r
274 Cmd,\r
275 CmdLen,\r
276 DataDir,\r
277 Data,\r
278 DataLen,\r
1c619535 279 Timeout\r
e237e7ae 280 );\r
06e24096 281 if (Status == EFI_SUCCESS || Status == EFI_NO_MEDIA) {\r
e237e7ae 282 break;\r
283 }\r
284 //\r
19bc8527 285 // If the sense data shows the drive is not ready, we need execute the cmd again.\r
286 // We limit the upper boundary to 60 seconds.\r
e237e7ae 287 //\r
19bc8527 288 if (Status == EFI_NOT_READY) {\r
289 continue;\r
e237e7ae 290 }\r
19bc8527 291 //\r
292 // If the status is other error, then just retry 5 times.\r
293 //\r
294 if (Retry++ >= USB_BOOT_COMMAND_RETRY) {\r
295 break;\r
296 }\r
297 }\r
298\r
299EXIT:\r
300 if (TimeoutEvt != NULL) {\r
301 gBS->CloseEvent (TimeoutEvt);\r
e237e7ae 302 }\r
303\r
304 return Status;\r
305}\r
306\r
307\r
e237e7ae 308/**\r
d80ed2a7 309 Execute TEST UNIT READY command to check if the device is ready.\r
e237e7ae 310\r
311 @param UsbMass The device to test\r
312\r
d80ed2a7 313 @retval EFI_SUCCESS The device is ready.\r
e237e7ae 314 @retval Others Device not ready.\r
315\r
316**/\r
317EFI_STATUS\r
318UsbBootIsUnitReady (\r
319 IN USB_MASS_DEVICE *UsbMass\r
320 )\r
321{\r
322 USB_BOOT_TEST_UNIT_READY_CMD TestCmd;\r
323\r
324 ZeroMem (&TestCmd, sizeof (USB_BOOT_TEST_UNIT_READY_CMD));\r
325\r
326 TestCmd.OpCode = USB_BOOT_TEST_UNIT_READY_OPCODE;\r
c52fa98c 327 TestCmd.Lun = (UINT8) (USB_BOOT_LUN (UsbMass->Lun));\r
e237e7ae 328\r
329 return UsbBootExecCmdWithRetry (\r
330 UsbMass,\r
331 &TestCmd,\r
c9325700 332 (UINT8) sizeof (USB_BOOT_TEST_UNIT_READY_CMD),\r
e237e7ae 333 EfiUsbNoData,\r
334 NULL,\r
335 0,\r
336 USB_BOOT_GENERAL_CMD_TIMEOUT\r
337 );\r
338}\r
339\r
340\r
341/**\r
d80ed2a7 342 Execute INQUIRY Command to request information regarding parameters of\r
343 the device be sent to the host computer.\r
e237e7ae 344\r
d80ed2a7 345 @param UsbMass The device to inquire.\r
e237e7ae 346\r
d80ed2a7 347 @retval EFI_SUCCESS INQUIRY Command is executed successfully.\r
348 @retval Others INQUIRY Command is not executed successfully.\r
e237e7ae 349\r
350**/\r
351EFI_STATUS\r
352UsbBootInquiry (\r
353 IN USB_MASS_DEVICE *UsbMass\r
354 )\r
355{\r
356 USB_BOOT_INQUIRY_CMD InquiryCmd;\r
e237e7ae 357 EFI_BLOCK_IO_MEDIA *Media;\r
358 EFI_STATUS Status;\r
359\r
360 Media = &(UsbMass->BlockIoMedia);\r
361\r
e237e7ae 362 ZeroMem (&InquiryCmd, sizeof (USB_BOOT_INQUIRY_CMD));\r
39840c50 363 ZeroMem (&UsbMass->InquiryData, sizeof (USB_BOOT_INQUIRY_DATA));\r
e237e7ae 364\r
365 InquiryCmd.OpCode = USB_BOOT_INQUIRY_OPCODE;\r
c52fa98c 366 InquiryCmd.Lun = (UINT8) (USB_BOOT_LUN (UsbMass->Lun));\r
39840c50 367 InquiryCmd.AllocLen = (UINT8) sizeof (USB_BOOT_INQUIRY_DATA);\r
e237e7ae 368\r
369 Status = UsbBootExecCmdWithRetry (\r
370 UsbMass,\r
371 &InquiryCmd,\r
c9325700 372 (UINT8) sizeof (USB_BOOT_INQUIRY_CMD),\r
e237e7ae 373 EfiUsbDataIn,\r
39840c50 374 &UsbMass->InquiryData,\r
e237e7ae 375 sizeof (USB_BOOT_INQUIRY_DATA),\r
50fa1b3a 376 USB_BOOT_GENERAL_CMD_TIMEOUT\r
e237e7ae 377 );\r
378 if (EFI_ERROR (Status)) {\r
379 return Status;\r
380 }\r
381\r
d80ed2a7 382 //\r
383 // Get information from PDT (Peripheral Device Type) field and Removable Medium Bit\r
384 // from the inquiry data.\r
385 //\r
39840c50 386 UsbMass->Pdt = (UINT8) (USB_BOOT_PDT (UsbMass->InquiryData.Pdt));\r
387 Media->RemovableMedia = (BOOLEAN) (USB_BOOT_REMOVABLE (UsbMass->InquiryData.Removable));\r
e237e7ae 388 //\r
d80ed2a7 389 // Set block size to the default value of 512 Bytes, in case no media is present at first time.\r
e237e7ae 390 //\r
391 Media->BlockSize = 0x0200;\r
392\r
393 return Status;\r
394}\r
395\r
99c1725e 396/**\r
397 Execute READ CAPACITY 16 bytes command to request information regarding\r
398 the capacity of the installed medium of the device.\r
399\r
400 This function executes READ CAPACITY 16 bytes command to get the capacity\r
401 of the USB mass storage media, including the presence, block size,\r
402 and last block number.\r
403\r
404 @param UsbMass The device to retireve disk gemotric.\r
405\r
406 @retval EFI_SUCCESS The disk geometry is successfully retrieved.\r
407 @retval EFI_NOT_READY The returned block size is zero.\r
408 @retval Other READ CAPACITY 16 bytes command execution failed.\r
d1102dba 409\r
99c1725e 410**/\r
411EFI_STATUS\r
412UsbBootReadCapacity16 (\r
413 IN USB_MASS_DEVICE *UsbMass\r
414 )\r
415{\r
416 UINT8 CapacityCmd[16];\r
417 EFI_SCSI_DISK_CAPACITY_DATA16 CapacityData;\r
418 EFI_BLOCK_IO_MEDIA *Media;\r
419 EFI_STATUS Status;\r
420 UINT32 BlockSize;\r
421\r
422 Media = &UsbMass->BlockIoMedia;\r
423\r
424 Media->MediaPresent = FALSE;\r
425 Media->LastBlock = 0;\r
426 Media->BlockSize = 0;\r
427\r
428 ZeroMem (CapacityCmd, sizeof (CapacityCmd));\r
429 ZeroMem (&CapacityData, sizeof (CapacityData));\r
430\r
431 CapacityCmd[0] = EFI_SCSI_OP_READ_CAPACITY16;\r
432 CapacityCmd[1] = 0x10;\r
433 //\r
434 // Partial medium indicator, set the bytes 2 ~ 9 of the Cdb as ZERO.\r
435 //\r
436 ZeroMem ((CapacityCmd + 2), 8);\r
437\r
438 CapacityCmd[13] = sizeof (CapacityData);\r
d1102dba 439\r
99c1725e 440 Status = UsbBootExecCmdWithRetry (\r
441 UsbMass,\r
442 CapacityCmd,\r
443 (UINT8) sizeof (CapacityCmd),\r
444 EfiUsbDataIn,\r
445 &CapacityData,\r
446 sizeof (CapacityData),\r
447 USB_BOOT_GENERAL_CMD_TIMEOUT\r
448 );\r
449 if (EFI_ERROR (Status)) {\r
450 return Status;\r
451 }\r
452\r
453 //\r
454 // Get the information on media presence, block size, and last block number\r
455 // from READ CAPACITY data.\r
456 //\r
457 Media->MediaPresent = TRUE;\r
458 Media->LastBlock = SwapBytes64 (ReadUnaligned64 ((CONST UINT64 *) &(CapacityData.LastLba7)));\r
459\r
460 BlockSize = SwapBytes32 (ReadUnaligned32 ((CONST UINT32 *) &(CapacityData.BlockSize3)));\r
d1102dba 461\r
99c1725e 462 Media->LowestAlignedLba = (CapacityData.LowestAlignLogic2 << 8) |\r
463 CapacityData.LowestAlignLogic1;\r
464 Media->LogicalBlocksPerPhysicalBlock = (1 << CapacityData.LogicPerPhysical);\r
465 if (BlockSize == 0) {\r
466 //\r
d1102dba 467 // Get sense data\r
99c1725e 468 //\r
469 return UsbBootRequestSense (UsbMass);\r
470 } else {\r
471 Media->BlockSize = BlockSize;\r
472 }\r
473\r
474 return Status;\r
475}\r
476\r
e237e7ae 477\r
478/**\r
d80ed2a7 479 Execute READ CAPACITY command to request information regarding\r
480 the capacity of the installed medium of the device.\r
481\r
482 This function executes READ CAPACITY command to get the capacity\r
483 of the USB mass storage media, including the presence, block size,\r
484 and last block number.\r
e237e7ae 485\r
486 @param UsbMass The device to retireve disk gemotric.\r
487\r
d80ed2a7 488 @retval EFI_SUCCESS The disk geometry is successfully retrieved.\r
489 @retval EFI_NOT_READY The returned block size is zero.\r
490 @retval Other READ CAPACITY command execution failed.\r
d1102dba 491\r
e237e7ae 492**/\r
493EFI_STATUS\r
494UsbBootReadCapacity (\r
495 IN USB_MASS_DEVICE *UsbMass\r
496 )\r
497{\r
498 USB_BOOT_READ_CAPACITY_CMD CapacityCmd;\r
499 USB_BOOT_READ_CAPACITY_DATA CapacityData;\r
500 EFI_BLOCK_IO_MEDIA *Media;\r
501 EFI_STATUS Status;\r
88e349f1 502 UINT32 BlockSize;\r
e237e7ae 503\r
504 Media = &UsbMass->BlockIoMedia;\r
505\r
e237e7ae 506 ZeroMem (&CapacityCmd, sizeof (USB_BOOT_READ_CAPACITY_CMD));\r
507 ZeroMem (&CapacityData, sizeof (USB_BOOT_READ_CAPACITY_DATA));\r
508\r
509 CapacityCmd.OpCode = USB_BOOT_READ_CAPACITY_OPCODE;\r
c52fa98c 510 CapacityCmd.Lun = (UINT8) (USB_BOOT_LUN (UsbMass->Lun));\r
e237e7ae 511\r
512 Status = UsbBootExecCmdWithRetry (\r
513 UsbMass,\r
514 &CapacityCmd,\r
c9325700 515 (UINT8) sizeof (USB_BOOT_READ_CAPACITY_CMD),\r
e237e7ae 516 EfiUsbDataIn,\r
517 &CapacityData,\r
518 sizeof (USB_BOOT_READ_CAPACITY_DATA),\r
519 USB_BOOT_GENERAL_CMD_TIMEOUT\r
520 );\r
521 if (EFI_ERROR (Status)) {\r
522 return Status;\r
523 }\r
524\r
d80ed2a7 525 //\r
526 // Get the information on media presence, block size, and last block number\r
527 // from READ CAPACITY data.\r
528 //\r
e237e7ae 529 Media->MediaPresent = TRUE;\r
d80ed2a7 530 Media->LastBlock = SwapBytes32 (ReadUnaligned32 ((CONST UINT32 *) CapacityData.LastLba));\r
e237e7ae 531\r
88e349f1 532 BlockSize = SwapBytes32 (ReadUnaligned32 ((CONST UINT32 *) CapacityData.BlockLen));\r
533 if (BlockSize == 0) {\r
efe9186f 534 //\r
d1102dba 535 // Get sense data\r
efe9186f 536 //\r
537 return UsbBootRequestSense (UsbMass);\r
88e349f1 538 } else {\r
539 Media->BlockSize = BlockSize;\r
50fa1b3a 540 }\r
541\r
99c1725e 542 if (Media->LastBlock == 0xFFFFFFFF) {\r
543 Status = UsbBootReadCapacity16 (UsbMass);\r
544 if (!EFI_ERROR (Status)) {\r
545 UsbMass->Cdb16Byte = TRUE;\r
546 }\r
547 }\r
548\r
efe9186f 549 return Status;\r
e237e7ae 550}\r
551\r
e237e7ae 552/**\r
d80ed2a7 553 Retrieves SCSI mode sense information via MODE SENSE(6) command.\r
e237e7ae 554\r
d80ed2a7 555 @param UsbMass The device whose sense data is requested.\r
e237e7ae 556\r
d80ed2a7 557 @retval EFI_SUCCESS SCSI mode sense information retrieved successfully.\r
558 @retval Other Command execution failed.\r
e237e7ae 559\r
560**/\r
561EFI_STATUS\r
50fa1b3a 562UsbScsiModeSense (\r
e237e7ae 563 IN USB_MASS_DEVICE *UsbMass\r
564 )\r
565{\r
ca12415a 566 EFI_STATUS Status;\r
50fa1b3a 567 USB_SCSI_MODE_SENSE6_CMD ModeSenseCmd;\r
568 USB_SCSI_MODE_SENSE6_PARA_HEADER ModeParaHeader;\r
569 EFI_BLOCK_IO_MEDIA *Media;\r
570\r
ca12415a 571 Media = &UsbMass->BlockIoMedia;\r
e237e7ae 572\r
50fa1b3a 573 ZeroMem (&ModeSenseCmd, sizeof (USB_SCSI_MODE_SENSE6_CMD));\r
574 ZeroMem (&ModeParaHeader, sizeof (USB_SCSI_MODE_SENSE6_PARA_HEADER));\r
e237e7ae 575\r
576 //\r
d80ed2a7 577 // MODE SENSE(6) command is defined in Section 8.2.10 of SCSI-2 Spec\r
e237e7ae 578 //\r
50fa1b3a 579 ModeSenseCmd.OpCode = USB_SCSI_MODE_SENSE6_OPCODE;\r
687a2e5f 580 ModeSenseCmd.Lun = (UINT8) USB_BOOT_LUN (UsbMass->Lun);\r
50fa1b3a 581 ModeSenseCmd.PageCode = 0x3F;\r
582 ModeSenseCmd.AllocateLen = (UINT8) sizeof (USB_SCSI_MODE_SENSE6_PARA_HEADER);\r
e237e7ae 583\r
584 Status = UsbBootExecCmdWithRetry (\r
585 UsbMass,\r
586 &ModeSenseCmd,\r
c9325700 587 (UINT8) sizeof (USB_SCSI_MODE_SENSE6_CMD),\r
e237e7ae 588 EfiUsbDataIn,\r
589 &ModeParaHeader,\r
50fa1b3a 590 sizeof (USB_SCSI_MODE_SENSE6_PARA_HEADER),\r
e237e7ae 591 USB_BOOT_GENERAL_CMD_TIMEOUT\r
592 );\r
50fa1b3a 593\r
e237e7ae 594 //\r
d80ed2a7 595 // Format of device-specific parameter byte of the mode parameter header is defined in\r
596 // Section 8.2.10 of SCSI-2 Spec.\r
597 // BIT7 of this byte is indicates whether the medium is write protected.\r
e237e7ae 598 //\r
50fa1b3a 599 if (!EFI_ERROR (Status)) {\r
d80ed2a7 600 Media->ReadOnly = (BOOLEAN) ((ModeParaHeader.DevicePara & BIT7) != 0);\r
50fa1b3a 601 }\r
e237e7ae 602\r
603 return Status;\r
604}\r
605\r
606\r
607/**\r
d80ed2a7 608 Get the parameters for the USB mass storage media.\r
609\r
610 This function get the parameters for the USB mass storage media,\r
611 It is used both to initialize the media during the Start() phase\r
612 of Driver Binding Protocol and to re-initialize it when the media is\r
e237e7ae 613 changed. Althought the RemoveableMedia is unlikely to change,\r
d80ed2a7 614 it is also included here.\r
e237e7ae 615\r
d80ed2a7 616 @param UsbMass The device to retrieve disk gemotric.\r
e237e7ae 617\r
618 @retval EFI_SUCCESS The disk gemotric is successfully retrieved.\r
d80ed2a7 619 @retval Other Failed to get the parameters.\r
e237e7ae 620\r
621**/\r
622EFI_STATUS\r
623UsbBootGetParams (\r
624 IN USB_MASS_DEVICE *UsbMass\r
625 )\r
626{\r
627 EFI_BLOCK_IO_MEDIA *Media;\r
628 EFI_STATUS Status;\r
50fa1b3a 629\r
ca12415a 630 Media = &(UsbMass->BlockIoMedia);\r
e237e7ae 631\r
632 Status = UsbBootInquiry (UsbMass);\r
633 if (EFI_ERROR (Status)) {\r
1c619535 634 DEBUG ((EFI_D_ERROR, "UsbBootGetParams: UsbBootInquiry (%r)\n", Status));\r
e237e7ae 635 return Status;\r
636 }\r
637\r
66a5771e
TF
638 //\r
639 // According to USB Mass Storage Specification for Bootability, only following\r
640 // 4 Peripheral Device Types are in spec.\r
641 //\r
642 if ((UsbMass->Pdt != USB_PDT_DIRECT_ACCESS) &&\r
643 (UsbMass->Pdt != USB_PDT_CDROM) &&\r
644 (UsbMass->Pdt != USB_PDT_OPTICAL) &&\r
645 (UsbMass->Pdt != USB_PDT_SIMPLE_DIRECT)) {\r
646 DEBUG ((EFI_D_ERROR, "UsbBootGetParams: Found an unsupported peripheral type[%d]\n", UsbMass->Pdt));\r
647 return EFI_UNSUPPORTED;\r
648 }\r
649\r
e237e7ae 650 //\r
d80ed2a7 651 // Don't use the Removable bit in inquiry data to test whether the media\r
e237e7ae 652 // is removable because many flash disks wrongly set this bit.\r
653 //\r
654 if ((UsbMass->Pdt == USB_PDT_CDROM) || (UsbMass->Pdt == USB_PDT_OPTICAL)) {\r
655 //\r
50fa1b3a 656 // CD-Rom device and Non-CD optical device\r
e237e7ae 657 //\r
658 UsbMass->OpticalStorage = TRUE;\r
659 //\r
660 // Default value 2048 Bytes, in case no media present at first time\r
661 //\r
662 Media->BlockSize = 0x0800;\r
50fa1b3a 663 }\r
664\r
19d76ae6 665 Status = UsbBootDetectMedia (UsbMass);\r
e237e7ae 666\r
19d76ae6 667 return Status;\r
e237e7ae 668}\r
669\r
670\r
671/**\r
672 Detect whether the removable media is present and whether it has changed.\r
e237e7ae 673\r
d80ed2a7 674 @param UsbMass The device to check.\r
e237e7ae 675\r
d80ed2a7 676 @retval EFI_SUCCESS The media status is successfully checked.\r
677 @retval Other Failed to detect media.\r
e237e7ae 678\r
679**/\r
680EFI_STATUS\r
681UsbBootDetectMedia (\r
682 IN USB_MASS_DEVICE *UsbMass\r
683 )\r
684{\r
685 EFI_BLOCK_IO_MEDIA OldMedia;\r
686 EFI_BLOCK_IO_MEDIA *Media;\r
50fa1b3a 687 UINT8 CmdSet;\r
e237e7ae 688 EFI_STATUS Status;\r
689\r
690 Media = &UsbMass->BlockIoMedia;\r
e61d30b0 691\r
d80ed2a7 692 CopyMem (&OldMedia, &(UsbMass->BlockIoMedia), sizeof (EFI_BLOCK_IO_MEDIA));\r
e237e7ae 693\r
50fa1b3a 694 CmdSet = ((EFI_USB_INTERFACE_DESCRIPTOR *) (UsbMass->Context))->InterfaceSubClass;\r
695\r
e237e7ae 696 Status = UsbBootIsUnitReady (UsbMass);\r
06e24096
RN
697 if (EFI_ERROR (Status)) {\r
698 DEBUG ((EFI_D_ERROR, "UsbBootDetectMedia: UsbBootIsUnitReady (%r)\n", Status));\r
e237e7ae 699 }\r
50fa1b3a 700\r
06e24096
RN
701 //\r
702 // Status could be:\r
703 // EFI_SUCCESS: all good.\r
704 // EFI_NO_MEDIA: media is not present.\r
705 // others: HW error.\r
706 // For either EFI_NO_MEDIA, or HW error, skip to get WriteProtected and capacity information.\r
707 //\r
708 if (!EFI_ERROR (Status)) {\r
709 if ((UsbMass->Pdt != USB_PDT_CDROM) && (CmdSet == USB_MASS_STORE_SCSI)) {\r
710 //\r
711 // MODE SENSE is required for the device with PDT of 0x00/0x07/0x0E,\r
712 // according to Section 4 of USB Mass Storage Specification for Bootability.\r
713 // MODE SENSE(10) is useless here, while MODE SENSE(6) defined in SCSI\r
714 // could get the information of Write Protected.\r
715 // Since not all device support this command, skip if fail.\r
716 //\r
717 UsbScsiModeSense (UsbMass);\r
718 }\r
50fa1b3a 719\r
06e24096
RN
720 Status = UsbBootReadCapacity (UsbMass);\r
721 if (EFI_ERROR (Status)) {\r
722 DEBUG ((EFI_D_ERROR, "UsbBootDetectMedia: UsbBootReadCapacity (%r)\n", Status));\r
723 }\r
e237e7ae 724 }\r
725\r
06e24096
RN
726 if (EFI_ERROR (Status) && Status != EFI_NO_MEDIA) {\r
727 //\r
728 // For NoMedia, BlockIo is still needed.\r
729 //\r
730 return Status;\r
731 }\r
50fa1b3a 732\r
25d310d9
RN
733 //\r
734 // Simply reject device whose block size is unacceptable small (==0) or large (>64K).\r
735 //\r
736 if ((Media->BlockSize == 0) || (Media->BlockSize > USB_BOOT_MAX_CARRY_SIZE)) {\r
737 return EFI_DEVICE_ERROR;\r
738 }\r
739\r
e237e7ae 740 //\r
d80ed2a7 741 // Detect whether it is necessary to reinstall the Block I/O Protocol.\r
e237e7ae 742 //\r
50fa1b3a 743 // MediaId may change in RequestSense for MediaChanged\r
744 // MediaPresent may change in RequestSense for NoMedia\r
745 // MediaReadOnly may change in RequestSense for WriteProtected or MediaChanged\r
746 // MediaPresent/BlockSize/LastBlock may change in ReadCapacity\r
747 //\r
e237e7ae 748 if ((Media->MediaId != OldMedia.MediaId) ||\r
749 (Media->MediaPresent != OldMedia.MediaPresent) ||\r
750 (Media->ReadOnly != OldMedia.ReadOnly) ||\r
751 (Media->BlockSize != OldMedia.BlockSize) ||\r
752 (Media->LastBlock != OldMedia.LastBlock)) {\r
50fa1b3a 753\r
d80ed2a7 754 //\r
cd626ef0
RN
755 // This function is called from:\r
756 // Block I/O Protocol APIs, which run at TPL_CALLBACK.\r
757 // DriverBindingStart(), which raises to TPL_CALLBACK.\r
758 ASSERT (EfiGetCurrentTpl () == TPL_CALLBACK);\r
50fa1b3a 759\r
06e24096
RN
760 //\r
761 // When it is called from DriverBindingStart(), below reinstall fails.\r
762 // So ignore the return status check.\r
763 //\r
e237e7ae 764 gBS->ReinstallProtocolInterface (\r
765 UsbMass->Controller,\r
766 &gEfiBlockIoProtocolGuid,\r
767 &UsbMass->BlockIo,\r
768 &UsbMass->BlockIo\r
769 );\r
50fa1b3a 770\r
e237e7ae 771 //\r
06e24096 772 // Reset MediaId after reinstalling Block I/O Protocol.\r
e237e7ae 773 //\r
50fa1b3a 774 if (Media->MediaPresent != OldMedia.MediaPresent) {\r
d80ed2a7 775 if (Media->MediaPresent) {\r
50fa1b3a 776 Media->MediaId = 1;\r
777 } else {\r
778 Media->MediaId = 0;\r
779 }\r
e237e7ae 780 }\r
50fa1b3a 781\r
782 if ((Media->ReadOnly != OldMedia.ReadOnly) ||\r
783 (Media->BlockSize != OldMedia.BlockSize) ||\r
784 (Media->LastBlock != OldMedia.LastBlock)) {\r
785 Media->MediaId++;\r
e237e7ae 786 }\r
06e24096
RN
787\r
788 Status = Media->MediaPresent ? EFI_MEDIA_CHANGED : EFI_NO_MEDIA;\r
e237e7ae 789 }\r
790\r
791 return Status;\r
792}\r
793\r
794\r
795/**\r
e59db6a7 796 Read or write some blocks from the device.\r
e237e7ae 797\r
e59db6a7
RN
798 @param UsbMass The USB mass storage device to access\r
799 @param Write TRUE for write operation.\r
e237e7ae 800 @param Lba The start block number\r
e59db6a7
RN
801 @param TotalBlock Total block number to read or write\r
802 @param Buffer The buffer to read to or write from\r
e237e7ae 803\r
e59db6a7
RN
804 @retval EFI_SUCCESS Data are read into the buffer or writen into the device.\r
805 @retval Others Failed to read or write all the data\r
e237e7ae 806\r
807**/\r
808EFI_STATUS\r
e59db6a7 809UsbBootReadWriteBlocks (\r
e237e7ae 810 IN USB_MASS_DEVICE *UsbMass,\r
e59db6a7 811 IN BOOLEAN Write,\r
e237e7ae 812 IN UINT32 Lba,\r
813 IN UINTN TotalBlock,\r
e59db6a7 814 IN OUT UINT8 *Buffer\r
e237e7ae 815 )\r
816{\r
e59db6a7
RN
817 USB_BOOT_READ_WRITE_10_CMD Cmd;\r
818 EFI_STATUS Status;\r
4f8b2f9d
RN
819 UINT32 Count;\r
820 UINT32 CountMax;\r
e59db6a7
RN
821 UINT32 BlockSize;\r
822 UINT32 ByteSize;\r
823 UINT32 Timeout;\r
e237e7ae 824\r
825 BlockSize = UsbMass->BlockIoMedia.BlockSize;\r
4f8b2f9d 826 CountMax = USB_BOOT_MAX_CARRY_SIZE / BlockSize;\r
e237e7ae 827 Status = EFI_SUCCESS;\r
828\r
829 while (TotalBlock > 0) {\r
830 //\r
831 // Split the total blocks into smaller pieces to ease the pressure\r
832 // on the device. We must split the total block because the READ10\r
833 // command only has 16 bit transfer length (in the unit of block).\r
834 //\r
4f8b2f9d
RN
835 Count = (UINT32)MIN (TotalBlock, CountMax);\r
836 Count = MIN (MAX_UINT16, Count);\r
837 ByteSize = Count * BlockSize;\r
e237e7ae 838\r
839 //\r
50fa1b3a 840 // USB command's upper limit timeout is 5s. [USB2.0-9.2.6.1]\r
e237e7ae 841 //\r
50fa1b3a 842 Timeout = (UINT32) USB_BOOT_GENERAL_CMD_TIMEOUT;\r
e237e7ae 843\r
844 //\r
845 // Fill in the command then execute\r
846 //\r
e59db6a7 847 ZeroMem (&Cmd, sizeof (USB_BOOT_READ_WRITE_10_CMD));\r
e237e7ae 848\r
e59db6a7
RN
849 Cmd.OpCode = Write ? USB_BOOT_WRITE10_OPCODE : USB_BOOT_READ10_OPCODE;\r
850 Cmd.Lun = (UINT8) (USB_BOOT_LUN (UsbMass->Lun));\r
851 WriteUnaligned32 ((UINT32 *) Cmd.Lba, SwapBytes32 (Lba));\r
4f8b2f9d 852 WriteUnaligned16 ((UINT16 *) Cmd.TransferLen, SwapBytes16 ((UINT16)Count));\r
e237e7ae 853\r
854 Status = UsbBootExecCmdWithRetry (\r
855 UsbMass,\r
e59db6a7
RN
856 &Cmd,\r
857 (UINT8) sizeof (USB_BOOT_READ_WRITE_10_CMD),\r
a58a421c 858 Write ? EfiUsbDataOut : EfiUsbDataIn,\r
e237e7ae 859 Buffer,\r
860 ByteSize,\r
861 Timeout\r
862 );\r
863 if (EFI_ERROR (Status)) {\r
864 return Status;\r
865 }\r
e59db6a7
RN
866 DEBUG ((\r
867 DEBUG_BLKIO, "UsbBoot%sBlocks: LBA (0x%lx), Blk (0x%x)\n",\r
868 Write ? L"Write" : L"Read",\r
869 Lba, Count\r
870 ));\r
99c1725e 871 Lba += Count;\r
4f8b2f9d 872 Buffer += ByteSize;\r
99c1725e 873 TotalBlock -= Count;\r
874 }\r
875\r
876 return Status;\r
877}\r
878\r
879/**\r
e59db6a7 880 Read or write some blocks from the device by SCSI 16 byte cmd.\r
99c1725e 881\r
e59db6a7
RN
882 @param UsbMass The USB mass storage device to access\r
883 @param Write TRUE for write operation.\r
99c1725e 884 @param Lba The start block number\r
e59db6a7
RN
885 @param TotalBlock Total block number to read or write\r
886 @param Buffer The buffer to read to or write from\r
99c1725e 887\r
e59db6a7
RN
888 @retval EFI_SUCCESS Data are read into the buffer or writen into the device.\r
889 @retval Others Failed to read or write all the data\r
99c1725e 890**/\r
891EFI_STATUS\r
e59db6a7 892UsbBootReadWriteBlocks16 (\r
99c1725e 893 IN USB_MASS_DEVICE *UsbMass,\r
e59db6a7 894 IN BOOLEAN Write,\r
99c1725e 895 IN UINT64 Lba,\r
896 IN UINTN TotalBlock,\r
e59db6a7 897 IN OUT UINT8 *Buffer\r
99c1725e 898 )\r
899{\r
e59db6a7 900 UINT8 Cmd[16];\r
99c1725e 901 EFI_STATUS Status;\r
4f8b2f9d
RN
902 UINT32 Count;\r
903 UINT32 CountMax;\r
99c1725e 904 UINT32 BlockSize;\r
905 UINT32 ByteSize;\r
906 UINT32 Timeout;\r
907\r
908 BlockSize = UsbMass->BlockIoMedia.BlockSize;\r
4f8b2f9d 909 CountMax = USB_BOOT_MAX_CARRY_SIZE / BlockSize;\r
99c1725e 910 Status = EFI_SUCCESS;\r
911\r
912 while (TotalBlock > 0) {\r
913 //\r
914 // Split the total blocks into smaller pieces.\r
915 //\r
4f8b2f9d
RN
916 Count = (UINT32)MIN (TotalBlock, CountMax);\r
917 ByteSize = Count * BlockSize;\r
99c1725e 918\r
919 //\r
920 // USB command's upper limit timeout is 5s. [USB2.0-9.2.6.1]\r
921 //\r
922 Timeout = (UINT32) USB_BOOT_GENERAL_CMD_TIMEOUT;\r
923\r
924 //\r
925 // Fill in the command then execute\r
926 //\r
e59db6a7 927 ZeroMem (Cmd, sizeof (Cmd));\r
99c1725e 928\r
e59db6a7
RN
929 Cmd[0] = Write ? EFI_SCSI_OP_WRITE16 : EFI_SCSI_OP_READ16;\r
930 Cmd[1] = (UINT8) ((USB_BOOT_LUN (UsbMass->Lun) & 0xE0));\r
931 WriteUnaligned64 ((UINT64 *) &Cmd[2], SwapBytes64 (Lba));\r
932 WriteUnaligned32 ((UINT32 *) &Cmd[10], SwapBytes32 (Count));\r
99c1725e 933\r
934 Status = UsbBootExecCmdWithRetry (\r
935 UsbMass,\r
e59db6a7
RN
936 Cmd,\r
937 (UINT8) sizeof (Cmd),\r
a58a421c 938 Write ? EfiUsbDataOut : EfiUsbDataIn,\r
99c1725e 939 Buffer,\r
940 ByteSize,\r
941 Timeout\r
942 );\r
943 if (EFI_ERROR (Status)) {\r
944 return Status;\r
945 }\r
e59db6a7
RN
946 DEBUG ((\r
947 DEBUG_BLKIO, "UsbBoot%sBlocks16: LBA (0x%lx), Blk (0x%x)\n",\r
948 Write ? L"Write" : L"Read",\r
949 Lba, Count\r
950 ));\r
e237e7ae 951 Lba += Count;\r
4f8b2f9d 952 Buffer += ByteSize;\r
e237e7ae 953 TotalBlock -= Count;\r
954 }\r
955\r
956 return Status;\r
957}\r
958\r
e237e7ae 959/**\r
d80ed2a7 960 Use the USB clear feature control transfer to clear the endpoint stall condition.\r
e237e7ae 961\r
d80ed2a7 962 @param UsbIo The USB I/O Protocol instance\r
e237e7ae 963 @param EndpointAddr The endpoint to clear stall for\r
964\r
d80ed2a7 965 @retval EFI_SUCCESS The endpoint stall condition is cleared.\r
966 @retval Others Failed to clear the endpoint stall condition.\r
e237e7ae 967\r
968**/\r
969EFI_STATUS\r
970UsbClearEndpointStall (\r
971 IN EFI_USB_IO_PROTOCOL *UsbIo,\r
972 IN UINT8 EndpointAddr\r
973 )\r
974{\r
975 EFI_USB_DEVICE_REQUEST Request;\r
976 EFI_STATUS Status;\r
977 UINT32 CmdResult;\r
978 UINT32 Timeout;\r
979\r
980 Request.RequestType = 0x02;\r
981 Request.Request = USB_REQ_CLEAR_FEATURE;\r
982 Request.Value = USB_FEATURE_ENDPOINT_HALT;\r
983 Request.Index = EndpointAddr;\r
984 Request.Length = 0;\r
41e8ff27 985 Timeout = USB_BOOT_GENERAL_CMD_TIMEOUT / USB_MASS_1_MILLISECOND;\r
e237e7ae 986\r
987 Status = UsbIo->UsbControlTransfer (\r
988 UsbIo,\r
989 &Request,\r
990 EfiUsbNoData,\r
991 Timeout,\r
992 NULL,\r
993 0,\r
994 &CmdResult\r
995 );\r
996\r
997 return Status;\r
998}\r