X-Git-Url: https://git.proxmox.com/?a=blobdiff_plain;f=MdeModulePkg%2FBus%2FUsb%2FUsbMassStorageDxe%2FUsbMassBoot.c;h=600896b6a2147c6c5cc17356a842b44347000434;hb=9d510e61fceee7b92955ef9a3c20343752d8ce3f;hp=e830517dd9f95864ee1e45ea8ac2f6eea2851d4e;hpb=19bc85276559ae01996fa0918ec3f6495e7aaa69;p=mirror_edk2.git diff --git a/MdeModulePkg/Bus/Usb/UsbMassStorageDxe/UsbMassBoot.c b/MdeModulePkg/Bus/Usb/UsbMassStorageDxe/UsbMassBoot.c index e830517dd9..600896b6a2 100644 --- a/MdeModulePkg/Bus/Usb/UsbMassStorageDxe/UsbMassBoot.c +++ b/MdeModulePkg/Bus/Usb/UsbMassStorageDxe/UsbMassBoot.c @@ -2,14 +2,8 @@ Implementation of the command set of USB Mass Storage Specification for Bootability, Revision 1.0. -Copyright (c) 2007 - 2011, Intel Corporation. All rights reserved.
-This program and the accompanying materials -are licensed and made available under the terms and conditions of the BSD License -which accompanies this distribution. The full text of the license may be found at -http://opensource.org/licenses/bsd-license.php - -THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, -WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. +Copyright (c) 2007 - 2018, Intel Corporation. All rights reserved.
+SPDX-License-Identifier: BSD-2-Clause-Patent **/ @@ -20,7 +14,7 @@ WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. @param UsbMass The device whose sense data is requested. - @retval EFI_SUCCESS The command is excuted successfully. + @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. @@ -80,7 +74,14 @@ UsbBootRequestSense ( switch (USB_BOOT_SENSE_KEY (SenseData.SenseKey)) { case USB_BOOT_SENSE_NO_SENSE: - Status = EFI_NO_RESPONSE; + 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: @@ -113,6 +114,10 @@ UsbBootRequestSense ( 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; @@ -126,8 +131,9 @@ UsbBootRequestSense ( break; } - DEBUG ((EFI_D_INFO, "UsbBootRequestSense: (%r) with sense key %x/%x/%x\n", + DEBUG ((EFI_D_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 @@ -152,7 +158,7 @@ UsbBootRequestSense ( @param DataLen The length of expected data @param Timeout The timeout used to transfer - @retval EFI_SUCCESS Command is excuted successfully + @retval EFI_SUCCESS Command is executed successfully @retval Others Command execution failed. **/ @@ -185,7 +191,7 @@ UsbBootExecCmd ( ); if (Status == EFI_TIMEOUT) { - DEBUG ((EFI_D_ERROR, "UsbBootExecCmd: Timeout to Exec 0x%x Cmd\n", *(UINT8 *)Cmd)); + DEBUG ((EFI_D_ERROR, "UsbBootExecCmd: %r to Exec 0x%x Cmd\n", Status, *(UINT8 *)Cmd)); return EFI_TIMEOUT; } @@ -200,6 +206,7 @@ UsbBootExecCmd ( // // If command execution failed, then retrieve error info via sense request. // + DEBUG ((EFI_D_ERROR, "UsbBootExecCmd: %r to Exec 0x%x Cmd (Result = %x)\n", Status, *(UINT8 *)Cmd, CmdResult)); return UsbBootRequestSense (UsbMass); } @@ -211,7 +218,7 @@ UsbBootExecCmd ( 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 @@ -221,7 +228,7 @@ UsbBootExecCmd ( @param Timeout The timeout used to transfer @retval EFI_SUCCESS The command is executed successfully. - @retval EFI_MEDIA_CHANGED The device media has been changed. + @retval EFI_NO_MEDIA The device media is removed. @retval Others Command execution failed after retrial. **/ @@ -271,7 +278,7 @@ UsbBootExecCmdWithRetry ( DataLen, Timeout ); - if (Status == EFI_SUCCESS || Status == EFI_MEDIA_CHANGED || Status == EFI_NO_MEDIA) { + if (Status == EFI_SUCCESS || Status == EFI_NO_MEDIA) { break; } // @@ -386,6 +393,87 @@ UsbBootInquiry ( 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 @@ -400,7 +488,7 @@ UsbBootInquiry ( @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 ( @@ -444,15 +532,19 @@ UsbBootReadCapacity ( BlockSize = SwapBytes32 (ReadUnaligned32 ((CONST UINT32 *) CapacityData.BlockLen)); if (BlockSize == 0) { // - // Get sense data + // Get sense data // return UsbBootRequestSense (UsbMass); } else { Media->BlockSize = BlockSize; } - DEBUG ((EFI_D_INFO, "UsbBootReadCapacity Success LBA=%ld BlockSize=%d\n", - Media->LastBlock, Media->BlockSize)); + if (Media->LastBlock == 0xFFFFFFFF) { + Status = UsbBootReadCapacity16 (UsbMass); + if (!EFI_ERROR (Status)) { + UsbMass->Cdb16Byte = TRUE; + } + } return Status; } @@ -534,10 +626,8 @@ UsbBootGetParams ( { EFI_BLOCK_IO_MEDIA *Media; EFI_STATUS Status; - UINT8 CmdSet; Media = &(UsbMass->BlockIoMedia); - CmdSet = ((EFI_USB_INTERFACE_DESCRIPTOR *) (UsbMass->Context))->InterfaceSubClass; Status = UsbBootInquiry (UsbMass); if (EFI_ERROR (Status)) { @@ -545,6 +635,18 @@ UsbBootGetParams ( 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 ((EFI_D_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. @@ -583,7 +685,6 @@ UsbBootDetectMedia ( EFI_BLOCK_IO_MEDIA OldMedia; EFI_BLOCK_IO_MEDIA *Media; UINT8 CmdSet; - EFI_TPL OldTpl; EFI_STATUS Status; Media = &UsbMass->BlockIoMedia; @@ -593,30 +694,49 @@ UsbBootDetectMedia ( CmdSet = ((EFI_USB_INTERFACE_DESCRIPTOR *) (UsbMass->Context))->InterfaceSubClass; Status = UsbBootIsUnitReady (UsbMass); - if (EFI_ERROR (Status) && (Status != EFI_MEDIA_CHANGED)) { - goto ON_ERROR; + if (EFI_ERROR (Status)) { + DEBUG ((EFI_D_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 ((EFI_D_ERROR, "UsbBootDetectMedia: UsbBootReadCapacity (%r)\n", Status)); + } } - if ((UsbMass->Pdt != USB_PDT_CDROM) && (CmdSet == USB_MASS_STORE_SCSI)) { + if (EFI_ERROR (Status) && Status != EFI_NO_MEDIA) { // - // 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. + // For NoMedia, BlockIo is still needed. // - UsbScsiModeSense (UsbMass); + return Status; } - Status = UsbBootReadCapacity (UsbMass); - if (EFI_ERROR (Status)) { - DEBUG ((EFI_D_ERROR, "UsbBootDetectMedia: UsbBootReadCapacity (%r)\n", Status)); - goto ON_ERROR; + // + // 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; } - return EFI_SUCCESS; - -ON_ERROR: // // Detect whether it is necessary to reinstall the Block I/O Protocol. // @@ -632,12 +752,15 @@ ON_ERROR: (Media->LastBlock != OldMedia.LastBlock)) { // - // This function is called by Block I/O Protocol APIs, which run at TPL_NOTIFY. - // Here we temporarily restore TPL to TPL_CALLBACK to invoke ReinstallProtocolInterface(). - // - OldTpl = EfiGetCurrentTpl (); - gBS->RestoreTPL (TPL_CALLBACK); + // 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, @@ -645,11 +768,8 @@ ON_ERROR: &UsbMass->BlockIo ); - ASSERT (EfiGetCurrentTpl () == TPL_CALLBACK); - gBS->RaiseTPL (OldTpl); - // - // Update MediaId after reinstalling Block I/O Protocol. + // Reset MediaId after reinstalling Block I/O Protocol. // if (Media->MediaPresent != OldMedia.MediaPresent) { if (Media->MediaPresent) { @@ -664,6 +784,8 @@ ON_ERROR: (Media->LastBlock != OldMedia.LastBlock)) { Media->MediaId++; } + + Status = Media->MediaPresent ? EFI_MEDIA_CHANGED : EFI_NO_MEDIA; } return Status; @@ -671,33 +793,37 @@ ON_ERROR: /** - Read some blocks from the device. + Read or write some blocks from the device. - @param UsbMass The USB mass storage device to read from + @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 - @param Buffer The buffer to read to + @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 - @retval Others Failed to read all the data + @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 -UsbBootReadBlocks ( +UsbBootReadWriteBlocks ( IN USB_MASS_DEVICE *UsbMass, + IN BOOLEAN Write, IN UINT32 Lba, IN UINTN TotalBlock, - OUT UINT8 *Buffer + IN OUT UINT8 *Buffer ) { - USB_BOOT_READ10_CMD ReadCmd; - EFI_STATUS Status; - UINT16 Count; - UINT32 BlockSize; - UINT32 ByteSize; - UINT32 Timeout; + 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) { @@ -706,8 +832,9 @@ UsbBootReadBlocks ( // 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 = (UINT16)((TotalBlock < USB_BOOT_IO_BLOCKS) ? TotalBlock : USB_BOOT_IO_BLOCKS); - ByteSize = (UINT32)Count * BlockSize; + 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] @@ -717,18 +844,18 @@ UsbBootReadBlocks ( // // Fill in the command then execute // - ZeroMem (&ReadCmd, sizeof (USB_BOOT_READ10_CMD)); + ZeroMem (&Cmd, sizeof (USB_BOOT_READ_WRITE_10_CMD)); - ReadCmd.OpCode = USB_BOOT_READ10_OPCODE; - ReadCmd.Lun = (UINT8) (USB_BOOT_LUN (UsbMass->Lun)); - WriteUnaligned32 ((UINT32 *) ReadCmd.Lba, SwapBytes32 (Lba)); - WriteUnaligned16 ((UINT16 *) ReadCmd.TransferLen, SwapBytes16 (Count)); + 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, - &ReadCmd, - (UINT8) sizeof (USB_BOOT_READ10_CMD), - EfiUsbDataIn, + &Cmd, + (UINT8) sizeof (USB_BOOT_READ_WRITE_10_CMD), + Write ? EfiUsbDataOut : EfiUsbDataIn, Buffer, ByteSize, Timeout @@ -736,54 +863,58 @@ UsbBootReadBlocks ( 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 += Count * BlockSize; + Buffer += ByteSize; TotalBlock -= Count; } return Status; } - /** - Write some blocks to the device. + Read or write some blocks from the device by SCSI 16 byte cmd. - @param UsbMass The USB mass storage device to write to + @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 write - @param Buffer Pointer to the source buffer for the data. - - @retval EFI_SUCCESS Data are written into the buffer - @retval Others Failed to write all the data + @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 -UsbBootWriteBlocks ( - IN USB_MASS_DEVICE *UsbMass, - IN UINT32 Lba, - IN UINTN TotalBlock, - IN UINT8 *Buffer +UsbBootReadWriteBlocks16 ( + IN USB_MASS_DEVICE *UsbMass, + IN BOOLEAN Write, + IN UINT64 Lba, + IN UINTN TotalBlock, + IN OUT UINT8 *Buffer ) { - USB_BOOT_WRITE10_CMD WriteCmd; - EFI_STATUS Status; - UINT16 Count; - UINT32 BlockSize; - UINT32 ByteSize; - UINT32 Timeout; + 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 to ease the pressure - // on the device. We must split the total block because the WRITE10 - // command only has 16 bit transfer length (in the unit of block). + // Split the total blocks into smaller pieces. // - Count = (UINT16)((TotalBlock < USB_BOOT_IO_BLOCKS) ? TotalBlock : USB_BOOT_IO_BLOCKS); - ByteSize = (UINT32)Count * BlockSize; + Count = (UINT32)MIN (TotalBlock, CountMax); + ByteSize = Count * BlockSize; // // USB command's upper limit timeout is 5s. [USB2.0-9.2.6.1] @@ -791,20 +922,20 @@ UsbBootWriteBlocks ( Timeout = (UINT32) USB_BOOT_GENERAL_CMD_TIMEOUT; // - // Fill in the write10 command block + // Fill in the command then execute // - ZeroMem (&WriteCmd, sizeof (USB_BOOT_WRITE10_CMD)); + ZeroMem (Cmd, sizeof (Cmd)); - WriteCmd.OpCode = USB_BOOT_WRITE10_OPCODE; - WriteCmd.Lun = (UINT8) (USB_BOOT_LUN (UsbMass->Lun)); - WriteUnaligned32 ((UINT32 *) WriteCmd.Lba, SwapBytes32 (Lba)); - WriteUnaligned16 ((UINT16 *) WriteCmd.TransferLen, SwapBytes16 (Count)); + 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, - &WriteCmd, - (UINT8) sizeof (USB_BOOT_WRITE10_CMD), - EfiUsbDataOut, + Cmd, + (UINT8) sizeof (Cmd), + Write ? EfiUsbDataOut : EfiUsbDataIn, Buffer, ByteSize, Timeout @@ -812,9 +943,13 @@ UsbBootWriteBlocks ( 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 += Count * BlockSize; + Buffer += ByteSize; TotalBlock -= Count; }