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