summaryrefslogtreecommitdiffstats
path: root/src/VBox/Devices/EFI/Firmware/MdeModulePkg/Bus/Ata/AhciPei/AhciPeiPassThru.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/VBox/Devices/EFI/Firmware/MdeModulePkg/Bus/Ata/AhciPei/AhciPeiPassThru.c')
-rw-r--r--src/VBox/Devices/EFI/Firmware/MdeModulePkg/Bus/Ata/AhciPei/AhciPeiPassThru.c514
1 files changed, 514 insertions, 0 deletions
diff --git a/src/VBox/Devices/EFI/Firmware/MdeModulePkg/Bus/Ata/AhciPei/AhciPeiPassThru.c b/src/VBox/Devices/EFI/Firmware/MdeModulePkg/Bus/Ata/AhciPei/AhciPeiPassThru.c
new file mode 100644
index 00000000..ef153bdf
--- /dev/null
+++ b/src/VBox/Devices/EFI/Firmware/MdeModulePkg/Bus/Ata/AhciPei/AhciPeiPassThru.c
@@ -0,0 +1,514 @@
+/** @file
+ The AhciPei driver is used to manage ATA hard disk device working under AHCI
+ mode at PEI phase.
+
+ Copyright (c) 2019, Intel Corporation. All rights reserved.<BR>
+
+ SPDX-License-Identifier: BSD-2-Clause-Patent
+
+**/
+
+#include "AhciPei.h"
+
+/**
+ Traverse the attached ATA devices list to find out the device with given Port
+ and PortMultiplierPort.
+
+ @param[in] Private A pointer to the PEI_AHCI_CONTROLLER_PRIVATE_DATA
+ instance.
+ @param[in] Port The port number of the ATA device.
+ @param[in] PortMultiplierPort The port multiplier port number of the ATA device.
+
+ @retval The pointer to the PEI_AHCI_ATA_DEVICE_DATA structure of the device
+ info to access.
+
+**/
+PEI_AHCI_ATA_DEVICE_DATA *
+SearchDeviceByPort (
+ IN PEI_AHCI_CONTROLLER_PRIVATE_DATA *Private,
+ IN UINT16 Port,
+ IN UINT16 PortMultiplierPort
+ )
+{
+ PEI_AHCI_ATA_DEVICE_DATA *DeviceData;
+ LIST_ENTRY *Node;
+
+ Node = GetFirstNode (&Private->DeviceList);
+ while (!IsNull (&Private->DeviceList, Node)) {
+ DeviceData = AHCI_PEI_ATA_DEVICE_INFO_FROM_THIS (Node);
+
+ if ((DeviceData->Port == Port) &&
+ (DeviceData->PortMultiplier == PortMultiplierPort)) {
+ return DeviceData;
+ }
+
+ Node = GetNextNode (&Private->DeviceList, Node);
+ }
+
+ return NULL;
+}
+
+/**
+ Sends an ATA command to an ATA device that is attached to the ATA controller.
+
+ @param[in] Private Pointer to the PEI_AHCI_CONTROLLER_PRIVATE_DATA.
+ @param[in] Port The port number of the ATA device.
+ @param[in] PortMultiplierPort The port multiplier port number of the ATA
+ device.
+ @param[in] FisIndex The index of the FIS.
+ @param[in,out] Packet A pointer to the ATA command to send to
+ the ATA device specified by Port and
+ PortMultiplierPort.
+
+ @retval EFI_SUCCESS The ATA command was sent by the host. For
+ bi-directional commands, InTransferLength bytes
+ were transferred from InDataBuffer. For write
+ and bi-directional commands, OutTransferLength
+ bytes were transferred by OutDataBuffer.
+ @retval EFI_BAD_BUFFER_SIZE The ATA command was not executed. The number
+ of bytes that could be transferred is returned
+ in InTransferLength. For write and bi-directional
+ commands, OutTransferLength bytes were transferred
+ by OutDataBuffer.
+ @retval EFI_NOT_READY The ATA command could not be sent because there
+ are too many ATA commands already queued. The
+ caller may retry again later.
+ @retval EFI_DEVICE_ERROR A device error occurred while attempting to
+ send the ATA command.
+ @retval EFI_INVALID_PARAMETER Port, PortMultiplierPort, or the contents of
+ Acb are invalid. The ATA command was not sent,
+ so no additional status information is available.
+
+**/
+EFI_STATUS
+AhciPassThruExecute (
+ IN PEI_AHCI_CONTROLLER_PRIVATE_DATA *Private,
+ IN UINT16 Port,
+ IN UINT16 PortMultiplierPort,
+ IN UINT8 FisIndex,
+ IN OUT EFI_ATA_PASS_THRU_COMMAND_PACKET *Packet
+ )
+{
+ EFI_STATUS Status;
+
+ switch (Packet->Protocol) {
+ case EFI_ATA_PASS_THRU_PROTOCOL_ATA_NON_DATA:
+ Status = AhciNonDataTransfer (
+ Private,
+ (UINT8) Port,
+ (UINT8) PortMultiplierPort,
+ FisIndex,
+ Packet->Acb,
+ Packet->Asb,
+ Packet->Timeout
+ );
+ break;
+ case EFI_ATA_PASS_THRU_PROTOCOL_PIO_DATA_IN:
+ Status = AhciPioTransfer (
+ Private,
+ (UINT8) Port,
+ (UINT8) PortMultiplierPort,
+ FisIndex,
+ TRUE,
+ Packet->Acb,
+ Packet->Asb,
+ Packet->InDataBuffer,
+ Packet->InTransferLength,
+ Packet->Timeout
+ );
+ break;
+ case EFI_ATA_PASS_THRU_PROTOCOL_PIO_DATA_OUT:
+ Status = AhciPioTransfer (
+ Private,
+ (UINT8) Port,
+ (UINT8) PortMultiplierPort,
+ FisIndex,
+ FALSE,
+ Packet->Acb,
+ Packet->Asb,
+ Packet->OutDataBuffer,
+ Packet->OutTransferLength,
+ Packet->Timeout
+ );
+ break;
+ default:
+ return EFI_UNSUPPORTED;
+ }
+
+ return Status;
+}
+
+/**
+ Sends an ATA command to an ATA device that is attached to the ATA controller.
+
+ @param[in] This The PPI instance pointer.
+ @param[in] Port The port number of the ATA device to send
+ the command.
+ @param[in] PortMultiplierPort The port multiplier port number of the ATA
+ device to send the command.
+ If there is no port multiplier, then specify
+ 0xFFFF.
+ @param[in,out] Packet A pointer to the ATA command to send to
+ the ATA device specified by Port and
+ PortMultiplierPort.
+
+ @retval EFI_SUCCESS The ATA command was sent by the host. For
+ bi-directional commands, InTransferLength bytes
+ were transferred from InDataBuffer. For write
+ and bi-directional commands, OutTransferLength
+ bytes were transferred by OutDataBuffer.
+ @retval EFI_NOT_FOUND The specified ATA device is not found.
+ @retval EFI_INVALID_PARAMETER The contents of Acb are invalid. The ATA command
+ was not sent, so no additional status information
+ is available.
+ @retval EFI_BAD_BUFFER_SIZE The ATA command was not executed. The number
+ of bytes that could be transferred is returned
+ in InTransferLength. For write and bi-directional
+ commands, OutTransferLength bytes were transferred
+ by OutDataBuffer.
+ @retval EFI_NOT_READY The ATA command could not be sent because there
+ are too many ATA commands already queued. The
+ caller may retry again later.
+ @retval EFI_DEVICE_ERROR A device error occurred while attempting to
+ send the ATA command.
+
+**/
+EFI_STATUS
+EFIAPI
+AhciAtaPassThruPassThru (
+ IN EDKII_PEI_ATA_PASS_THRU_PPI *This,
+ IN UINT16 Port,
+ IN UINT16 PortMultiplierPort,
+ IN OUT EFI_ATA_PASS_THRU_COMMAND_PACKET *Packet
+ )
+{
+ UINT32 IoAlign;
+ PEI_AHCI_CONTROLLER_PRIVATE_DATA *Private;
+ PEI_AHCI_ATA_DEVICE_DATA *DeviceData;
+ UINT32 MaxSectorCount;
+ UINT32 BlockSize;
+
+ if (This == NULL || Packet == NULL) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ IoAlign = This->Mode->IoAlign;
+ if ((IoAlign > 1) && !IS_ALIGNED (Packet->InDataBuffer, IoAlign)) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ if ((IoAlign > 1) && !IS_ALIGNED (Packet->OutDataBuffer, IoAlign)) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ if ((IoAlign > 1) && !IS_ALIGNED (Packet->Asb, IoAlign)) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ Private = GET_AHCI_PEIM_HC_PRIVATE_DATA_FROM_THIS_PASS_THRU (This);
+ DeviceData = SearchDeviceByPort (Private, Port, PortMultiplierPort);
+ if (DeviceData == NULL) {
+ return EFI_NOT_FOUND;
+ }
+
+ MaxSectorCount = mMaxTransferBlockNumber[DeviceData->Lba48Bit];
+ BlockSize = DeviceData->Media.BlockSize;
+
+ //
+ // Convert the transfer length from sector count to byte.
+ //
+ if (((Packet->Length & EFI_ATA_PASS_THRU_LENGTH_BYTES) == 0) &&
+ (Packet->InTransferLength != 0)) {
+ Packet->InTransferLength = Packet->InTransferLength * BlockSize;
+ }
+
+ //
+ // Convert the transfer length from sector count to byte.
+ //
+ if (((Packet->Length & EFI_ATA_PASS_THRU_LENGTH_BYTES) == 0) &&
+ (Packet->OutTransferLength != 0)) {
+ Packet->OutTransferLength = Packet->OutTransferLength * BlockSize;
+ }
+
+ //
+ // If the data buffer described by InDataBuffer/OutDataBuffer and
+ // InTransferLength/OutTransferLength is too big to be transferred in a single
+ // command, then no data is transferred and EFI_BAD_BUFFER_SIZE is returned.
+ //
+ if (((Packet->InTransferLength != 0) && (Packet->InTransferLength > MaxSectorCount * BlockSize)) ||
+ ((Packet->OutTransferLength != 0) && (Packet->OutTransferLength > MaxSectorCount * BlockSize))) {
+ return EFI_BAD_BUFFER_SIZE;
+ }
+
+ return AhciPassThruExecute (
+ Private,
+ DeviceData->Port,
+ DeviceData->PortMultiplier,
+ DeviceData->FisIndex,
+ Packet
+ );
+}
+
+/**
+ Used to retrieve the list of legal port numbers for ATA devices on an ATA controller.
+ These can either be the list of ports where ATA devices are actually present or the
+ list of legal port numbers for the ATA controller. Regardless, the caller of this
+ function must probe the port number returned to see if an ATA device is actually
+ present at that location on the ATA controller.
+
+ The GetNextPort() function retrieves the port number on an ATA controller. If on
+ input Port is 0xFFFF, then the port number of the first port on the ATA controller
+ is returned in Port and EFI_SUCCESS is returned.
+
+ If Port is a port number that was returned on a previous call to GetNextPort(),
+ then the port number of the next port on the ATA controller is returned in Port,
+ and EFI_SUCCESS is returned. If Port is not 0xFFFF and Port was not returned on
+ a previous call to GetNextPort(), then EFI_INVALID_PARAMETER is returned.
+
+ If Port is the port number of the last port on the ATA controller, then EFI_NOT_FOUND
+ is returned.
+
+ @param[in] This The PPI instance pointer.
+ @param[in,out] Port On input, a pointer to the port number on the ATA controller.
+ On output, a pointer to the next port number on the ATA
+ controller. An input value of 0xFFFF retrieves the first
+ port number on the ATA controller.
+
+ @retval EFI_SUCCESS The next port number on the ATA controller was
+ returned in Port.
+ @retval EFI_NOT_FOUND There are no more ports on this ATA controller.
+ @retval EFI_INVALID_PARAMETER Port is not 0xFFFF and Port was not returned
+ on a previous call to GetNextPort().
+
+**/
+EFI_STATUS
+EFIAPI
+AhciAtaPassThruGetNextPort (
+ IN EDKII_PEI_ATA_PASS_THRU_PPI *This,
+ IN OUT UINT16 *Port
+ )
+{
+ PEI_AHCI_CONTROLLER_PRIVATE_DATA *Private;
+ PEI_AHCI_ATA_DEVICE_DATA *DeviceData;
+ LIST_ENTRY *Node;
+
+ if (This == NULL || Port == NULL) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ Private = GET_AHCI_PEIM_HC_PRIVATE_DATA_FROM_THIS_PASS_THRU (This);
+
+ if (*Port == 0xFFFF) {
+ //
+ // If the Port is all 0xFF's, start to traverse the device list from the
+ // beginning.
+ //
+ Node = GetFirstNode (&Private->DeviceList);
+ if (!IsNull (&Private->DeviceList, Node)) {
+ DeviceData = AHCI_PEI_ATA_DEVICE_INFO_FROM_THIS (Node);
+
+ *Port = DeviceData->Port;
+ goto Exit;
+ }
+
+ return EFI_NOT_FOUND;
+ } else if (*Port == Private->PreviousPort) {
+ Node = GetFirstNode (&Private->DeviceList);
+
+ while (!IsNull (&Private->DeviceList, Node)) {
+ DeviceData = AHCI_PEI_ATA_DEVICE_INFO_FROM_THIS (Node);
+
+ if (DeviceData->Port > *Port){
+ *Port = DeviceData->Port;
+ goto Exit;
+ }
+
+ Node = GetNextNode (&Private->DeviceList, Node);
+ }
+
+ return EFI_NOT_FOUND;
+ } else {
+ //
+ // Port is not equal to all 0xFF's and not equal to previous return value.
+ //
+ return EFI_INVALID_PARAMETER;
+ }
+
+Exit:
+ //
+ // Update the PreviousPort.
+ //
+ Private->PreviousPort = *Port;
+
+ return EFI_SUCCESS;
+}
+
+/**
+ Used to retrieve the list of legal port multiplier port numbers for ATA devices
+ on a port of an ATA controller. These can either be the list of port multiplier
+ ports where ATA devices are actually present on port or the list of legal port
+ multiplier ports on that port. Regardless, the caller of this function must probe
+ the port number and port multiplier port number returned to see if an ATA device
+ is actually present.
+
+ The GetNextDevice() function retrieves the port multiplier port number of an ATA
+ device present on a port of an ATA controller.
+
+ If PortMultiplierPort points to a port multiplier port number value that was
+ returned on a previous call to GetNextDevice(), then the port multiplier port
+ number of the next ATA device on the port of the ATA controller is returned in
+ PortMultiplierPort, and EFI_SUCCESS is returned.
+
+ If PortMultiplierPort points to 0xFFFF, then the port multiplier port number
+ of the first ATA device on port of the ATA controller is returned in PortMultiplierPort
+ and EFI_SUCCESS is returned.
+
+ If PortMultiplierPort is not 0xFFFF and the value pointed to by PortMultiplierPort
+ was not returned on a previous call to GetNextDevice(), then EFI_INVALID_PARAMETER
+ is returned.
+
+ If PortMultiplierPort is the port multiplier port number of the last ATA device
+ on the port of the ATA controller, then EFI_NOT_FOUND is returned.
+
+ @param[in] This The PPI instance pointer.
+ @param[in] Port The port number present on the ATA controller.
+ @param[in,out] PortMultiplierPort On input, a pointer to the port multiplier
+ port number of an ATA device present on the
+ ATA controller. If on input a PortMultiplierPort
+ of 0xFFFF is specified, then the port multiplier
+ port number of the first ATA device is returned.
+ On output, a pointer to the port multiplier port
+ number of the next ATA device present on an ATA
+ controller.
+
+ @retval EFI_SUCCESS The port multiplier port number of the next ATA
+ device on the port of the ATA controller was
+ returned in PortMultiplierPort.
+ @retval EFI_NOT_FOUND There are no more ATA devices on this port of
+ the ATA controller.
+ @retval EFI_INVALID_PARAMETER PortMultiplierPort is not 0xFFFF, and PortMultiplierPort
+ was not returned on a previous call to GetNextDevice().
+
+**/
+EFI_STATUS
+EFIAPI
+AhciAtaPassThruGetNextDevice (
+ IN EDKII_PEI_ATA_PASS_THRU_PPI *This,
+ IN UINT16 Port,
+ IN OUT UINT16 *PortMultiplierPort
+ )
+{
+ PEI_AHCI_CONTROLLER_PRIVATE_DATA *Private;
+ PEI_AHCI_ATA_DEVICE_DATA *DeviceData;
+ LIST_ENTRY *Node;
+
+ if (This == NULL || PortMultiplierPort == NULL) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ Private = GET_AHCI_PEIM_HC_PRIVATE_DATA_FROM_THIS_PASS_THRU (This);
+
+ if (Private->PreviousPortMultiplier == 0xFFFF) {
+ //
+ // If a device is directly attached on a port, previous call to this
+ // function will return the value 0xFFFF for PortMultiplierPort. In
+ // this case, there should be no more device on the port multiplier.
+ //
+ Private->PreviousPortMultiplier = 0;
+ return EFI_NOT_FOUND;
+ }
+
+ if (*PortMultiplierPort == Private->PreviousPortMultiplier) {
+ Node = GetFirstNode (&Private->DeviceList);
+
+ while (!IsNull (&Private->DeviceList, Node)) {
+ DeviceData = AHCI_PEI_ATA_DEVICE_INFO_FROM_THIS (Node);
+
+ if ((DeviceData->Port == Port) &&
+ (DeviceData->PortMultiplier > *PortMultiplierPort)){
+ *PortMultiplierPort = DeviceData->PortMultiplier;
+ goto Exit;
+ }
+
+ Node = GetNextNode (&Private->DeviceList, Node);
+ }
+
+ return EFI_NOT_FOUND;
+ } else if (*PortMultiplierPort == 0xFFFF) {
+ //
+ // If the PortMultiplierPort is all 0xFF's, start to traverse the device list
+ // from the beginning.
+ //
+ Node = GetFirstNode (&Private->DeviceList);
+
+ while (!IsNull (&Private->DeviceList, Node)) {
+ DeviceData = AHCI_PEI_ATA_DEVICE_INFO_FROM_THIS (Node);
+
+ if (DeviceData->Port == Port){
+ *PortMultiplierPort = DeviceData->PortMultiplier;
+ goto Exit;
+ }
+
+ Node = GetNextNode (&Private->DeviceList, Node);
+ }
+
+ return EFI_NOT_FOUND;
+ } else {
+ //
+ // PortMultiplierPort is not equal to all 0xFF's and not equal to previous
+ // return value.
+ //
+ return EFI_INVALID_PARAMETER;
+ }
+
+Exit:
+ //
+ // Update the PreviousPortMultiplier.
+ //
+ Private->PreviousPortMultiplier = *PortMultiplierPort;
+
+ return EFI_SUCCESS;
+}
+
+/**
+ Gets the device path information of the underlying ATA host controller.
+
+ @param[in] This The PPI instance pointer.
+ @param[out] DevicePathLength The length of the device path in bytes specified
+ by DevicePath.
+ @param[out] DevicePath The device path of the underlying ATA host controller.
+ This field re-uses EFI Device Path Protocol as
+ defined by Section 10.2 EFI Device Path Protocol
+ of UEFI 2.7 Specification.
+
+ @retval EFI_SUCCESS The device path of the ATA host controller has
+ been successfully returned.
+ @retval EFI_INVALID_PARAMETER DevicePathLength or DevicePath is NULL.
+ @retval EFI_OUT_OF_RESOURCES Not enough resource to return the device path.
+
+**/
+EFI_STATUS
+EFIAPI
+AhciAtaPassThruGetDevicePath (
+ IN EDKII_PEI_ATA_PASS_THRU_PPI *This,
+ OUT UINTN *DevicePathLength,
+ OUT EFI_DEVICE_PATH_PROTOCOL **DevicePath
+ )
+{
+ PEI_AHCI_CONTROLLER_PRIVATE_DATA *Private;
+
+ if (This == NULL || DevicePathLength == NULL || DevicePath == NULL) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ Private = GET_AHCI_PEIM_HC_PRIVATE_DATA_FROM_THIS_PASS_THRU (This);
+
+ *DevicePathLength = Private->DevicePathLength;
+ *DevicePath = AllocateCopyPool (Private->DevicePathLength, Private->DevicePath);
+ if (*DevicePath == NULL) {
+ *DevicePathLength = 0;
+ return EFI_OUT_OF_RESOURCES;
+ }
+
+ return EFI_SUCCESS;
+}