MdeModulePkg: ScsiDiskDxe: Query Write Protected State

Currently, only SCSI attached CDROMs are marked as read only
SCSI devices. Write failures have been seen to occur when a
read only disk is attached as a SCSI device, because the device
is never queried for its write protected state and the disk is
listed as writeable.

This adds a new mode sense query to determine if a SCSI disk is
write protected. If so, the proper BlockIO media property is set
to read only, to ensure that all layers know this is a read only
disk.

Signed-off-by: Oliver Smith-Denny <osde@microsoft.com>
This commit is contained in:
Mayank Kumar
2025-10-09 07:39:20 -07:00
committed by mergify[bot]
parent d428ca6fe2
commit c9eb3717b4

View File

@ -196,6 +196,65 @@ ScsiDiskDriverBindingSupported (
return Status;
}
/**
Check whether the SCSI disk is write protected.
@param[in] ScsiDiskDevice The SCSI disk device.
@param[out] WriteProtectionEnabled A pointer to a Boolean that will be set to TRUE if the disk is write protected,
FALSE otherwise.
@retval EFI_SUCCESS The operation completed successfully.
@retval other An error occurred while executing the SCSI command.
*/
STATIC
EFI_STATUS
IsWriteProtected (
IN OUT SCSI_DISK_DEV *ScsiDiskDevice,
OUT BOOLEAN *WriteProtectionEnabled
)
{
EFI_STATUS Status;
EFI_SCSI_IO_SCSI_REQUEST_PACKET CommandPacket;
UINT8 Cdb[6];
UINT8 DataBuffer[64];
if ((ScsiDiskDevice == NULL) || (ScsiDiskDevice->ScsiIo == NULL) || (WriteProtectionEnabled == NULL)) {
return EFI_INVALID_PARAMETER;
}
//
// Initialize SCSI REQUEST_PACKET and 6-byte Cdb
//
ZeroMem (&CommandPacket, sizeof (CommandPacket));
ZeroMem (Cdb, sizeof (Cdb));
// Initialize output parameter to default value
*WriteProtectionEnabled = FALSE;
Cdb[0] = ATA_CMD_MODE_SENSE6;
Cdb[1] = BIT3; // Setting the bit for Disable Block Descriptor
Cdb[2] = ATA_PAGE_CODE_RETURN_ALL_PAGES;
Cdb[4] = sizeof (DataBuffer);
CommandPacket.Timeout = SCSI_DISK_TIMEOUT;
CommandPacket.Cdb = Cdb;
CommandPacket.CdbLength = (UINT8)sizeof (Cdb);
CommandPacket.InDataBuffer = &DataBuffer;
CommandPacket.InTransferLength = sizeof (DataBuffer);
Status = ScsiDiskDevice->ScsiIo->ExecuteScsiCommand (ScsiDiskDevice->ScsiIo, &CommandPacket, NULL);
if (EFI_ERROR (Status)) {
return Status;
}
// Mode Sense 6 Byte Command returns the Write Protection status in the 3rd byte
// Bit 7 of the 3rd byte indicates the Write Protection status
// See SCSI Block Commands - 3 section 6.3.1 and SCSI-2 Spec, 8.3.3.
*WriteProtectionEnabled = (DataBuffer[2] & BIT7) != 0;
return EFI_SUCCESS;
}
/**
Start this driver on ControllerHandle.
@ -234,6 +293,7 @@ ScsiDiskDriverBindingStart (
CHAR8 VendorStr[VENDOR_IDENTIFICATION_LENGTH + 1];
CHAR8 ProductStr[PRODUCT_IDENTIFICATION_LENGTH + 1];
CHAR16 DeviceStr[VENDOR_IDENTIFICATION_LENGTH + PRODUCT_IDENTIFICATION_LENGTH + 2];
BOOLEAN WriteProtectionEnabled = FALSE;
MustReadCapacity = TRUE;
@ -297,6 +357,17 @@ ScsiDiskDriverBindingStart (
break;
}
if (ScsiDiskDevice->DeviceType == EFI_SCSI_TYPE_DISK) {
Status = IsWriteProtected (ScsiDiskDevice, &WriteProtectionEnabled);
if (EFI_ERROR (Status)) {
DEBUG ((DEBUG_ERROR, "ScsiDisk: IsWriteProtected() fails. Status = %r\n", Status));
}
if (WriteProtectionEnabled) {
ScsiDiskDevice->BlkIo.Media->ReadOnly = TRUE;
}
}
//
// The Sense Data Array's initial size is 6
//