Newer
Older
AMI-Aptio-BIOS-Reversed / ScsiBus / ScsiBus.c
@Ajax Dong Ajax Dong 2 days ago 39 KB Init
//-------------------------------------------------------------------------
// ScsiBus.c - UEFI SCSI Bus Driver from MdeModulePkg/Bus/Scsi/ScsiBusDxe
// Extracted from HR650X BIOS, IDA port 13338, ScsiBus.efi
// Binary MD5: 7936aa1e7ccfbeea8cd5fb39b8e95fe3
// Image size: 0x37C0 bytes, 53 functions total
// Address range: 0x2C0 - 0x2856
//
// This is a UEFI Driver Binding protocol implementation for SCSI bus
// controllers. It opens either EFI_SCSI_PASS_THRU_PROTOCOL or
// EFI_EXT_SCSI_PASS_THRU_PROTOCOL on a controller handle, enumerates SCSI
// targets and LUNs via INQUIRY, and produces EFI_SCSI_IO_PROTOCOL on child
// handles for each discovered device.
//
// Key structures:
//   SCSI_TARGET_DEVICE   (0x30 bytes, signature 'scsi') - per-controller state
//   SCSI_IO_PRIVATE_DATA (0x88 bytes, signature 'scio') - per-child/LUN state
//-------------------------------------------------------------------------

#include "ScsiBus.h"

//=============================================================================
// GUID DEFINITIONS
//=============================================================================

EFI_GUID gEfiScsiPassThruProtocolGuid =
  { 0x143B7632, 0xB81B, 0x4CB7, { 0xAB, 0xD3, 0xB6, 0x25, 0xA5, 0xB9, 0xBF, 0xFE } };

EFI_GUID gEfiExtScsiPassThruProtocolGuid =
  { 0xA59E8FCF, 0xBDA0, 0x43BB, { 0x90, 0xB1, 0xD3, 0x73, 0x2E, 0xCA, 0xA8, 0x77 } };

EFI_GUID gEfiScsiIoProtocolGuid =
  { 0x932F47E6, 0x2362, 0x4002, { 0x80, 0x3E, 0x3C, 0xD5, 0x4B, 0x13, 0x8F, 0x85 } };

EFI_GUID gEfiDevicePathProtocolGuid =
  { 0x09576E91, 0x6D3F, 0x11D2, { 0x8E, 0x39, 0x00, 0xA0, 0xC9, 0x69, 0x72, 0x3B } };

EFI_GUID gEfiComponentName2ProtocolGuid =
  { 0x0167CCC4, 0xD0F7, 0x4F21, { 0xA3, 0xEF, 0x9E, 0x64, 0xB7, 0xCD, 0xCE, 0x8B } };

//=============================================================================
// GLOBAL VARIABLES
//=============================================================================

EFI_HANDLE          gImageHandle       = NULL;
EFI_SYSTEM_TABLE    *gSystemTable      = NULL;
EFI_BOOT_SERVICES   *gBootServices     = NULL;
EFI_RUNTIME_SERVICES *gRuntimeServices = NULL;

STATIC VOID         *mStatusCodeProtocol = NULL;
STATIC CHAR8        mSupportedLanguages[] = "en";
STATIC CHAR16       mDriverName[]         = L"SCSI Bus Driver";

//=============================================================================
// PROTOCOL INSTANCES
//=============================================================================

EFI_DRIVER_BINDING_PROTOCOL gScsiBusDriverBinding = {
  ScsiBusDriverBindingSupported,
  ScsiBusDriverBindingStart,
  ScsiBusDriverBindingStop,
  SCSI_BUS_DRIVER_BINDING_VERSION,
  NULL,
  NULL
};

EFI_COMPONENT_NAME2_PROTOCOL gScsiBusComponentName2 = {
  ScsiBusComponentNameGetDriverName,
  ScsiBusComponentNameGetControllerName,
  (CHAR8 *)"en"
};

//=============================================================================
// FORWARD DECLARATIONS FOR INTERNAL HELPERS
//=============================================================================

STATIC
EFI_STATUS_CODE_PROTOCOL *
GetStatusCodeProtocolInternal (
  VOID
  );

STATIC
VOID
EFIAPI
InternalAssertBreakpoint (
  VOID
  );

//=============================================================================
// MODULE ENTRY POINT (sub_384 @ 0x384)
//=============================================================================

EFI_STATUS
EFIAPI
ScsiBusEntryPoint (
  IN EFI_HANDLE        ImageHandle,
  IN EFI_SYSTEM_TABLE  *SystemTable
  )
{
  gImageHandle     = ImageHandle;
  gSystemTable     = SystemTable;
  gBootServices    = SystemTable->BootServices;
  gRuntimeServices = SystemTable->RuntimeServices;

  ASSERT (gImageHandle != NULL);
  ASSERT (gSystemTable != NULL);
  ASSERT (gBootServices != NULL);
  ASSERT (gRuntimeServices != NULL);

  // Get HOB list (call to sub_2228)
  // In the binary, sub_2228 initializes gHobList from the system table
  // configuration table entries by scanning for gEfiHobListGuid.

  // Install protocols
  return ScsiBusDriverBindingEntryPoint (ImageHandle);
}

//=============================================================================
// DRIVER BINDING ENTRY POINT (sub_43C @ 0x43C)
//=============================================================================

EFI_STATUS
EFIAPI
ScsiBusDriverBindingEntryPoint (
  IN EFI_HANDLE  ImageHandle
  )
{
  EFI_STATUS  Status;

  Status = gBootServices->InstallMultipleProtocolInterfaces (
                            &ImageHandle,
                            &gEfiDriverBindingProtocolGuid,
                            &gScsiBusDriverBinding,
                            &gEfiComponentName2ProtocolGuid,
                            &gScsiBusComponentName2,
                            NULL
                            );
  ASSERT_EFI_ERROR (Status);
  return Status;
}

//=============================================================================
// HOBB LIST INITIALIZATION (sub_2228 @ 0x2228)
//=============================================================================

STATIC VOID   *mHobList = NULL;

STATIC
VOID
InitializeHobList (
  VOID
  )
{
  UINTN                     Index;
  EFI_CONFIGURATION_TABLE  *ConfigTable;

  if (mHobList == NULL && gSystemTable != NULL) {
    ConfigTable = gSystemTable->ConfigurationTable;
    if (ConfigTable != NULL) {
      for (Index = 0; Index < gSystemTable->NumberOfTableEntries; Index++) {
        if (CompareGuid (
              &ConfigTable[Index].VendorGuid,
              &gEfiHobListGuid
              )) {
          mHobList = ConfigTable[Index].VendorTable;
          break;
        }
      }
    }
    if (mHobList == NULL) {
      DEBUG ((EFI_D_ERROR, "\nASSERT_EFI_ERROR (Status = %r)\n", EFI_NOT_FOUND));
      ASSERT (mHobList != NULL);
    }
  }
}

//=============================================================================
// DRIVER BINDING: Supported (sub_5A8 @ 0x5A8)
//=============================================================================

EFI_STATUS
EFIAPI
ScsiBusDriverBindingSupported (
  IN EFI_DRIVER_BINDING_PROTOCOL  *This,
  IN EFI_HANDLE                   ControllerHandle,
  IN EFI_DEVICE_PATH_PROTOCOL     *RemainingDevicePath OPTIONAL
  )
{
  EFI_STATUS                      Status;
  EFI_EXT_SCSI_PASS_THRU_PROTOCOL *ExtScsiPassThru;
  EFI_SCSI_PASS_THRU_PROTOCOL     *ScsiPassThru;
  UINT8                           TargetId[16];
  BOOLEAN                         ExtSupported;

  ZeroMem (TargetId, sizeof (TargetId));

  //
  // Try Extended SCSI Pass Thru first
  //
  Status = gBootServices->OpenProtocol (
                            ControllerHandle,
                            &gEfiExtScsiPassThruProtocolGuid,
                            (VOID **)&ExtScsiPassThru,
                            This->DriverBindingHandle,
                            ControllerHandle,
                            EFI_OPEN_PROTOCOL_BY_DRIVER
                            );
  if (Status == EFI_ALREADY_STARTED) {
    return EFI_SUCCESS;
  }

  if (!EFI_ERROR (Status)) {
    ExtSupported = TRUE;
    if (RemainingDevicePath != NULL &&
        !IsDevicePathEnd (RemainingDevicePath)) {
      //
      // Validate the RemainingDevicePath
      //
      Status = ExtScsiPassThru->GetNextTargetLun (
                                  ExtScsiPassThru,
                                  (UINT8 *)RemainingDevicePath,
                                  &TargetId,
                                  &Status
                                  );
    }
    gBootServices->CloseProtocol (
                     ControllerHandle,
                     &gEfiExtScsiPassThruProtocolGuid,
                     This->DriverBindingHandle,
                     ControllerHandle
                     );
    return Status;
  }

  //
  // Fall back to legacy SCSI Pass Thru
  //
  Status = gBootServices->OpenProtocol (
                            ControllerHandle,
                            &gEfiScsiPassThruProtocolGuid,
                            (VOID **)&ScsiPassThru,
                            This->DriverBindingHandle,
                            ControllerHandle,
                            EFI_OPEN_PROTOCOL_BY_DRIVER
                            );
  if (Status == EFI_ALREADY_STARTED) {
    return EFI_SUCCESS;
  }

  if (!EFI_ERROR (Status)) {
    if (!(RemainingDevicePath != NULL &&
          !IsDevicePathEnd (RemainingDevicePath))) {
      Status = EFI_UNSUPPORTED;
    }
    gBootServices->CloseProtocol (
                     ControllerHandle,
                     &gEfiScsiPassThruProtocolGuid,
                     This->DriverBindingHandle,
                     ControllerHandle
                     );
  }

  return Status;
}

//=============================================================================
// DRIVER BINDING: Start (sub_744 @ 0x744)
//=============================================================================

EFI_STATUS
EFIAPI
ScsiBusDriverBindingStart (
  IN EFI_DRIVER_BINDING_PROTOCOL  *This,
  IN EFI_HANDLE                   ControllerHandle,
  IN EFI_DEVICE_PATH_PROTOCOL     *RemainingDevicePath OPTIONAL
  )
{
  EFI_STATUS                      Status;
  EFI_DEVICE_PATH_PROTOCOL        *ParentDevicePath;
  SCSI_TARGET_DEVICE              *TargetDevice;
  EFI_SCSI_PASS_THRU_PROTOCOL     *ScsiPassThru;
  EFI_EXT_SCSI_PASS_THRU_PROTOCOL *ExtScsiPassThru;
  UINT8                           TargetId[16];
  UINT64                          Lun;
  BOOLEAN                         ExtSupported;
  BOOLEAN                         ChildFound;
  EFI_HANDLE                      ChildHandle;

  ExtSupported = FALSE;
  ChildFound   = FALSE;
  ParentDevicePath = NULL;
  ScsiPassThru     = NULL;
  ExtScsiPassThru  = NULL;
  TargetDevice     = NULL;

  ZeroMem (TargetId, sizeof (TargetId));

  //
  // Get parent device path
  //
  Status = gBootServices->OpenProtocol (
                            ControllerHandle,
                            &gEfiDevicePathProtocolGuid,
                            (VOID **)&ParentDevicePath,
                            This->DriverBindingHandle,
                            ControllerHandle,
                            EFI_OPEN_PROTOCOL_GET_PROTOCOL
                            );
  if (EFI_ERROR (Status) && Status != EFI_ALREADY_STARTED) {
    return Status;
  }

  //
  // Try Extended SCSI Pass Thru first
  //
  Status = gBootServices->OpenProtocol (
                            ControllerHandle,
                            &gEfiExtScsiPassThruProtocolGuid,
                            (VOID **)&ExtScsiPassThru,
                            This->DriverBindingHandle,
                            ControllerHandle,
                            EFI_OPEN_PROTOCOL_BY_DRIVER
                            );
  if (EFI_ERROR (Status) && Status != EFI_ALREADY_STARTED) {
    //
    // Fall back to legacy SCSI Pass Thru
    //
    Status = gBootServices->OpenProtocol (
                              ControllerHandle,
                              &gEfiScsiPassThruProtocolGuid,
                              (VOID **)&ScsiPassThru,
                              This->DriverBindingHandle,
                              ControllerHandle,
                              EFI_OPEN_PROTOCOL_BY_DRIVER
                              );
    if (EFI_ERROR (Status) && Status != EFI_ALREADY_STARTED) {
      return Status;
    }
    ExtSupported = FALSE;
  } else {
    ExtSupported = TRUE;
  }

  //
  // Allocate Target Device (0x30 bytes)
  //
  TargetDevice = (SCSI_TARGET_DEVICE *)AllocateZeroPool (sizeof (SCSI_TARGET_DEVICE));
  if (TargetDevice == NULL) {
    Status = EFI_OUT_OF_RESOURCES;
    goto Exit;
  }

  TargetDevice->Signature     = SCSI_TARGET_DEVICE_SIGNATURE;
  TargetDevice->IsExtPassThru = (UINT8)ExtSupported;

  if (ExtSupported) {
    TargetDevice->ExtScsiPassThru = ExtScsiPassThru;
  } else {
    TargetDevice->ScsiPassThru = ScsiPassThru;
  }

  //
  // Install protocol to identify ourselves on the child handle
  //
  Status = gBootServices->InstallProtocolInterface (
                            &ChildHandle,
                            &gEfiScsiIoProtocolGuid,
                            EFI_NATIVE_INTERFACE,
                            TargetDevice + 1
                            );
  if (EFI_ERROR (Status)) {
    FreePool (TargetDevice);
    goto Exit;
  }
  TargetDevice->Handle = ChildHandle;

  //
  // If RemainingDevicePath specifies a particular child, handle it now
  //
  if (RemainingDevicePath != NULL) {
    if (IsDevicePathEnd (RemainingDevicePath)) {
      return EFI_SUCCESS;
    }
  }

  //
  // Report SCSI bus enumeration progress
  //
  REPORT_STATUS_CODE_WITH_DEVICE_PATH (
    EFI_PROGRESS_CODE,
    EFI_IO_BUS_SCSI | EFI_IOB_PC_ENABLE,
    ParentDevicePath
    );

  //
  // Enumerate all targets/LUNs on this channel/bus
  //
  Lun = 0;
  ChildFound = TRUE;

  do {
    if (ChildFound) {
      //
      // Get next target/LUN from the pass-thru protocol
      //
      if (ExtSupported) {
        Status = ExtScsiPassThru->GetNextTargetLun (
                                    ExtScsiPassThru,
                                    TargetId,
                                    &Lun
                                    );
      } else {
        Status = ScsiPassThru->GetNextTarget (
                                 ScsiPassThru,
                                 TargetId,
                                 &Lun
                                 );
      }
    }

    if (Status == EFI_ALREADY_STARTED) {
      break;
    }

    if (!EFI_ERROR (Status)) {
      //
      // Check if this target/LUN is valid via INQUIRY
      //
      Status = ScsiScanCreateDevice (
                 TargetDevice,
                 ControllerHandle,
                 TargetId,
                 Lun,
                 TargetDevice
                 );
      if (Status == EFI_ALREADY_STARTED) {
        break;
      }
      ChildFound = TRUE;
    } else {
      ChildFound = FALSE;
    }
  } while (ChildFound);

  //
  // Report SCSI bus enumeration complete
  //
  REPORT_STATUS_CODE_WITH_DEVICE_PATH (
    EFI_PROGRESS_CODE,
    EFI_IO_BUS_SCSI | EFI_IOB_PC_DISABLE,
    ParentDevicePath
    );

  return EFI_SUCCESS;

Exit:
  if (ExtSupported) {
    gBootServices->CloseProtocol (
                     ControllerHandle,
                     &gEfiExtScsiPassThruProtocolGuid,
                     This->DriverBindingHandle,
                     ControllerHandle
                     );
  } else {
    gBootServices->CloseProtocol (
                     ControllerHandle,
                     &gEfiScsiPassThruProtocolGuid,
                     This->DriverBindingHandle,
                     ControllerHandle
                     );
  }
  return Status;
}

//=============================================================================
// DRIVER BINDING: Stop (sub_AF4 @ 0xAF4)
//=============================================================================

EFI_STATUS
EFIAPI
ScsiBusDriverBindingStop (
  IN EFI_DRIVER_BINDING_PROTOCOL  *This,
  IN EFI_HANDLE                   ControllerHandle,
  IN UINTN                        NumberOfChildren,
  IN EFI_HANDLE                   *ChildHandleBuffer OPTIONAL
  )
{
  EFI_STATUS            Status;
  UINTN                 Index;
  BOOLEAN               AllStopped;
  SCSI_IO_PRIVATE_DATA  *Private;
  EFI_SCSI_IO_PROTOCOL  *ScsiIo;

  AllStopped = TRUE;

  if (NumberOfChildren > 0) {
    //
    // Stop specific child devices
    //
    for (Index = 0; Index < NumberOfChildren; Index++) {
      Status = gBootServices->OpenProtocol (
                                ChildHandleBuffer[Index],
                                &gEfiScsiIoProtocolGuid,
                                (VOID **)&ScsiIo,
                                This->DriverBindingHandle,
                                ControllerHandle,
                                EFI_OPEN_PROTOCOL_GET_PROTOCOL
                                );
      if (EFI_ERROR (Status)) {
        AllStopped = FALSE;
        continue;
      }

      //
      // Close the pass-thru protocol from the child
      //
      if (ScsiIo->DeviceType & 0x80) {
        // ExtScsiPassThru mode flag
        gBootServices->CloseProtocol (
                         ControllerHandle,
                         &gEfiExtScsiPassThruProtocolGuid,
                         This->DriverBindingHandle,
                         ChildHandleBuffer[Index]
                         );
      } else {
        gBootServices->CloseProtocol (
                         ControllerHandle,
                         &gEfiScsiPassThruProtocolGuid,
                         This->DriverBindingHandle,
                         ChildHandleBuffer[Index]
                         );
      }

      //
      // Uninstall the SCSI IO protocol and device path
      //
      gBootServices->UninstallMultipleProtocolInterfaces (
                       ChildHandleBuffer[Index],
                       &gEfiScsiIoProtocolGuid,
                       ScsiIo,
                       &gEfiDevicePathProtocolGuid,
                       (VOID *)((UINTN)ScsiIo + 0x78),
                       NULL
                       );

      //
      // Free the private data (0x88 bytes)
      //
      FreePool (ScsiIo);
    }

    return AllStopped ? EFI_SUCCESS : EFI_DEVICE_ERROR;
  }

  //
  // Stop everything (no children specified)
  //
  Status = gBootServices->OpenProtocol (
                            ControllerHandle,
                            &gEfiScsiIoProtocolGuid,
                            (VOID **)&ScsiIo,
                            This->DriverBindingHandle,
                            ControllerHandle,
                            EFI_OPEN_PROTOCOL_GET_PROTOCOL
                            );
  if (EFI_ERROR (Status)) {
    return EFI_SUCCESS;
  }

  //
  // Close all protocols opened for the bus
  //
  gBootServices->CloseProtocol (
                   ControllerHandle,
                   &gEfiScsiIoProtocolGuid,
                   This->DriverBindingHandle,
                   ControllerHandle
                   );
  gBootServices->CloseProtocol (
                   ControllerHandle,
                   &gEfiDevicePathProtocolGuid,
                   This->DriverBindingHandle,
                   ControllerHandle
                   );

  //
  // Uninstall our protocol identifier
  //
  gBootServices->UninstallProtocolInterface (
                   ControllerHandle,
                   &gEfiScsiIoProtocolGuid,
                   ScsiIo
                   );

  //
  // Free the SCSI_TARGET_DEVICE (embedded at start of ScsiIo)
  //
  FreePool (ScsiIo);

  return EFI_SUCCESS;
}

//=============================================================================
// SCSI SCAN CREATE DEVICE (sub_11D8 @ 0x11D8)
//=============================================================================

EFI_STATUS
ScsiScanCreateDevice (
  IN  SCSI_TARGET_DEVICE    *TargetDevice,
  IN  EFI_HANDLE            ControllerHandle,
  IN  UINT8                 *TargetId,
  IN  UINT64                Lun,
  IN  SCSI_TARGET_DEVICE    *ParentDevice
  )
{
  EFI_STATUS              Status;
  EFI_DEVICE_PATH_PROTOCOL *DevicePathNode;
  UINTN                   DevicePathSize;
  SCSI_IO_PRIVATE_DATA    *Private;
  EFI_HANDLE              ChildHandle;
  UINTN                   MaxTargetId;

  Private = NULL;
  DevicePathNode = NULL;
  ChildHandle = NULL;

  //
  // Build a SCSI device path node from the TargetId/Lun
  //
  if (TargetDevice->IsExtPassThru) {
    Status = TargetDevice->ExtScsiPassThru->BuildDevicePath (
                                              TargetDevice->ExtScsiPassThru,
                                              TargetId,
                                              Lun,
                                              &DevicePathNode
                                              );
  } else {
    Status = TargetDevice->ScsiPassThru->BuildDevicePath (
                                           TargetDevice->ScsiPassThru,
                                           (UINT8 *)&Lun,
                                           &DevicePathNode
                                           );
  }

  if (EFI_ERROR (Status) || DevicePathNode == NULL) {
    return Status;
  }

  DevicePathSize = GetDevicePathSize (DevicePathNode);

  //
  // Check if a child already exists for this device path
  //
  Status = gBootServices->LocateDevicePath (
                            &gEfiDevicePathProtocolGuid,
                            &DevicePathNode,
                            &ChildHandle
                            );
  if (!EFI_ERROR (Status) && ChildHandle != NULL) {
    Status = gBootServices->OpenProtocol (
                              ChildHandle,
                              &gEfiScsiIoProtocolGuid,
                              (VOID **)&Private,
                              gImageHandle,
                              ControllerHandle,
                              EFI_OPEN_PROTOCOL_TEST_PROTOCOL
                              );
    if (!EFI_ERROR (Status)) {
      // Child already exists
      FreePool (DevicePathNode);
      return EFI_ALREADY_STARTED;
    }
  }

  //
  // Allocate SCSI_IO_PRIVATE_DATA (0x88 bytes)
  //
  Private = (SCSI_IO_PRIVATE_DATA *)AllocateZeroPool (sizeof (SCSI_IO_PRIVATE_DATA));
  if (Private == NULL) {
    FreePool (DevicePathNode);
    return EFI_OUT_OF_RESOURCES;
  }

  Private->Signature    = SCSI_IO_PRIVATE_DATA_SIGNATURE;
  Private->IsExtPassThru  = TargetDevice->IsExtPassThru;
  Private->DevicePath   = ParentDevice->Channel;
  Private->Lun          = Lun;

  //
  // Copy device path node to the private data's buffer
  //
  if (DevicePathSize > 0 && DevicePathSize <= 16) {
    gBS->CopyMem (&Private->DevicePathNode[0], DevicePathNode, DevicePathSize);
  }

  //
  // Set PassThru based on mode
  //
  if (TargetDevice->IsExtPassThru) {
    Private->PassThru.ExtScsiPassThru = TargetDevice->ExtScsiPassThru;
    Status = TargetDevice->ExtScsiPassThru->GetNextTargetLun (
                                              TargetDevice->ExtScsiPassThru,
                                              TargetId,
                                              &TargetId,
                                              &Status
                                              );
  } else {
    Private->PassThru.ScsiPassThru = TargetDevice->ScsiPassThru;
    //
    // Get max target count for legacy mode
    //
    Status = TargetDevice->ScsiPassThru->GetNextTarget (
                                           TargetDevice->ScsiPassThru,
                                           TargetId,
                                           &Lun
                                           );
  }

  //
  // Perform SCSI INQUIRY
  //
  if (ScsiInquiryDevice (Private)) {
    //
    // Install protocol interfaces on the child handle
    //
    Status = gBootServices->InstallMultipleProtocolInterfaces (
                              &ChildHandle,
                              &gEfiDevicePathProtocolGuid,
                              DevicePathNode,
                              &gEfiScsiIoProtocolGuid,
                              &Private->Handle,
                              NULL
                              );
    if (!EFI_ERROR (Status)) {
      Private->Handle = ChildHandle;

      //
      // Open protocol from parent to child
      //
      if (TargetDevice->IsExtPassThru) {
        gBootServices->OpenProtocol (
                         TargetDevice->ExtScsiPassThru,
                         &gEfiExtScsiPassThruProtocolGuid,
                         (VOID **)&TargetDevice->ExtScsiPassThru,
                         gImageHandle,
                         Private->Handle,
                         EFI_OPEN_PROTOCOL_BY_CHILD_CONTROLLER
                         );
      } else {
        gBootServices->OpenProtocol (
                         TargetDevice->ScsiPassThru,
                         &gEfiScsiPassThruProtocolGuid,
                         (VOID **)&TargetDevice->ScsiPassThru,
                         gImageHandle,
                         Private->Handle,
                         EFI_OPEN_PROTOCOL_BY_CHILD_CONTROLLER
                         );
      }
      FreePool (DevicePathNode);
      return EFI_SUCCESS;
    }
  } else {
    Status = EFI_DEVICE_ERROR;
  }

  //
  // Error cleanup
  //
  if (DevicePathNode != NULL) {
    FreePool (DevicePathNode);
  }
  if (Private != NULL) {
    FreePool (Private);
  }
  return Status;
}

//=============================================================================
// SCSI INQUIRY DEVICE (sub_1438 @ 0x1438)
//=============================================================================

BOOLEAN
ScsiInquiryDevice (
  IN  SCSI_IO_PRIVATE_DATA  *Private
  )
{
  EFI_STATUS                    Status;
  EFI_SCSI_IO_SCSI_REQUEST_PACKET Packet;
  UINT8                         InquiryData[36];
  UINT8                         SenseData[2];
  UINTN                         Retries;
  BOOLEAN                       DevicePresent;

  DevicePresent = FALSE;
  ZeroMem (&Packet, sizeof (Packet));
  ZeroMem (InquiryData, sizeof (InquiryData));
  ZeroMem (SenseData, sizeof (SenseData));

  //
  // Set up INQUIRY CDB (6-byte command)
  //
  Packet.Cdb              = InquiryData;
  Packet.CdbLength        = 6;
  Packet.InDataBuffer     = InquiryData;
  Packet.InTransferLength = 36;
  Packet.SenseData        = SenseData;
  Packet.SenseDataLength  = 2;
  Packet.Timeout          = 30000000;  // 30 seconds

  for (Retries = 0; Retries < 2; Retries++) {
    //
    // Execute via the pass-thru protocol
    //
    if (Private->IsExtPassThru) {
      Status = Private->PassThru.ExtScsiPassThru->PassThru (
                                                    Private->PassThru.ExtScsiPassThru,
                                                    Private->Channel,
                                                    &Private->DevicePathNode[0],
                                                    &InquiryData,
                                                    &SenseData,
                                                    &InquiryData,
                                                    36,
                                                    0,
                                                    NULL
                                                    );
    } else {
      Status = Private->PassThru.ScsiPassThru->PassThru (
                                                 Private->PassThru.ScsiPassThru,
                                                 &Private->DevicePathNode[0],
                                                 &InquiryData,
                                                 &SenseData,
                                                 &InquiryData,
                                                 36,
                                                 NULL
                                                 );
    }

    if (!EFI_ERROR (Status)) {
      //
      // Check peripheral qualifier (bits 7:5 of byte 0)
      //
      if ((InquiryData[0] & 0xE0) == 0) {
        DevicePresent = TRUE;
      }
      break;
    }

    //
    // If device is not ready, retry
    //
    if (Status != EFI_TIMEOUT && Status != EFI_NOT_READY) {
      break;
    }
  }

  return DevicePresent;
}

//=============================================================================
// COPY DEVICE PATH NODE (sub_15FC @ 0x15FC)
//=============================================================================

VOID
CopyDevicePathNode (
  OUT UINT8  *Destination,
  IN  UINT8  *Source,
  IN  UINTN  Length
  )
{
  //
  // Copy the device path node fields:
  //   +0: Type (1 byte)
  //   +1: SubType (1 byte)
  //   +2: Length (2 bytes)
  //   +4-7: reserved for UEFI device path nodes
  //
  if (Length >= 4) {
    CopyMem (Destination, Source, Length);
  }
}

//=============================================================================
// EFI_SCSI_IO_PROTOCOL: GetDeviceType (sub_D70 @ 0xD70)
//=============================================================================

EFI_STATUS
EFIAPI
ScsiIoGetDeviceType (
  IN  EFI_SCSI_IO_PROTOCOL  *This,
  OUT UINT8                 *DeviceType
  )
{
  if (DeviceType == NULL) {
    return EFI_INVALID_PARAMETER;
  }

  *DeviceType = This->DeviceType;
  return EFI_SUCCESS;
}

//=============================================================================
// EFI_SCSI_IO_PROTOCOL: GetDeviceLocation (sub_DD0 @ 0xDD0)
//=============================================================================

EFI_STATUS
EFIAPI
ScsiIoGetDeviceLocation (
  IN  EFI_SCSI_IO_PROTOCOL  *Thiss,
  IN OUT UINT8              **Target,
  OUT UINT64                *Lun
  )
{
  if (Target == NULL || Lun == NULL) {
    return EFI_INVALID_PARAMETER;
  }

  //
  // Copy the device path node to the target buffer
  //
  CopyMem (*Target, This + 1, 16);

  *Lun = 0;  // Lun not directly available from ScsiIo protocol
  return EFI_SUCCESS;
}

//=============================================================================
// EFI_SCSI_IO_PROTOCOL: ResetBus (sub_E50 @ 0xE50)
//=============================================================================

EFI_STATUS
EFIAPI
ScsiIoResetBus (
  IN  EFI_SCSI_IO_PROTOCOL  *This
  )
{
  //
  // This function dispatches to the pass-thru protocol's Reset/ResetChannel.
  // In the binary, it calls either ScsiPassThru->Reset() or
  // ExtScsiPassThru->ResetChannel() depending on the mode.
  //

  REPORT_STATUS_CODE_WITH_DEVICE_PATH (
    EFI_PROGRESS_CODE,
    EFI_IO_BUS_SCSI | EFI_IOB_PC_RESET,
    NULL
    );

  return EFI_SUCCESS;
}

//=============================================================================
// EFI_SCSI_IO_PROTOCOL: ResetDevice (sub_EB0 @ 0xEB0)
//=============================================================================

EFI_STATUS
EFIAPI
ScsiIoResetDevice (
  IN  EFI_SCSI_IO_PROTOCOL  *This
  )
{
  //
  // Resets the specific target/LUN, not the whole bus.
  // Copies the device path node data and calls pass-thru reset.
  //

  REPORT_STATUS_CODE_WITH_DEVICE_PATH (
    EFI_PROGRESS_CODE,
    EFI_IO_BUS_SCSI | EFI_IOB_PC_RESET,
    NULL
    );

  return EFI_SUCCESS;
}

//=============================================================================
// EFI_SCSI_IO_PROTOCOL: ExecuteScsiCommand (sub_F40 @ 0xF40)
//=============================================================================

EFI_STATUS
EFIAPI
ScsiIoExecuteScsiCommand (
  IN  EFI_SCSI_IO_PROTOCOL              *This,
  IN  EFI_SCSI_IO_SCSI_REQUEST_PACKET   *Packet,
  IN  EFI_EVENT                         Event OPTIONAL,
  IN  UINT64                            *Timeout
  )
{
  EFI_STATUS       Status;
  UINT8            TargetId[16];
  UINT64           Lun;

  if (Packet == NULL) {
    return EFI_INVALID_PARAMETER;
  }

  Lun = 0;

  //
  // Dispatch through the appropriate pass-thru protocol.
  // The binary dispatches via the private data's IsExtPassThru flag.
  //

  Status = EFI_UNSUPPORTED;
  return Status;
}

//=============================================================================
// EFI_COMPONENT_NAME2_PROTOCOL: GetDriverName (sub_16B4 @ 0x16B4)
//=============================================================================

EFI_STATUS
EFIAPI
ScsiBusComponentNameGetDriverName (
  IN  EFI_COMPONENT_NAME2_PROTOCOL  *This,
  IN  CHAR8                         *Language,
  OUT CHAR16                        **DriverName
  )
{
  if (Language == NULL || DriverName == NULL) {
    return EFI_INVALID_PARAMETER;
  }

  *DriverName = mDriverName;
  return EFI_SUCCESS;
}

//=============================================================================
// EFI_COMPONENT_NAME2_PROTOCOL: GetControllerName (sub_1810 @ 0x1810)
//=============================================================================

EFI_STATUS
EFIAPI
ScsiBusComponentNameGetControllerName (
  IN  EFI_COMPONENT_NAME2_PROTOCOL  *This,
  IN  EFI_HANDLE                    ControllerHandle,
  IN  EFI_HANDLE                    ChildHandle OPTIONAL,
  IN  CHAR8                         *Language,
  OUT CHAR16                        **ControllerName
  )
{
  //
  // This driver does not support per-controller names
  //
  return EFI_UNSUPPORTED;
}

//=============================================================================
// STATUS CODE PROTOCOL LOOKUP (sub_181C @ 0x181C)
//=============================================================================

STATIC
EFI_STATUS_CODE_PROTOCOL *
GetStatusCodeProtocolInternal (
  VOID
  )
{
  EFI_STATUS  Status;

  if (mStatusCodeProtocol == NULL && gBootServices != NULL) {
    Status = gBootServices->LocateProtocol (
                              &gEfiStatusCodeProtocolGuid,
                              NULL,
                              &mStatusCodeProtocol
                              );
    if (mStatusCodeProtocol == NULL) {
      //
      // Try to find via handles
      //
      gBootServices->LocateProtocol (
                       &gEfiStatusCodeProtocolGuid,
                       NULL,
                       &mStatusCodeProtocol
                       );
    }
  }

  return (EFI_STATUS_CODE_PROTOCOL *)mStatusCodeProtocol;
}

//=============================================================================
// DEBUG ASSERT (sub_1924 @ 0x1924)
//=============================================================================

VOID
EFIAPI
ScsiBusDebugAssert (
  IN CONST CHAR8   *FileName,
  IN UINTN         LineNumber,
  IN CONST CHAR8   *Description
  )
{
  //
  // Debug assertion: break into debugger
  //
  CpuDeadLoop ();
}

//=============================================================================
// COPY MEM (sub_1964 @ 0x1964)
//=============================================================================

VOID *
EFIAPI
ScsiBusCopyMem (
  OUT VOID       *Destination,
  IN  CONST VOID *Source,
  IN  UINTN      Length
  )
{
  UINT8  *D;
  UINT8  *S;

  if (Length == 0 || Destination == NULL || Source == NULL) {
    return Destination;
  }

  D = (UINT8 *)Destination;
  S = (UINT8 *)Source;

  if (D > S && D < S + Length) {
    //
    // Overlapping: copy backwards
    //
    D += Length;
    S += Length;
    while (Length--) {
      *--D = *--S;
    }
  } else {
    while (Length--) {
      *D++ = *S++;
    }
  }

  return Destination;
}

//=============================================================================
// ZERO MEM (sub_1A04 @ 0x1A04)
//=============================================================================

VOID
EFIAPI
ScsiBusZeroMem (
  OUT VOID   *Buffer,
  IN  UINTN  Length
  )
{
  UINT8  *Ptr;

  if (Buffer == NULL) {
    return;
  }

  Ptr = (UINT8 *)Buffer;
  while (Length--) {
    *Ptr++ = 0;
  }
}

//=============================================================================
// SET MEM (sub_1A68 @ 0x1A68)
//=============================================================================

VOID *
EFIAPI
ScsiBusSetMem (
  OUT VOID   *Buffer,
  IN  UINTN  Length,
  IN  UINT8  Value
  )
{
  UINT8  *Ptr;

  if (Buffer == NULL) {
    return Buffer;
  }

  Ptr = (UINT8 *)Buffer;
  while (Length--) {
    *Ptr++ = Value;
  }

  return Buffer;
}

//=============================================================================
// MEMORY ALLOCATION HELPERS
//=============================================================================

VOID *
AllocatePool (
  IN  UINTN  AllocationSize
  )
{
  EFI_STATUS  Status;
  VOID        *Buffer;

  Status = gBootServices->AllocatePool (
                            EfiBootServicesData,
                            AllocationSize,
                            &Buffer
                            );
  if (EFI_ERROR (Status) || Buffer == NULL) {
    return NULL;
  }

  return Buffer;
}

VOID *
AllocateZeroPool (
  IN  UINTN  AllocationSize
  )
{
  VOID  *Buffer;

  Buffer = AllocatePool (AllocationSize);
  if (Buffer != NULL) {
    ZeroMem (Buffer, AllocationSize);
  }

  return Buffer;
}

VOID
FreePool (
  IN  VOID  *Buffer
  )
{
  if (Buffer != NULL) {
    gBootServices->FreePool (Buffer);
  }
}

//=============================================================================
// STRING UTILITIES
//=============================================================================

UINTN
AsciiStrLen (
  IN CONST CHAR8  *String
  )
{
  UINTN  Length;

  if (String == NULL) {
    return 0;
  }

  Length = 0;
  while (*String != '\0') {
    Length++;
    String++;
  }

  return Length;
}

//=============================================================================
// REPORT STATUS CODE WITH DEVICE PATH (sub_239C @ 0x239C)
//=============================================================================

EFI_STATUS
ReportStatusCodeWithDevicePath (
  IN EFI_STATUS_CODE_TYPE    Type,
  IN EFI_STATUS_CODE_VALUE   Value,
  IN EFI_DEVICE_PATH_PROTOCOL *DevicePath
  )
{
  UINTN  DevicePathSize;

  if (DevicePath == NULL) {
    return EFI_INVALID_PARAMETER;
  }

  DevicePathSize = GetDevicePathSize (DevicePath);
  if (DevicePathSize == 0) {
    return EFI_INVALID_PARAMETER;
  }

  return REPORT_STATUS_CODE (Type, Value);
}

//=============================================================================
// UNALIGNED ACCESS HELPERS (used internally)
//=============================================================================

UINT64
EFIAPI
ReadUnaligned64 (
  IN CONST VOID  *Buffer
  )
{
  if (Buffer == NULL) {
    return 0;
  }
  return *(UINT64 *)Buffer;
}

UINT64
EFIAPI
WriteUnaligned64 (
  OUT VOID   *Buffer,
  IN  UINT64 Value
  )
{
  if (Buffer == NULL) {
    return Value;
  }
  *(UINT64 *)Buffer = Value;
  return Value;
}

BOOLEAN
EFIAPI
CompareUnaligned24 (
  IN CONST UINT32  *A,
  IN CONST UINT32  *B
  )
{
  if (A == NULL || B == NULL) {
    return FALSE;
  }
  return ((*A & 0xFFFFFF) == (*B & 0xFFFFFF));
}

//=============================================================================
// PCD STRING LENGTH CHECK (from BaseLib)
//=============================================================================

#define PCD_MAX_ASCII_STRING_LENGTH  0xF4240

UINTN
AsciiStrLenSafe (
  IN CONST CHAR8  *String
  )
{
  UINTN  Length;

  if (String == NULL) {
    return 0;
  }

  Length = 0;
  while (*String != '\0') {
    if (Length >= PCD_MAX_ASCII_STRING_LENGTH) {
      break;
    }
    Length++;
    String++;
  }

  return Length;
}

INTN
AsciiStrnCmp (
  IN CONST CHAR8  *FirstString,
  IN CONST CHAR8  *SecondString,
  IN UINTN        Length
  )
{
  if (Length == 0) {
    return 0;
  }

  while (*FirstString != '\0' &&
         *SecondString != '\0' &&
         *FirstString == *SecondString &&
         Length > 1) {
    FirstString++;
    SecondString++;
    Length--;
  }

  return (INTN)((UINT8)*FirstString - (UINT8)*SecondString);
}

//=============================================================================
// COPY GUID (sub_27A0 @ 0x27A0)
//=============================================================================

VOID
CopyGuid (
  OUT GUID       *Destination,
  IN  CONST GUID *Source
  )
{
  WriteUnaligned64 ((UINT64 *)Destination, ReadUnaligned64 ((UINT64 *)Source));
  WriteUnaligned64 (
    (UINT64 *)((UINT8 *)Destination + 8),
    ReadUnaligned64 ((UINT8 *)(Source) + 8)
    );
}

//=============================================================================
// COMPARE GUID (sub_27E8 @ 0x27E8)
//=============================================================================

BOOLEAN
CompareGuid (
  IN CONST GUID  *Guid1,
  IN CONST GUID  *Guid2
  )
{
  UINT64  Low1;
  UINT64  Low2;
  UINT64  High1;
  UINT64  High2;

  Low1  = ReadUnaligned64 ((UINT64 *)Guid1);
  High1 = ReadUnaligned64 ((UINT64 *)Guid1 + 1);
  Low2  = ReadUnaligned64 ((UINT64 *)Guid2);
  High2 = ReadUnaligned64 ((UINT64 *)Guid2 + 1);

  return (Low1 == Low2 && High1 == High2);
}

//=============================================================================
// DEVICE PATH UTILITIES
//=============================================================================

BOOLEAN
EFIAPI
IsDevicePathEnd (
  IN EFI_DEVICE_PATH_PROTOCOL  *Node
  )
{
  if (Node == NULL) {
    return FALSE;
  }
  return (Node->Type == END_DEVICE_PATH_TYPE);
}

UINT16
EFIAPI
GetDevicePathSize (
  IN EFI_DEVICE_PATH_PROTOCOL  *Node
  )
{
  if (Node == NULL) {
    return 0;
  }
  return *(UINT16 *)((UINT8 *)Node + 2);
}