scsidisk.c

来自「EFI BIOS是Intel提出的下一代的BIOS标准。这里上传的Edk源代码是」· C语言 代码 · 共 2,420 行 · 第 1/4 页

C
2,420
字号
  }
  //
  // Get the intrinsic block size
  //
  Media           = ScsiDiskDevice->BlkIo.Media;
  BlockSize       = Media->BlockSize;

  NumberOfBlocks  = BufferSize / BlockSize;

  if (!(Media->MediaPresent)) {
    return EFI_NO_MEDIA;
  }

  if (MediaId != Media->MediaId) {
    return EFI_MEDIA_CHANGED;
  }

  if (BufferSize % BlockSize != 0) {
    return EFI_BAD_BUFFER_SIZE;
  }

  if (LBA > Media->LastBlock) {
    return EFI_INVALID_PARAMETER;
  }

  if ((LBA + NumberOfBlocks - 1) > Media->LastBlock) {
    return EFI_INVALID_PARAMETER;
  }

  if ((Media->IoAlign > 1) && (((UINTN) Buffer & (Media->IoAlign - 1)) != 0)) {
    return EFI_INVALID_PARAMETER;
  }
  //
  // if all the parameters are valid, then perform read sectors command
  // to transfer data from device to host.
  //
  Status = ScsiDiskWriteSectors (ScsiDiskDevice, Buffer, LBA, NumberOfBlocks);

  return Status;
}

EFI_STATUS
EFIAPI
ScsiDiskFlushBlocks (
  IN  EFI_BLOCK_IO_PROTOCOL   *This
  )
/*++

Routine Description:

  Flush Block to Disk

Arguments:

  This  - The pointer of EFI_BLOCK_IO_PROTOCOL

Returns:

  EFI_SUCCESS 

--*/
{
  //
  // return directly
  //
  return EFI_SUCCESS;
}

EFI_STATUS
ScsiDiskDetectMedia (
  SCSI_DISK_DEV   *ScsiDiskDevice,
  BOOLEAN         MustReadCapacity,
  BOOLEAN         *MediaChange
  )
/*++

Routine Description:

  Dectect Device and read out capacity ,if error occurs, parse the sense key.

Arguments:

  ScsiDiskDevice     - The pointer of SCSI_DISK_DEV
  MustReadCapacity   - The flag about reading device capacity
  MediaChange        - The pointer of flag indicates if media has changed 

Returns:

  EFI_DEVICE_ERROR   - Indicates that error occurs
  EFI_SUCCESS        - Successfully to detect media

--*/
{
  EFI_STATUS          Status;
  EFI_STATUS          ReadCapacityStatus;
  EFI_SCSI_SENSE_DATA *SenseData;
  UINTN               NumberOfSenseKeys;
  BOOLEAN             NeedRetry;
  BOOLEAN             NeedReadCapacity;
  UINT8               Index;
  UINT8               MaxRetry;
  EFI_BLOCK_IO_MEDIA  OldMedia;
  UINTN               Action;

  Status              = EFI_SUCCESS;
  ReadCapacityStatus  = EFI_SUCCESS;
  SenseData           = NULL;
  NumberOfSenseKeys   = 0;
  NeedReadCapacity    = FALSE;
  OldMedia            = *(ScsiDiskDevice->BlkIo.Media);
  *MediaChange        = FALSE;
  MaxRetry            = 3;

  for (Index = 0; Index < MaxRetry; Index++) {
    Status = ScsiDiskTestUnitReady (
              ScsiDiskDevice,
              &NeedRetry,
              &SenseData,
              &NumberOfSenseKeys
              );
    if (!EFI_ERROR (Status)) {
      break;
    }

    if (!NeedRetry) {
      return Status;
    }
  }

  if ((Index == MaxRetry) && EFI_ERROR (Status)) {
    return EFI_DEVICE_ERROR;
  }

  Status = DetectMediaParsingSenseKeys (
            ScsiDiskDevice,
            SenseData,
            NumberOfSenseKeys,
            &Action
            );
  if (EFI_ERROR (Status)) {
    return Status;
  }
  //
  // ACTION_NO_ACTION: need not read capacity
  // other action code: need read capacity
  //
  if (Action == ACTION_NO_ACTION) {
    NeedReadCapacity = FALSE;
  } else {
    NeedReadCapacity = TRUE;
  }

  //
  // either NeedReadCapacity is TRUE, or MustReadCapacity is TRUE,
  // retrieve capacity via Read Capacity command
  //
  if (NeedReadCapacity || MustReadCapacity) {
    //
    // retrieve media information
    //
    MaxRetry = 3;
    for (Index = 0; Index < MaxRetry; Index++) {

      ReadCapacityStatus = ScsiDiskReadCapacity (
                            ScsiDiskDevice,
                            &NeedRetry,
                            &SenseData,
                            &NumberOfSenseKeys
                            );
      if (EFI_ERROR (ReadCapacityStatus) && !NeedRetry) {
        return EFI_DEVICE_ERROR;
      }
      //
      // analyze sense key to action
      //
      Status = DetectMediaParsingSenseKeys (
                ScsiDiskDevice,
                SenseData,
                NumberOfSenseKeys,
                &Action
                );
      //
      // if Status is error, it may indicate crisis error,
      // so return without retry.
      //
      if (EFI_ERROR (Status)) {
        return Status;
      }

      switch (Action) {
      case ACTION_NO_ACTION:
        //
        // no retry
        //
        Index = MaxRetry;
        break;

      case ACTION_RETRY_COMMAND_LATER:
        //
        // retry the ReadCapacity later and continuously, until the condition
        // no longer emerges.
        // stall time is 100000us, or say 0.1 second.
        //
        gBS->Stall (100000);
        Index = 0;
        break;

      default:
        //
        // other cases, just retry the command
        //
        break;
      }
    }

    if ((Index == MaxRetry) && EFI_ERROR (ReadCapacityStatus)) {
      return EFI_DEVICE_ERROR;
    }
  }

  if (ScsiDiskDevice->BlkIo.Media->MediaId != OldMedia.MediaId) {
    //
    // Media change information got from the device
    //
    *MediaChange = TRUE;
  }

  if (ScsiDiskDevice->BlkIo.Media->ReadOnly != OldMedia.ReadOnly) {
    *MediaChange = TRUE;
    ScsiDiskDevice->BlkIo.Media->MediaId += 1;
  }

  if (ScsiDiskDevice->BlkIo.Media->BlockSize != OldMedia.BlockSize) {
    *MediaChange = TRUE;
    ScsiDiskDevice->BlkIo.Media->MediaId += 1;
  }

  if (ScsiDiskDevice->BlkIo.Media->LastBlock != OldMedia.LastBlock) {
    *MediaChange = TRUE;
    ScsiDiskDevice->BlkIo.Media->MediaId += 1;
  }

  if (ScsiDiskDevice->BlkIo.Media->MediaPresent != OldMedia.MediaPresent) {
    if (ScsiDiskDevice->BlkIo.Media->MediaPresent) {
      //
      // when change from no media to media present, reset the MediaId to 1.
      //
      ScsiDiskDevice->BlkIo.Media->MediaId = 1;
    } else {
      //
      // when no media, reset the MediaId to zero.
      //
      ScsiDiskDevice->BlkIo.Media->MediaId = 0;
    }

    *MediaChange = TRUE;
  }

  return EFI_SUCCESS;
}

EFI_STATUS
ScsiDiskInquiryDevice (
  SCSI_DISK_DEV   *ScsiDiskDevice,
  BOOLEAN         *NeedRetry
  )
/*++

Routine Description:

  Send out Inquiry command to Device

Arguments:

  ScsiDiskDevice  - The pointer of SCSI_DISK_DEV
  NeedRetry       - Indicates if needs try again when error happens

Returns:

  EFI_DEVICE_ERROR   - Indicates that error occurs
  EFI_SUCCESS        - Successfully to detect media

--*/
{
  UINT32              InquiryDataLength;
  UINT8               SenseDataLength;
  UINT8               HostAdapterStatus;
  UINT8               TargetStatus;
  EFI_SCSI_SENSE_DATA *SenseDataArray;
  UINTN               NumberOfSenseKeys;
  EFI_STATUS          Status;
  UINT8               MaxRetry;
  UINT8               Index;

  InquiryDataLength = sizeof (EFI_SCSI_INQUIRY_DATA);
  SenseDataLength   = 0;

  Status = SubmitInquiryCommand (
            ScsiDiskDevice->ScsiIo,
            EfiScsiStallSeconds (1),
            NULL,
            &SenseDataLength,
            &HostAdapterStatus,
            &TargetStatus,
            (VOID *) &(ScsiDiskDevice->InquiryData),
            &InquiryDataLength,
            FALSE
            );
  switch (Status) {
  //
  // no need to check HostAdapterStatus and TargetStatus
  //
  case EFI_SUCCESS:
  case EFI_WARN_BUFFER_TOO_SMALL:
    ParseInquiryData (ScsiDiskDevice);
    return EFI_SUCCESS;

  case EFI_NOT_READY:
    *NeedRetry = TRUE;
    return EFI_DEVICE_ERROR;

  case EFI_INVALID_PARAMETER:
  case EFI_UNSUPPORTED:
    *NeedRetry = FALSE;
    return EFI_DEVICE_ERROR;

  //
  // go ahead to check HostAdapterStatus and TargetStatus
  // (EFI_TIMEOUT, EFI_DEVICE_ERROR)
  //
  default:
    break;
  }

  Status = CheckHostAdapterStatus (HostAdapterStatus);
  switch (Status) {
  case EFI_SUCCESS:
    break;

  case EFI_TIMEOUT:
  case EFI_NOT_READY:
    *NeedRetry = TRUE;
    return EFI_DEVICE_ERROR;

  case EFI_DEVICE_ERROR:
    //
    // reset the scsi channel
    //
    ScsiDiskDevice->ScsiIo->ResetBus (ScsiDiskDevice->ScsiIo);
    *NeedRetry = FALSE;
    return EFI_DEVICE_ERROR;
  }

  Status = CheckTargetStatus (TargetStatus);
  switch (Status) {
  case EFI_SUCCESS:
    break;

  case EFI_NOT_READY:
    //
    // reset the scsi device
    //
    ScsiDiskDevice->ScsiIo->ResetDevice (ScsiDiskDevice->ScsiIo);
    *NeedRetry = TRUE;
    return EFI_DEVICE_ERROR;

  case EFI_DEVICE_ERROR:
    *NeedRetry = FALSE;
    return EFI_DEVICE_ERROR;
  }
  
  //
  // if goes here, meant SubmitInquiryCommand() failed.
  // if ScsiDiskRequestSenseKeys() succeeds at last,
  // better retry SubmitInquiryCommand(). (by setting *NeedRetry = TRUE)
  //
  MaxRetry = 3;
  for (Index = 0; Index < MaxRetry; Index++) {
    Status = ScsiDiskRequestSenseKeys (
              ScsiDiskDevice,
              NeedRetry,
              &SenseDataArray,
              &NumberOfSenseKeys,
              TRUE
              );
    if (!EFI_ERROR (Status)) {
      *NeedRetry = TRUE;
      return EFI_DEVICE_ERROR;
    }

    if (!*NeedRetry) {
      return EFI_DEVICE_ERROR;
    }
  }
  //
  // ScsiDiskRequestSenseKeys() failed after several rounds of retry.
  // set *NeedRetry = FALSE to avoid the outside caller try again.
  //
  *NeedRetry = FALSE;
  return EFI_DEVICE_ERROR;
}

EFI_STATUS
ScsiDiskTestUnitReady (
  SCSI_DISK_DEV         *ScsiDiskDevice,
  BOOLEAN               *NeedRetry,
  EFI_SCSI_SENSE_DATA   **SenseDataArray,
  UINTN                 *NumberOfSenseKeys
  )
 /*++

Routine Description:

  When Test Unit Ready command succeeds, retrieve Sense Keys via Request Sense;
  When Test Unit Ready command encounters any error caused by host adapter or
  target, return error without retrieving Sense Keys.
  
Arguments:

  ScsiDiskDevice  - The pointer of SCSI_DISK_DEV
  NeedRetry       - The pointer of flag indicates try again
  SenseDataArray  - The pointer of an array of sense data
  NumberOfSenseKeys - The pointer of the number of sense data array
  
Returns:

  EFI_DEVICE_ERROR   - Indicates that error occurs
  EFI_SUCCESS        - Successfully to test unit

--*/
{
  EFI_STATUS  Status;
  UINT8       SenseDataLength;
  UINT8       HostAdapterStatus;
  UINT8       TargetStatus;
  UINT8       Index;
  UINT8       MaxRetry;

  SenseDataLength     = 0;
  *NumberOfSenseKeys  = 0;

  //
  // Parameter 3 and 4: do not require sense data, retrieve it when needed.
  //
  Status = SubmitTestUnitReadyCommand (
            ScsiDiskDevice->ScsiIo,
            EfiScsiStallSeconds (1),
            NULL,
            &SenseDataLength,
            &HostAdapterStatus,
            &TargetStatus
            );
  switch (Status) {
  //
  // no need to check HostAdapterStatus and TargetStatus
  //
  case EFI_NOT_READY:
    *NeedRetry = TRUE;
    return EFI_DEVICE_ERROR;

  case EFI_INVALID_PARAMETER:
  case EFI_UNSUPPORTED:
    *NeedRetry = FALSE;
    return EFI_DEVICE_ERROR;

  //
  // go ahead to check HostAdapterStatus and TargetStatus(in case of EFI_DEVICE_ERROR)
  //
  default:
    break;
  }

  Status = CheckHostAdapterStatus (HostAdapterStatus);
  switch (Status) {
  case EFI_SUCCESS:
    break;

  case EFI_TIMEOUT:
  case EFI_NOT_READY:
    *NeedRetry = TRUE;
    return EFI_DEVICE_ERROR;

  case EFI_DEVICE_ERROR:
    //
    // reset the scsi channel
    //
    ScsiDiskDevice->ScsiIo->ResetBus (ScsiDiskDevice->ScsiIo);
    *NeedRetry = FALSE;
    return EFI_DEVICE_ERROR;
  }

  Status = CheckTargetStatus (TargetStatus);
  switch (Status) {
  case EFI_SUCCESS:
    break;

  case EFI_NOT_READY:
    //
    // reset the scsi device
    //
    ScsiDiskDevice->ScsiIo->ResetDevice (ScsiDiskDevice->ScsiIo);
    *NeedRetry = TRUE;
    return EFI_DEVICE_ERROR;

  case EFI_DEVICE_ERROR:
    *NeedRetry = FALSE;
    return EFI_DEVICE_ERROR;
  }

  MaxRetry = 3;
  for (Index = 0; Index < MaxRetry; Index++) {
    Status = ScsiDiskRequestSenseKeys (
              ScsiDiskDevice,
              NeedRetry,
              SenseDataArray,
              NumberOfSenseKeys,
              FALSE
              );
    if (!EFI_ERROR (Status)) {
      return EFI_SUCCESS;
    }

    if (!*NeedRetry) {
      return EFI_DEVICE_ERROR;
    }
  }
  //
  // ScsiDiskRequestSenseKeys() failed after several rounds of retry.
  // set *NeedRetry = FALSE to avoid the outside caller try again.
  //
  *NeedRetry = FALSE;
  return EFI_DEVICE_ERROR;
}

EFI_STATUS
DetectMediaParsingSenseKeys (
  SCSI_DISK_DEV           *ScsiDiskDevice,
  EFI_SCSI_SENSE_DATA     *SenseData,
  UINTN                   NumberOfSenseKeys,
  UINTN                   *Action
  )
/*++

Routine Description:

  Parsing Sense Keys which got from request sense command.
  
Arguments:

  ScsiDiskDevice    - The pointer of SCSI_DISK_DEV
  SenseData         - The pointer of EFI_SCSI_SENSE_DATA
  NumberOfSenseKeys - The number of sense key  
  Action            - The pointer of action which indicates what is need to do next

Returns:

  EFI_DEVICE_ERROR   - Indicates that error occurs
  EFI_SUCCESS        - Successfully to complete the parsing

--*/
{
  BOOLEAN RetryLater;

  //
  // Default is to read capacity, unless..
  //
  *Action = ACTION_READ_CAPACITY;

  if (NumberOfSenseKeys == 0) {
    *Action = ACTION_NO_ACTION;
    return EFI_SUCCESS;
  }

  if (!ScsiDiskHaveSenseKey (SenseData, NumberOfSenseKeys)) {
    //
    // No Sense Key returned from last submitted command
    //
    *Action = ACTION_NO_ACTION;
    return EFI_SUCCESS;
  }

  if (ScsiDiskIsNoMedia (SenseData, NumberOfSenseKeys)) {
    ScsiDiskDevice->BlkIo.Media->MediaPresent = FALSE;
    ScsiDiskDevice->BlkIo.Media->LastBlock    = 0;
    *Action = ACTION_NO_ACTION;
    return EFI_SUCCESS;
  }

  if (ScsiDiskIsMediaChange (SenseData, NumberOfSenseKeys)) {
    ScsiDiskDevice->BlkIo.Media->MediaId++;
    return EFI_SUCCESS;
  }

  if (ScsiDiskIsMediaError (SenseData, NumberOfSenseKeys)) {
    ScsiDiskDevice->BlkIo.Media->MediaPresent = FALSE;
    ScsiDiskDevice->BlkIo.Media->LastBlock    = 0;
    return EFI_DEVICE_ERROR;
  }

  if (ScsiDiskIsHardwareError (SenseData, NumberOfSenseKeys)) {
    return EFI_DEVICE_ERROR;
  }

  if (!ScsiDiskIsDriveReady (SenseData, NumberOfSenseKeys, &RetryLater)) {
    if (RetryLater) {

⌨️ 快捷键说明

复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?