diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-11 08:17:27 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-11 08:17:27 +0000 |
commit | f215e02bf85f68d3a6106c2a1f4f7f063f819064 (patch) | |
tree | 6bb5b92c046312c4e95ac2620b10ddf482d3fa8b /src/VBox/Devices/EFI/Firmware/MdeModulePkg/Bus/Ata/AhciPei/AhciPeiPassThru.c | |
parent | Initial commit. (diff) | |
download | virtualbox-f215e02bf85f68d3a6106c2a1f4f7f063f819064.tar.xz virtualbox-f215e02bf85f68d3a6106c2a1f4f7f063f819064.zip |
Adding upstream version 7.0.14-dfsg.upstream/7.0.14-dfsg
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
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.c | 514 |
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; +} |