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