REF: https://bugzilla.tianocore.org/show_bug.cgi?id=3737 Apply uncrustify changes to .c/.h files in the MdeModulePkg package Cc: Andrew Fish <afish@apple.com> Cc: Leif Lindholm <leif@nuviainc.com> Cc: Michael D Kinney <michael.d.kinney@intel.com> Signed-off-by: Michael Kubacki <michael.kubacki@microsoft.com> Reviewed-by: Liming Gao <gaoliming@byosoft.com.cn>
		
			
				
	
	
		
			1006 lines
		
	
	
		
			30 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			1006 lines
		
	
	
		
			30 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
| /** @file
 | |
|   Implementation of the command set of USB Mass Storage Specification
 | |
|   for Bootability, Revision 1.0.
 | |
| 
 | |
| Copyright (c) 2007 - 2018, Intel Corporation. All rights reserved.<BR>
 | |
| SPDX-License-Identifier: BSD-2-Clause-Patent
 | |
| 
 | |
| **/
 | |
| 
 | |
| #include "UsbMass.h"
 | |
| 
 | |
| /**
 | |
|   Execute REQUEST SENSE Command to retrieve sense data from device.
 | |
| 
 | |
|   @param  UsbMass                The device whose sense data is requested.
 | |
| 
 | |
|   @retval EFI_SUCCESS            The command is executed successfully.
 | |
|   @retval EFI_DEVICE_ERROR       Failed to request sense.
 | |
|   @retval EFI_NO_RESPONSE        The device media doesn't response this request.
 | |
|   @retval EFI_INVALID_PARAMETER  The command has some invalid parameters.
 | |
|   @retval EFI_WRITE_PROTECTED    The device is write protected.
 | |
|   @retval EFI_MEDIA_CHANGED      The device media has been changed.
 | |
| 
 | |
| **/
 | |
| EFI_STATUS
 | |
| UsbBootRequestSense (
 | |
|   IN USB_MASS_DEVICE  *UsbMass
 | |
|   )
 | |
| {
 | |
|   USB_BOOT_REQUEST_SENSE_CMD   SenseCmd;
 | |
|   USB_BOOT_REQUEST_SENSE_DATA  SenseData;
 | |
|   EFI_BLOCK_IO_MEDIA           *Media;
 | |
|   USB_MASS_TRANSPORT           *Transport;
 | |
|   EFI_STATUS                   Status;
 | |
|   UINT32                       CmdResult;
 | |
| 
 | |
|   Transport = UsbMass->Transport;
 | |
| 
 | |
|   //
 | |
|   // Request the sense data from the device
 | |
|   //
 | |
|   ZeroMem (&SenseCmd, sizeof (USB_BOOT_REQUEST_SENSE_CMD));
 | |
|   ZeroMem (&SenseData, sizeof (USB_BOOT_REQUEST_SENSE_DATA));
 | |
| 
 | |
|   SenseCmd.OpCode   = USB_BOOT_REQUEST_SENSE_OPCODE;
 | |
|   SenseCmd.Lun      = (UINT8)(USB_BOOT_LUN (UsbMass->Lun));
 | |
|   SenseCmd.AllocLen = (UINT8)sizeof (USB_BOOT_REQUEST_SENSE_DATA);
 | |
| 
 | |
|   Status = Transport->ExecCommand (
 | |
|                         UsbMass->Context,
 | |
|                         &SenseCmd,
 | |
|                         sizeof (USB_BOOT_REQUEST_SENSE_CMD),
 | |
|                         EfiUsbDataIn,
 | |
|                         &SenseData,
 | |
|                         sizeof (USB_BOOT_REQUEST_SENSE_DATA),
 | |
|                         UsbMass->Lun,
 | |
|                         USB_BOOT_GENERAL_CMD_TIMEOUT,
 | |
|                         &CmdResult
 | |
|                         );
 | |
|   if (EFI_ERROR (Status) || (CmdResult != USB_MASS_CMD_SUCCESS)) {
 | |
|     DEBUG ((DEBUG_ERROR, "UsbBootRequestSense: (%r) CmdResult=0x%x\n", Status, CmdResult));
 | |
|     if (!EFI_ERROR (Status)) {
 | |
|       Status = EFI_DEVICE_ERROR;
 | |
|     }
 | |
| 
 | |
|     return Status;
 | |
|   }
 | |
| 
 | |
|   //
 | |
|   // If sense data is retrieved successfully, interpret the sense data
 | |
|   // and update the media status if necessary.
 | |
|   //
 | |
|   Media = &UsbMass->BlockIoMedia;
 | |
| 
 | |
|   switch (USB_BOOT_SENSE_KEY (SenseData.SenseKey)) {
 | |
|     case USB_BOOT_SENSE_NO_SENSE:
 | |
|       if (SenseData.Asc == USB_BOOT_ASC_NO_ADDITIONAL_SENSE_INFORMATION) {
 | |
|         //
 | |
|         // It is not an error if a device does not have additional sense information
 | |
|         //
 | |
|         Status = EFI_SUCCESS;
 | |
|       } else {
 | |
|         Status = EFI_NO_RESPONSE;
 | |
|       }
 | |
| 
 | |
|       break;
 | |
| 
 | |
|     case USB_BOOT_SENSE_RECOVERED:
 | |
|       //
 | |
|       // Suppose hardware can handle this case, and recover later by itself
 | |
|       //
 | |
|       Status = EFI_NOT_READY;
 | |
|       break;
 | |
| 
 | |
|     case USB_BOOT_SENSE_NOT_READY:
 | |
|       Status = EFI_DEVICE_ERROR;
 | |
|       if (SenseData.Asc == USB_BOOT_ASC_NO_MEDIA) {
 | |
|         Media->MediaPresent = FALSE;
 | |
|         Status              = EFI_NO_MEDIA;
 | |
|       } else if (SenseData.Asc == USB_BOOT_ASC_NOT_READY) {
 | |
|         Status = EFI_NOT_READY;
 | |
|       }
 | |
| 
 | |
|       break;
 | |
| 
 | |
|     case USB_BOOT_SENSE_ILLEGAL_REQUEST:
 | |
|       Status = EFI_INVALID_PARAMETER;
 | |
|       break;
 | |
| 
 | |
|     case USB_BOOT_SENSE_UNIT_ATTENTION:
 | |
|       Status = EFI_DEVICE_ERROR;
 | |
|       if (SenseData.Asc == USB_BOOT_ASC_MEDIA_CHANGE) {
 | |
|         //
 | |
|         // If MediaChange, reset ReadOnly and new MediaId
 | |
|         //
 | |
|         Status          = EFI_MEDIA_CHANGED;
 | |
|         Media->ReadOnly = FALSE;
 | |
|         Media->MediaId++;
 | |
|       } else if (SenseData.Asc == USB_BOOT_ASC_NOT_READY) {
 | |
|         Status = EFI_NOT_READY;
 | |
|       } else if (SenseData.Asc == USB_BOOT_ASC_NO_MEDIA) {
 | |
|         Status = EFI_NOT_READY;
 | |
|       }
 | |
| 
 | |
|       break;
 | |
| 
 | |
|     case USB_BOOT_SENSE_DATA_PROTECT:
 | |
|       Status          = EFI_WRITE_PROTECTED;
 | |
|       Media->ReadOnly = TRUE;
 | |
|       break;
 | |
| 
 | |
|     default:
 | |
|       Status = EFI_DEVICE_ERROR;
 | |
|       break;
 | |
|   }
 | |
| 
 | |
|   DEBUG ((
 | |
|     DEBUG_INFO,
 | |
|     "UsbBootRequestSense: (%r) with error code (%x) sense key %x/%x/%x\n",
 | |
|     Status,
 | |
|     SenseData.ErrorCode,
 | |
|     USB_BOOT_SENSE_KEY (SenseData.SenseKey),
 | |
|     SenseData.Asc,
 | |
|     SenseData.Ascq
 | |
|     ));
 | |
| 
 | |
|   return Status;
 | |
| }
 | |
| 
 | |
| /**
 | |
|   Execute the USB mass storage bootability commands.
 | |
| 
 | |
|   This function executes the USB mass storage bootability commands.
 | |
|   If execution failed, retrieve the error by REQUEST_SENSE, then
 | |
|   update the device's status, such as ReadyOnly.
 | |
| 
 | |
|   @param  UsbMass                The device to issue commands to
 | |
|   @param  Cmd                    The command to execute
 | |
|   @param  CmdLen                 The length of the command
 | |
|   @param  DataDir                The direction of data transfer
 | |
|   @param  Data                   The buffer to hold the data
 | |
|   @param  DataLen                The length of expected data
 | |
|   @param  Timeout                The timeout used to transfer
 | |
| 
 | |
|   @retval EFI_SUCCESS            Command is executed successfully
 | |
|   @retval Others                 Command execution failed.
 | |
| 
 | |
| **/
 | |
| 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
 | |
|   )
 | |
| {
 | |
|   USB_MASS_TRANSPORT  *Transport;
 | |
|   EFI_STATUS          Status;
 | |
|   UINT32              CmdResult;
 | |
| 
 | |
|   Transport = UsbMass->Transport;
 | |
|   Status    = Transport->ExecCommand (
 | |
|                            UsbMass->Context,
 | |
|                            Cmd,
 | |
|                            CmdLen,
 | |
|                            DataDir,
 | |
|                            Data,
 | |
|                            DataLen,
 | |
|                            UsbMass->Lun,
 | |
|                            Timeout,
 | |
|                            &CmdResult
 | |
|                            );
 | |
| 
 | |
|   if (Status == EFI_TIMEOUT) {
 | |
|     DEBUG ((DEBUG_ERROR, "UsbBootExecCmd: %r to Exec 0x%x Cmd\n", Status, *(UINT8 *)Cmd));
 | |
|     return EFI_TIMEOUT;
 | |
|   }
 | |
| 
 | |
|   //
 | |
|   // If ExecCommand() returns no error and CmdResult is success,
 | |
|   // then the command transfer is successful.
 | |
|   //
 | |
|   if ((CmdResult == USB_MASS_CMD_SUCCESS) && !EFI_ERROR (Status)) {
 | |
|     return EFI_SUCCESS;
 | |
|   }
 | |
| 
 | |
|   //
 | |
|   // If command execution failed, then retrieve error info via sense request.
 | |
|   //
 | |
|   DEBUG ((DEBUG_ERROR, "UsbBootExecCmd: %r to Exec 0x%x Cmd (Result = %x)\n", Status, *(UINT8 *)Cmd, CmdResult));
 | |
|   return UsbBootRequestSense (UsbMass);
 | |
| }
 | |
| 
 | |
| /**
 | |
|   Execute the USB mass storage bootability commands with retrial.
 | |
| 
 | |
|   This function executes USB mass storage bootability commands.
 | |
|   If the device isn't ready, wait for it. If the device is ready
 | |
|   and error occurs, retry the command again until it exceeds the
 | |
|   limit of retrial times.
 | |
| 
 | |
|   @param  UsbMass                The device to issue commands to
 | |
|   @param  Cmd                    The command to execute
 | |
|   @param  CmdLen                 The length of the command
 | |
|   @param  DataDir                The direction of data transfer
 | |
|   @param  Data                   The buffer to hold the data
 | |
|   @param  DataLen                The length of expected data
 | |
|   @param  Timeout                The timeout used to transfer
 | |
| 
 | |
|   @retval EFI_SUCCESS            The command is executed successfully.
 | |
|   @retval EFI_NO_MEDIA           The device media is removed.
 | |
|   @retval Others                 Command execution failed after retrial.
 | |
| 
 | |
| **/
 | |
| 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
 | |
|   )
 | |
| {
 | |
|   EFI_STATUS  Status;
 | |
|   UINTN       Retry;
 | |
|   EFI_EVENT   TimeoutEvt;
 | |
| 
 | |
|   Retry  = 0;
 | |
|   Status = EFI_SUCCESS;
 | |
|   Status = gBS->CreateEvent (
 | |
|                   EVT_TIMER,
 | |
|                   TPL_CALLBACK,
 | |
|                   NULL,
 | |
|                   NULL,
 | |
|                   &TimeoutEvt
 | |
|                   );
 | |
|   if (EFI_ERROR (Status)) {
 | |
|     return Status;
 | |
|   }
 | |
| 
 | |
|   Status = gBS->SetTimer (TimeoutEvt, TimerRelative, EFI_TIMER_PERIOD_SECONDS (60));
 | |
|   if (EFI_ERROR (Status)) {
 | |
|     goto EXIT;
 | |
|   }
 | |
| 
 | |
|   //
 | |
|   // Execute the cmd and retry if it fails.
 | |
|   //
 | |
|   while (EFI_ERROR (gBS->CheckEvent (TimeoutEvt))) {
 | |
|     Status = UsbBootExecCmd (
 | |
|                UsbMass,
 | |
|                Cmd,
 | |
|                CmdLen,
 | |
|                DataDir,
 | |
|                Data,
 | |
|                DataLen,
 | |
|                Timeout
 | |
|                );
 | |
|     if ((Status == EFI_SUCCESS) || (Status == EFI_NO_MEDIA)) {
 | |
|       break;
 | |
|     }
 | |
| 
 | |
|     //
 | |
|     // If the sense data shows the drive is not ready, we need execute the cmd again.
 | |
|     // We limit the upper boundary to 60 seconds.
 | |
|     //
 | |
|     if (Status == EFI_NOT_READY) {
 | |
|       continue;
 | |
|     }
 | |
| 
 | |
|     //
 | |
|     // If the status is other error, then just retry 5 times.
 | |
|     //
 | |
|     if (Retry++ >= USB_BOOT_COMMAND_RETRY) {
 | |
|       break;
 | |
|     }
 | |
|   }
 | |
| 
 | |
| EXIT:
 | |
|   if (TimeoutEvt != NULL) {
 | |
|     gBS->CloseEvent (TimeoutEvt);
 | |
|   }
 | |
| 
 | |
|   return Status;
 | |
| }
 | |
| 
 | |
| /**
 | |
|   Execute TEST UNIT READY command to check if the device is ready.
 | |
| 
 | |
|   @param  UsbMass                The device to test
 | |
| 
 | |
|   @retval EFI_SUCCESS            The device is ready.
 | |
|   @retval Others                 Device not ready.
 | |
| 
 | |
| **/
 | |
| EFI_STATUS
 | |
| UsbBootIsUnitReady (
 | |
|   IN USB_MASS_DEVICE  *UsbMass
 | |
|   )
 | |
| {
 | |
|   USB_BOOT_TEST_UNIT_READY_CMD  TestCmd;
 | |
| 
 | |
|   ZeroMem (&TestCmd, sizeof (USB_BOOT_TEST_UNIT_READY_CMD));
 | |
| 
 | |
|   TestCmd.OpCode = USB_BOOT_TEST_UNIT_READY_OPCODE;
 | |
|   TestCmd.Lun    = (UINT8)(USB_BOOT_LUN (UsbMass->Lun));
 | |
| 
 | |
|   return UsbBootExecCmdWithRetry (
 | |
|            UsbMass,
 | |
|            &TestCmd,
 | |
|            (UINT8)sizeof (USB_BOOT_TEST_UNIT_READY_CMD),
 | |
|            EfiUsbNoData,
 | |
|            NULL,
 | |
|            0,
 | |
|            USB_BOOT_GENERAL_CMD_TIMEOUT
 | |
|            );
 | |
| }
 | |
| 
 | |
| /**
 | |
|   Execute INQUIRY Command to request information regarding parameters of
 | |
|   the device be sent to the host computer.
 | |
| 
 | |
|   @param  UsbMass                The device to inquire.
 | |
| 
 | |
|   @retval EFI_SUCCESS            INQUIRY Command is executed successfully.
 | |
|   @retval Others                 INQUIRY Command is not executed successfully.
 | |
| 
 | |
| **/
 | |
| EFI_STATUS
 | |
| UsbBootInquiry (
 | |
|   IN USB_MASS_DEVICE  *UsbMass
 | |
|   )
 | |
| {
 | |
|   USB_BOOT_INQUIRY_CMD  InquiryCmd;
 | |
|   EFI_BLOCK_IO_MEDIA    *Media;
 | |
|   EFI_STATUS            Status;
 | |
| 
 | |
|   Media = &(UsbMass->BlockIoMedia);
 | |
| 
 | |
|   ZeroMem (&InquiryCmd, sizeof (USB_BOOT_INQUIRY_CMD));
 | |
|   ZeroMem (&UsbMass->InquiryData, sizeof (USB_BOOT_INQUIRY_DATA));
 | |
| 
 | |
|   InquiryCmd.OpCode   = USB_BOOT_INQUIRY_OPCODE;
 | |
|   InquiryCmd.Lun      = (UINT8)(USB_BOOT_LUN (UsbMass->Lun));
 | |
|   InquiryCmd.AllocLen = (UINT8)sizeof (USB_BOOT_INQUIRY_DATA);
 | |
| 
 | |
|   Status = UsbBootExecCmdWithRetry (
 | |
|              UsbMass,
 | |
|              &InquiryCmd,
 | |
|              (UINT8)sizeof (USB_BOOT_INQUIRY_CMD),
 | |
|              EfiUsbDataIn,
 | |
|              &UsbMass->InquiryData,
 | |
|              sizeof (USB_BOOT_INQUIRY_DATA),
 | |
|              USB_BOOT_GENERAL_CMD_TIMEOUT
 | |
|              );
 | |
|   if (EFI_ERROR (Status)) {
 | |
|     return Status;
 | |
|   }
 | |
| 
 | |
|   //
 | |
|   // Get information from PDT (Peripheral Device Type) field and Removable Medium Bit
 | |
|   // from the inquiry data.
 | |
|   //
 | |
|   UsbMass->Pdt          = (UINT8)(USB_BOOT_PDT (UsbMass->InquiryData.Pdt));
 | |
|   Media->RemovableMedia = (BOOLEAN)(USB_BOOT_REMOVABLE (UsbMass->InquiryData.Removable));
 | |
|   //
 | |
|   // Set block size to the default value of 512 Bytes, in case no media is present at first time.
 | |
|   //
 | |
|   Media->BlockSize = 0x0200;
 | |
| 
 | |
|   return Status;
 | |
| }
 | |
| 
 | |
| /**
 | |
|   Execute READ CAPACITY 16 bytes command to request information regarding
 | |
|   the capacity of the installed medium of the device.
 | |
| 
 | |
|   This function executes READ CAPACITY 16 bytes command to get the capacity
 | |
|   of the USB mass storage media, including the presence, block size,
 | |
|   and last block number.
 | |
| 
 | |
|   @param  UsbMass                The device to retireve disk gemotric.
 | |
| 
 | |
|   @retval EFI_SUCCESS            The disk geometry is successfully retrieved.
 | |
|   @retval EFI_NOT_READY          The returned block size is zero.
 | |
|   @retval Other                  READ CAPACITY 16 bytes command execution failed.
 | |
| 
 | |
| **/
 | |
| EFI_STATUS
 | |
| UsbBootReadCapacity16 (
 | |
|   IN USB_MASS_DEVICE  *UsbMass
 | |
|   )
 | |
| {
 | |
|   UINT8                          CapacityCmd[16];
 | |
|   EFI_SCSI_DISK_CAPACITY_DATA16  CapacityData;
 | |
|   EFI_BLOCK_IO_MEDIA             *Media;
 | |
|   EFI_STATUS                     Status;
 | |
|   UINT32                         BlockSize;
 | |
| 
 | |
|   Media = &UsbMass->BlockIoMedia;
 | |
| 
 | |
|   Media->MediaPresent = FALSE;
 | |
|   Media->LastBlock    = 0;
 | |
|   Media->BlockSize    = 0;
 | |
| 
 | |
|   ZeroMem (CapacityCmd, sizeof (CapacityCmd));
 | |
|   ZeroMem (&CapacityData, sizeof (CapacityData));
 | |
| 
 | |
|   CapacityCmd[0] = EFI_SCSI_OP_READ_CAPACITY16;
 | |
|   CapacityCmd[1] = 0x10;
 | |
|   //
 | |
|   // Partial medium indicator, set the bytes 2 ~ 9 of the Cdb as ZERO.
 | |
|   //
 | |
|   ZeroMem ((CapacityCmd + 2), 8);
 | |
| 
 | |
|   CapacityCmd[13] = sizeof (CapacityData);
 | |
| 
 | |
|   Status = UsbBootExecCmdWithRetry (
 | |
|              UsbMass,
 | |
|              CapacityCmd,
 | |
|              (UINT8)sizeof (CapacityCmd),
 | |
|              EfiUsbDataIn,
 | |
|              &CapacityData,
 | |
|              sizeof (CapacityData),
 | |
|              USB_BOOT_GENERAL_CMD_TIMEOUT
 | |
|              );
 | |
|   if (EFI_ERROR (Status)) {
 | |
|     return Status;
 | |
|   }
 | |
| 
 | |
|   //
 | |
|   // Get the information on media presence, block size, and last block number
 | |
|   // from READ CAPACITY data.
 | |
|   //
 | |
|   Media->MediaPresent = TRUE;
 | |
|   Media->LastBlock    = SwapBytes64 (ReadUnaligned64 ((CONST UINT64 *)&(CapacityData.LastLba7)));
 | |
| 
 | |
|   BlockSize = SwapBytes32 (ReadUnaligned32 ((CONST UINT32 *)&(CapacityData.BlockSize3)));
 | |
| 
 | |
|   Media->LowestAlignedLba = (CapacityData.LowestAlignLogic2 << 8) |
 | |
|                             CapacityData.LowestAlignLogic1;
 | |
|   Media->LogicalBlocksPerPhysicalBlock = (1 << CapacityData.LogicPerPhysical);
 | |
|   if (BlockSize == 0) {
 | |
|     //
 | |
|     //  Get sense data
 | |
|     //
 | |
|     return UsbBootRequestSense (UsbMass);
 | |
|   } else {
 | |
|     Media->BlockSize = BlockSize;
 | |
|   }
 | |
| 
 | |
|   return Status;
 | |
| }
 | |
| 
 | |
| /**
 | |
|   Execute READ CAPACITY command to request information regarding
 | |
|   the capacity of the installed medium of the device.
 | |
| 
 | |
|   This function executes READ CAPACITY command to get the capacity
 | |
|   of the USB mass storage media, including the presence, block size,
 | |
|   and last block number.
 | |
| 
 | |
|   @param  UsbMass                The device to retireve disk gemotric.
 | |
| 
 | |
|   @retval EFI_SUCCESS            The disk geometry is successfully retrieved.
 | |
|   @retval EFI_NOT_READY          The returned block size is zero.
 | |
|   @retval Other                  READ CAPACITY command execution failed.
 | |
| 
 | |
| **/
 | |
| EFI_STATUS
 | |
| UsbBootReadCapacity (
 | |
|   IN USB_MASS_DEVICE  *UsbMass
 | |
|   )
 | |
| {
 | |
|   USB_BOOT_READ_CAPACITY_CMD   CapacityCmd;
 | |
|   USB_BOOT_READ_CAPACITY_DATA  CapacityData;
 | |
|   EFI_BLOCK_IO_MEDIA           *Media;
 | |
|   EFI_STATUS                   Status;
 | |
|   UINT32                       BlockSize;
 | |
| 
 | |
|   Media = &UsbMass->BlockIoMedia;
 | |
| 
 | |
|   ZeroMem (&CapacityCmd, sizeof (USB_BOOT_READ_CAPACITY_CMD));
 | |
|   ZeroMem (&CapacityData, sizeof (USB_BOOT_READ_CAPACITY_DATA));
 | |
| 
 | |
|   CapacityCmd.OpCode = USB_BOOT_READ_CAPACITY_OPCODE;
 | |
|   CapacityCmd.Lun    = (UINT8)(USB_BOOT_LUN (UsbMass->Lun));
 | |
| 
 | |
|   Status = UsbBootExecCmdWithRetry (
 | |
|              UsbMass,
 | |
|              &CapacityCmd,
 | |
|              (UINT8)sizeof (USB_BOOT_READ_CAPACITY_CMD),
 | |
|              EfiUsbDataIn,
 | |
|              &CapacityData,
 | |
|              sizeof (USB_BOOT_READ_CAPACITY_DATA),
 | |
|              USB_BOOT_GENERAL_CMD_TIMEOUT
 | |
|              );
 | |
|   if (EFI_ERROR (Status)) {
 | |
|     return Status;
 | |
|   }
 | |
| 
 | |
|   //
 | |
|   // Get the information on media presence, block size, and last block number
 | |
|   // from READ CAPACITY data.
 | |
|   //
 | |
|   Media->MediaPresent = TRUE;
 | |
|   Media->LastBlock    = SwapBytes32 (ReadUnaligned32 ((CONST UINT32 *)CapacityData.LastLba));
 | |
| 
 | |
|   BlockSize = SwapBytes32 (ReadUnaligned32 ((CONST UINT32 *)CapacityData.BlockLen));
 | |
|   if (BlockSize == 0) {
 | |
|     //
 | |
|     //  Get sense data
 | |
|     //
 | |
|     return UsbBootRequestSense (UsbMass);
 | |
|   } else {
 | |
|     Media->BlockSize = BlockSize;
 | |
|   }
 | |
| 
 | |
|   if (Media->LastBlock == 0xFFFFFFFF) {
 | |
|     Status = UsbBootReadCapacity16 (UsbMass);
 | |
|     if (!EFI_ERROR (Status)) {
 | |
|       UsbMass->Cdb16Byte = TRUE;
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   return Status;
 | |
| }
 | |
| 
 | |
| /**
 | |
|   Retrieves SCSI mode sense information via MODE SENSE(6) command.
 | |
| 
 | |
|   @param  UsbMass                The device whose sense data is requested.
 | |
| 
 | |
|   @retval EFI_SUCCESS            SCSI mode sense information retrieved successfully.
 | |
|   @retval Other                  Command execution failed.
 | |
| 
 | |
| **/
 | |
| EFI_STATUS
 | |
| UsbScsiModeSense (
 | |
|   IN USB_MASS_DEVICE  *UsbMass
 | |
|   )
 | |
| {
 | |
|   EFI_STATUS                        Status;
 | |
|   USB_SCSI_MODE_SENSE6_CMD          ModeSenseCmd;
 | |
|   USB_SCSI_MODE_SENSE6_PARA_HEADER  ModeParaHeader;
 | |
|   EFI_BLOCK_IO_MEDIA                *Media;
 | |
| 
 | |
|   Media = &UsbMass->BlockIoMedia;
 | |
| 
 | |
|   ZeroMem (&ModeSenseCmd, sizeof (USB_SCSI_MODE_SENSE6_CMD));
 | |
|   ZeroMem (&ModeParaHeader, sizeof (USB_SCSI_MODE_SENSE6_PARA_HEADER));
 | |
| 
 | |
|   //
 | |
|   // MODE SENSE(6) command is defined in Section 8.2.10 of SCSI-2 Spec
 | |
|   //
 | |
|   ModeSenseCmd.OpCode      = USB_SCSI_MODE_SENSE6_OPCODE;
 | |
|   ModeSenseCmd.Lun         = (UINT8)USB_BOOT_LUN (UsbMass->Lun);
 | |
|   ModeSenseCmd.PageCode    = 0x3F;
 | |
|   ModeSenseCmd.AllocateLen = (UINT8)sizeof (USB_SCSI_MODE_SENSE6_PARA_HEADER);
 | |
| 
 | |
|   Status = UsbBootExecCmdWithRetry (
 | |
|              UsbMass,
 | |
|              &ModeSenseCmd,
 | |
|              (UINT8)sizeof (USB_SCSI_MODE_SENSE6_CMD),
 | |
|              EfiUsbDataIn,
 | |
|              &ModeParaHeader,
 | |
|              sizeof (USB_SCSI_MODE_SENSE6_PARA_HEADER),
 | |
|              USB_BOOT_GENERAL_CMD_TIMEOUT
 | |
|              );
 | |
| 
 | |
|   //
 | |
|   // Format of device-specific parameter byte of the mode parameter header is defined in
 | |
|   // Section 8.2.10 of SCSI-2 Spec.
 | |
|   // BIT7 of this byte is indicates whether the medium is write protected.
 | |
|   //
 | |
|   if (!EFI_ERROR (Status)) {
 | |
|     Media->ReadOnly = (BOOLEAN)((ModeParaHeader.DevicePara & BIT7) != 0);
 | |
|   }
 | |
| 
 | |
|   return Status;
 | |
| }
 | |
| 
 | |
| /**
 | |
|   Get the parameters for the USB mass storage media.
 | |
| 
 | |
|   This function get the parameters for the USB mass storage media,
 | |
|   It is used both to initialize the media during the Start() phase
 | |
|   of Driver Binding Protocol and to re-initialize it when the media is
 | |
|   changed. Although the RemoveableMedia is unlikely to change,
 | |
|   it is also included here.
 | |
| 
 | |
|   @param  UsbMass                The device to retrieve disk gemotric.
 | |
| 
 | |
|   @retval EFI_SUCCESS            The disk gemotric is successfully retrieved.
 | |
|   @retval Other                  Failed to get the parameters.
 | |
| 
 | |
| **/
 | |
| EFI_STATUS
 | |
| UsbBootGetParams (
 | |
|   IN USB_MASS_DEVICE  *UsbMass
 | |
|   )
 | |
| {
 | |
|   EFI_BLOCK_IO_MEDIA  *Media;
 | |
|   EFI_STATUS          Status;
 | |
| 
 | |
|   Media = &(UsbMass->BlockIoMedia);
 | |
| 
 | |
|   Status = UsbBootInquiry (UsbMass);
 | |
|   if (EFI_ERROR (Status)) {
 | |
|     DEBUG ((DEBUG_ERROR, "UsbBootGetParams: UsbBootInquiry (%r)\n", Status));
 | |
|     return Status;
 | |
|   }
 | |
| 
 | |
|   //
 | |
|   // According to USB Mass Storage Specification for Bootability, only following
 | |
|   // 4 Peripheral Device Types are in spec.
 | |
|   //
 | |
|   if ((UsbMass->Pdt != USB_PDT_DIRECT_ACCESS) &&
 | |
|       (UsbMass->Pdt != USB_PDT_CDROM) &&
 | |
|       (UsbMass->Pdt != USB_PDT_OPTICAL) &&
 | |
|       (UsbMass->Pdt != USB_PDT_SIMPLE_DIRECT))
 | |
|   {
 | |
|     DEBUG ((DEBUG_ERROR, "UsbBootGetParams: Found an unsupported peripheral type[%d]\n", UsbMass->Pdt));
 | |
|     return EFI_UNSUPPORTED;
 | |
|   }
 | |
| 
 | |
|   //
 | |
|   // Don't use the Removable bit in inquiry data to test whether the media
 | |
|   // is removable because many flash disks wrongly set this bit.
 | |
|   //
 | |
|   if ((UsbMass->Pdt == USB_PDT_CDROM) || (UsbMass->Pdt == USB_PDT_OPTICAL)) {
 | |
|     //
 | |
|     // CD-Rom device and Non-CD optical device
 | |
|     //
 | |
|     UsbMass->OpticalStorage = TRUE;
 | |
|     //
 | |
|     // Default value 2048 Bytes, in case no media present at first time
 | |
|     //
 | |
|     Media->BlockSize = 0x0800;
 | |
|   }
 | |
| 
 | |
|   Status = UsbBootDetectMedia (UsbMass);
 | |
| 
 | |
|   return Status;
 | |
| }
 | |
| 
 | |
| /**
 | |
|   Detect whether the removable media is present and whether it has changed.
 | |
| 
 | |
|   @param  UsbMass                The device to check.
 | |
| 
 | |
|   @retval EFI_SUCCESS            The media status is successfully checked.
 | |
|   @retval Other                  Failed to detect media.
 | |
| 
 | |
| **/
 | |
| EFI_STATUS
 | |
| UsbBootDetectMedia (
 | |
|   IN  USB_MASS_DEVICE  *UsbMass
 | |
|   )
 | |
| {
 | |
|   EFI_BLOCK_IO_MEDIA  OldMedia;
 | |
|   EFI_BLOCK_IO_MEDIA  *Media;
 | |
|   UINT8               CmdSet;
 | |
|   EFI_STATUS          Status;
 | |
| 
 | |
|   Media = &UsbMass->BlockIoMedia;
 | |
| 
 | |
|   CopyMem (&OldMedia, &(UsbMass->BlockIoMedia), sizeof (EFI_BLOCK_IO_MEDIA));
 | |
| 
 | |
|   CmdSet = ((EFI_USB_INTERFACE_DESCRIPTOR *)(UsbMass->Context))->InterfaceSubClass;
 | |
| 
 | |
|   Status = UsbBootIsUnitReady (UsbMass);
 | |
|   if (EFI_ERROR (Status)) {
 | |
|     DEBUG ((DEBUG_ERROR, "UsbBootDetectMedia: UsbBootIsUnitReady (%r)\n", Status));
 | |
|   }
 | |
| 
 | |
|   //
 | |
|   // Status could be:
 | |
|   //   EFI_SUCCESS: all good.
 | |
|   //   EFI_NO_MEDIA: media is not present.
 | |
|   //   others: HW error.
 | |
|   // For either EFI_NO_MEDIA, or HW error, skip to get WriteProtected and capacity information.
 | |
|   //
 | |
|   if (!EFI_ERROR (Status)) {
 | |
|     if ((UsbMass->Pdt != USB_PDT_CDROM) && (CmdSet == USB_MASS_STORE_SCSI)) {
 | |
|       //
 | |
|       // MODE SENSE is required for the device with PDT of 0x00/0x07/0x0E,
 | |
|       // according to Section 4 of USB Mass Storage Specification for Bootability.
 | |
|       // MODE SENSE(10) is useless here, while MODE SENSE(6) defined in SCSI
 | |
|       // could get the information of Write Protected.
 | |
|       // Since not all device support this command, skip if fail.
 | |
|       //
 | |
|       UsbScsiModeSense (UsbMass);
 | |
|     }
 | |
| 
 | |
|     Status = UsbBootReadCapacity (UsbMass);
 | |
|     if (EFI_ERROR (Status)) {
 | |
|       DEBUG ((DEBUG_ERROR, "UsbBootDetectMedia: UsbBootReadCapacity (%r)\n", Status));
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   if (EFI_ERROR (Status) && (Status != EFI_NO_MEDIA)) {
 | |
|     //
 | |
|     // For NoMedia, BlockIo is still needed.
 | |
|     //
 | |
|     return Status;
 | |
|   }
 | |
| 
 | |
|   //
 | |
|   // Simply reject device whose block size is unacceptable small (==0) or large (>64K).
 | |
|   //
 | |
|   if ((Media->BlockSize == 0) || (Media->BlockSize > USB_BOOT_MAX_CARRY_SIZE)) {
 | |
|     return EFI_DEVICE_ERROR;
 | |
|   }
 | |
| 
 | |
|   //
 | |
|   // Detect whether it is necessary to reinstall the Block I/O Protocol.
 | |
|   //
 | |
|   // MediaId may change in RequestSense for MediaChanged
 | |
|   // MediaPresent may change in RequestSense for NoMedia
 | |
|   // MediaReadOnly may change in RequestSense for WriteProtected or MediaChanged
 | |
|   // MediaPresent/BlockSize/LastBlock may change in ReadCapacity
 | |
|   //
 | |
|   if ((Media->MediaId != OldMedia.MediaId) ||
 | |
|       (Media->MediaPresent != OldMedia.MediaPresent) ||
 | |
|       (Media->ReadOnly != OldMedia.ReadOnly) ||
 | |
|       (Media->BlockSize != OldMedia.BlockSize) ||
 | |
|       (Media->LastBlock != OldMedia.LastBlock))
 | |
|   {
 | |
|     //
 | |
|     // This function is called from:
 | |
|     //   Block I/O Protocol APIs, which run at TPL_CALLBACK.
 | |
|     //   DriverBindingStart(), which raises to TPL_CALLBACK.
 | |
|     ASSERT (EfiGetCurrentTpl () == TPL_CALLBACK);
 | |
| 
 | |
|     //
 | |
|     // When it is called from DriverBindingStart(), below reinstall fails.
 | |
|     // So ignore the return status check.
 | |
|     //
 | |
|     gBS->ReinstallProtocolInterface (
 | |
|            UsbMass->Controller,
 | |
|            &gEfiBlockIoProtocolGuid,
 | |
|            &UsbMass->BlockIo,
 | |
|            &UsbMass->BlockIo
 | |
|            );
 | |
| 
 | |
|     //
 | |
|     // Reset MediaId after reinstalling Block I/O Protocol.
 | |
|     //
 | |
|     if (Media->MediaPresent != OldMedia.MediaPresent) {
 | |
|       if (Media->MediaPresent) {
 | |
|         Media->MediaId = 1;
 | |
|       } else {
 | |
|         Media->MediaId = 0;
 | |
|       }
 | |
|     }
 | |
| 
 | |
|     if ((Media->ReadOnly != OldMedia.ReadOnly) ||
 | |
|         (Media->BlockSize != OldMedia.BlockSize) ||
 | |
|         (Media->LastBlock != OldMedia.LastBlock))
 | |
|     {
 | |
|       Media->MediaId++;
 | |
|     }
 | |
| 
 | |
|     Status = Media->MediaPresent ? EFI_MEDIA_CHANGED : EFI_NO_MEDIA;
 | |
|   }
 | |
| 
 | |
|   return Status;
 | |
| }
 | |
| 
 | |
| /**
 | |
|   Read or write some blocks from the device.
 | |
| 
 | |
|   @param  UsbMass                The USB mass storage device to access
 | |
|   @param  Write                  TRUE for write operation.
 | |
|   @param  Lba                    The start block number
 | |
|   @param  TotalBlock             Total block number to read or write
 | |
|   @param  Buffer                 The buffer to read to or write from
 | |
| 
 | |
|   @retval EFI_SUCCESS            Data are read into the buffer or writen into the device.
 | |
|   @retval Others                 Failed to read or write all the data
 | |
| 
 | |
| **/
 | |
| EFI_STATUS
 | |
| UsbBootReadWriteBlocks (
 | |
|   IN  USB_MASS_DEVICE  *UsbMass,
 | |
|   IN  BOOLEAN          Write,
 | |
|   IN  UINT32           Lba,
 | |
|   IN  UINTN            TotalBlock,
 | |
|   IN OUT UINT8         *Buffer
 | |
|   )
 | |
| {
 | |
|   USB_BOOT_READ_WRITE_10_CMD  Cmd;
 | |
|   EFI_STATUS                  Status;
 | |
|   UINT32                      Count;
 | |
|   UINT32                      CountMax;
 | |
|   UINT32                      BlockSize;
 | |
|   UINT32                      ByteSize;
 | |
|   UINT32                      Timeout;
 | |
| 
 | |
|   BlockSize = UsbMass->BlockIoMedia.BlockSize;
 | |
|   CountMax  = USB_BOOT_MAX_CARRY_SIZE / BlockSize;
 | |
|   Status    = EFI_SUCCESS;
 | |
| 
 | |
|   while (TotalBlock > 0) {
 | |
|     //
 | |
|     // Split the total blocks into smaller pieces to ease the pressure
 | |
|     // on the device. We must split the total block because the READ10
 | |
|     // command only has 16 bit transfer length (in the unit of block).
 | |
|     //
 | |
|     Count    = (UINT32)MIN (TotalBlock, CountMax);
 | |
|     Count    = MIN (MAX_UINT16, Count);
 | |
|     ByteSize = Count * BlockSize;
 | |
| 
 | |
|     //
 | |
|     // USB command's upper limit timeout is 5s. [USB2.0-9.2.6.1]
 | |
|     //
 | |
|     Timeout = (UINT32)USB_BOOT_GENERAL_CMD_TIMEOUT;
 | |
| 
 | |
|     //
 | |
|     // Fill in the command then execute
 | |
|     //
 | |
|     ZeroMem (&Cmd, sizeof (USB_BOOT_READ_WRITE_10_CMD));
 | |
| 
 | |
|     Cmd.OpCode = Write ? USB_BOOT_WRITE10_OPCODE : USB_BOOT_READ10_OPCODE;
 | |
|     Cmd.Lun    = (UINT8)(USB_BOOT_LUN (UsbMass->Lun));
 | |
|     WriteUnaligned32 ((UINT32 *)Cmd.Lba, SwapBytes32 (Lba));
 | |
|     WriteUnaligned16 ((UINT16 *)Cmd.TransferLen, SwapBytes16 ((UINT16)Count));
 | |
| 
 | |
|     Status = UsbBootExecCmdWithRetry (
 | |
|                UsbMass,
 | |
|                &Cmd,
 | |
|                (UINT8)sizeof (USB_BOOT_READ_WRITE_10_CMD),
 | |
|                Write ? EfiUsbDataOut : EfiUsbDataIn,
 | |
|                Buffer,
 | |
|                ByteSize,
 | |
|                Timeout
 | |
|                );
 | |
|     if (EFI_ERROR (Status)) {
 | |
|       return Status;
 | |
|     }
 | |
| 
 | |
|     DEBUG ((
 | |
|       DEBUG_BLKIO,
 | |
|       "UsbBoot%sBlocks: LBA (0x%lx), Blk (0x%x)\n",
 | |
|       Write ? L"Write" : L"Read",
 | |
|       Lba,
 | |
|       Count
 | |
|       ));
 | |
|     Lba        += Count;
 | |
|     Buffer     += ByteSize;
 | |
|     TotalBlock -= Count;
 | |
|   }
 | |
| 
 | |
|   return Status;
 | |
| }
 | |
| 
 | |
| /**
 | |
|   Read or write some blocks from the device by SCSI 16 byte cmd.
 | |
| 
 | |
|   @param  UsbMass                The USB mass storage device to access
 | |
|   @param  Write                  TRUE for write operation.
 | |
|   @param  Lba                    The start block number
 | |
|   @param  TotalBlock             Total block number to read or write
 | |
|   @param  Buffer                 The buffer to read to or write from
 | |
| 
 | |
|   @retval EFI_SUCCESS            Data are read into the buffer or writen into the device.
 | |
|   @retval Others                 Failed to read or write all the data
 | |
| **/
 | |
| EFI_STATUS
 | |
| UsbBootReadWriteBlocks16 (
 | |
|   IN  USB_MASS_DEVICE  *UsbMass,
 | |
|   IN  BOOLEAN          Write,
 | |
|   IN  UINT64           Lba,
 | |
|   IN  UINTN            TotalBlock,
 | |
|   IN OUT UINT8         *Buffer
 | |
|   )
 | |
| {
 | |
|   UINT8       Cmd[16];
 | |
|   EFI_STATUS  Status;
 | |
|   UINT32      Count;
 | |
|   UINT32      CountMax;
 | |
|   UINT32      BlockSize;
 | |
|   UINT32      ByteSize;
 | |
|   UINT32      Timeout;
 | |
| 
 | |
|   BlockSize = UsbMass->BlockIoMedia.BlockSize;
 | |
|   CountMax  = USB_BOOT_MAX_CARRY_SIZE / BlockSize;
 | |
|   Status    = EFI_SUCCESS;
 | |
| 
 | |
|   while (TotalBlock > 0) {
 | |
|     //
 | |
|     // Split the total blocks into smaller pieces.
 | |
|     //
 | |
|     Count    = (UINT32)MIN (TotalBlock, CountMax);
 | |
|     ByteSize = Count * BlockSize;
 | |
| 
 | |
|     //
 | |
|     // USB command's upper limit timeout is 5s. [USB2.0-9.2.6.1]
 | |
|     //
 | |
|     Timeout = (UINT32)USB_BOOT_GENERAL_CMD_TIMEOUT;
 | |
| 
 | |
|     //
 | |
|     // Fill in the command then execute
 | |
|     //
 | |
|     ZeroMem (Cmd, sizeof (Cmd));
 | |
| 
 | |
|     Cmd[0] = Write ? EFI_SCSI_OP_WRITE16 : EFI_SCSI_OP_READ16;
 | |
|     Cmd[1] = (UINT8)((USB_BOOT_LUN (UsbMass->Lun) & 0xE0));
 | |
|     WriteUnaligned64 ((UINT64 *)&Cmd[2], SwapBytes64 (Lba));
 | |
|     WriteUnaligned32 ((UINT32 *)&Cmd[10], SwapBytes32 (Count));
 | |
| 
 | |
|     Status = UsbBootExecCmdWithRetry (
 | |
|                UsbMass,
 | |
|                Cmd,
 | |
|                (UINT8)sizeof (Cmd),
 | |
|                Write ? EfiUsbDataOut : EfiUsbDataIn,
 | |
|                Buffer,
 | |
|                ByteSize,
 | |
|                Timeout
 | |
|                );
 | |
|     if (EFI_ERROR (Status)) {
 | |
|       return Status;
 | |
|     }
 | |
| 
 | |
|     DEBUG ((
 | |
|       DEBUG_BLKIO,
 | |
|       "UsbBoot%sBlocks16: LBA (0x%lx), Blk (0x%x)\n",
 | |
|       Write ? L"Write" : L"Read",
 | |
|       Lba,
 | |
|       Count
 | |
|       ));
 | |
|     Lba        += Count;
 | |
|     Buffer     += ByteSize;
 | |
|     TotalBlock -= Count;
 | |
|   }
 | |
| 
 | |
|   return Status;
 | |
| }
 | |
| 
 | |
| /**
 | |
|   Use the USB clear feature control transfer to clear the endpoint stall condition.
 | |
| 
 | |
|   @param  UsbIo                  The USB I/O Protocol instance
 | |
|   @param  EndpointAddr           The endpoint to clear stall for
 | |
| 
 | |
|   @retval EFI_SUCCESS            The endpoint stall condition is cleared.
 | |
|   @retval Others                 Failed to clear the endpoint stall condition.
 | |
| 
 | |
| **/
 | |
| EFI_STATUS
 | |
| UsbClearEndpointStall (
 | |
|   IN EFI_USB_IO_PROTOCOL  *UsbIo,
 | |
|   IN UINT8                EndpointAddr
 | |
|   )
 | |
| {
 | |
|   EFI_USB_DEVICE_REQUEST  Request;
 | |
|   EFI_STATUS              Status;
 | |
|   UINT32                  CmdResult;
 | |
|   UINT32                  Timeout;
 | |
| 
 | |
|   Request.RequestType = 0x02;
 | |
|   Request.Request     = USB_REQ_CLEAR_FEATURE;
 | |
|   Request.Value       = USB_FEATURE_ENDPOINT_HALT;
 | |
|   Request.Index       = EndpointAddr;
 | |
|   Request.Length      = 0;
 | |
|   Timeout             = USB_BOOT_GENERAL_CMD_TIMEOUT / USB_MASS_1_MILLISECOND;
 | |
| 
 | |
|   Status = UsbIo->UsbControlTransfer (
 | |
|                     UsbIo,
 | |
|                     &Request,
 | |
|                     EfiUsbNoData,
 | |
|                     Timeout,
 | |
|                     NULL,
 | |
|                     0,
 | |
|                     &CmdResult
 | |
|                     );
 | |
| 
 | |
|   return Status;
 | |
| }
 |