MdeModulePkg UhciPei: Support IoMmu

Update the UhciPei driver to consume IOMMU_PPI to allocate DMA buffer.

If no IOMMU_PPI exists, this driver still calls PEI service to allocate
DMA buffer, with assumption that DRAM==DMA.

Cc: Jiewen Yao <jiewen.yao@intel.com>
Contributed-under: TianoCore Contribution Agreement 1.1
Signed-off-by: Star Zeng <star.zeng@intel.com>
Reviewed-by: Jiewen Yao <jiewen.yao@intel.com>
This commit is contained in:
Star Zeng
2017-11-20 15:44:39 +08:00
parent c34a5aab53
commit 8284b1791e
4 changed files with 719 additions and 99 deletions

View File

@@ -2,7 +2,7 @@
PEIM to produce gPeiUsbHostControllerPpiGuid based on gPeiUsbControllerPpiGuid
which is used to enable recovery function from USB Drivers.
Copyright (c) 2006 - 2016, Intel Corporation. All rights reserved. <BR>
Copyright (c) 2006 - 2017, Intel Corporation. All rights reserved. <BR>
This program and the accompanying materials
are licensed and made available under the terms and conditions
@@ -17,6 +17,78 @@ WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
#include "UhcPeim.h"
/**
Stop the host controller.
@param Uhc The UHCI device.
@param Timeout Max time allowed.
@retval EFI_SUCCESS The host controller is stopped.
@retval EFI_TIMEOUT Failed to stop the host controller.
**/
EFI_STATUS
UhciStopHc (
IN USB_UHC_DEV *Uhc,
IN UINTN Timeout
)
{
UINT16 CommandContent;
UINT16 UsbSts;
UINTN Index;
CommandContent = USBReadPortW (Uhc, Uhc->UsbHostControllerBaseAddress + USBCMD);
CommandContent &= USBCMD_RS;
USBWritePortW (Uhc, Uhc->UsbHostControllerBaseAddress + USBCMD, CommandContent);
//
// ensure the HC is in halt status after send the stop command
// Timeout is in us unit.
//
for (Index = 0; Index < (Timeout / 50) + 1; Index++) {
UsbSts = USBReadPortW (Uhc, Uhc->UsbHostControllerBaseAddress + USBSTS);
if ((UsbSts & USBSTS_HCH) == USBSTS_HCH) {
return EFI_SUCCESS;
}
MicroSecondDelay (50);
}
return EFI_TIMEOUT;
}
/**
One notified function to stop the Host Controller at the end of PEI
@param[in] PeiServices Pointer to PEI Services Table.
@param[in] NotifyDescriptor Pointer to the descriptor for the Notification event that
caused this function to execute.
@param[in] Ppi Pointer to the PPI data associated with this function.
@retval EFI_SUCCESS The function completes successfully
@retval others
**/
EFI_STATUS
EFIAPI
UhcEndOfPei (
IN EFI_PEI_SERVICES **PeiServices,
IN EFI_PEI_NOTIFY_DESCRIPTOR *NotifyDescriptor,
IN VOID *Ppi
)
{
USB_UHC_DEV *Uhc;
Uhc = PEI_RECOVERY_USB_UHC_DEV_FROM_THIS_NOTIFY (NotifyDescriptor);
//
// Stop the Host Controller
//
UhciStopHc (Uhc, 1000 * 1000);
return EFI_SUCCESS;
}
/**
Initializes Usb Host Controller.
@@ -98,6 +170,7 @@ UhcPeimEntry (
UhcDev = (USB_UHC_DEV *) ((UINTN) TempPtr);
UhcDev->Signature = USB_UHC_DEV_SIGNATURE;
IoMmuInit (&UhcDev->IoMmu);
UhcDev->UsbHostControllerBaseAddress = (UINT32) BaseAddress;
//
@@ -133,6 +206,12 @@ UhcPeimEntry (
continue;
}
UhcDev->EndOfPeiNotifyList.Flags = (EFI_PEI_PPI_DESCRIPTOR_NOTIFY_CALLBACK | EFI_PEI_PPI_DESCRIPTOR_TERMINATE_LIST);
UhcDev->EndOfPeiNotifyList.Guid = &gEfiEndOfPeiSignalPpiGuid;
UhcDev->EndOfPeiNotifyList.Notify = UhcEndOfPei;
PeiServicesNotifyPpi (&UhcDev->EndOfPeiNotifyList);
Index++;
}
@@ -190,9 +269,11 @@ UhcControlTransfer (
TD_STRUCT *PtrStatusTD;
EFI_STATUS Status;
UINT32 DataLen;
UINT8 *PtrDataSource;
UINT8 *Ptr;
UINT8 DataToggle;
UINT8 *RequestPhy;
VOID *RequestMap;
UINT8 *DataPhy;
VOID *DataMap;
UhcDev = PEI_RECOVERY_USB_UHC_DEV_FROM_UHCI_THIS (This);
@@ -216,6 +297,24 @@ UhcControlTransfer (
ClearStatusReg (UhcDev, StatusReg);
//
// Map the Request and data for bus master access,
// then create a list of TD for this transfer
//
Status = UhciMapUserRequest (UhcDev, Request, &RequestPhy, &RequestMap);
if (EFI_ERROR (Status)) {
return Status;
}
Status = UhciMapUserData (UhcDev, TransferDirection, Data, DataLength, &PktID, &DataPhy, &DataMap);
if (EFI_ERROR (Status)) {
if (RequestMap != NULL) {
IoMmuUnmap (UhcDev->IoMmu, RequestMap);
}
return Status;
}
//
// generate Setup Stage TD
//
@@ -228,6 +327,7 @@ UhcControlTransfer (
0,
DeviceSpeed,
(UINT8 *) Request,
RequestPhy,
(UINT8) sizeof (EFI_USB_DEVICE_REQUEST),
&PtrSetupTD
);
@@ -242,38 +342,11 @@ UhcControlTransfer (
//
// Data Stage of Control Transfer
//
switch (TransferDirection) {
case EfiUsbDataIn:
PktID = INPUT_PACKET_ID;
PtrDataSource = Data;
DataLen = (UINT32) *DataLength;
Ptr = PtrDataSource;
break;
case EfiUsbDataOut:
PktID = OUTPUT_PACKET_ID;
PtrDataSource = Data;
DataLen = (UINT32) *DataLength;
Ptr = PtrDataSource;
break;
//
// no data stage
//
case EfiUsbNoData:
if (*DataLength != 0) {
return EFI_INVALID_PARAMETER;
}
PktID = OUTPUT_PACKET_ID;
PtrDataSource = NULL;
DataLen = 0;
Ptr = NULL;
break;
default:
return EFI_INVALID_PARAMETER;
if (TransferDirection == EfiUsbNoData) {
DataLen = 0;
} else {
DataLen = (UINT32) *DataLength;
}
DataToggle = 1;
@@ -297,7 +370,8 @@ UhcControlTransfer (
UhcDev,
DeviceAddress,
0,
Ptr,
Data,
DataPhy,
PacketSize,
PktID,
DataToggle,
@@ -312,7 +386,8 @@ UhcControlTransfer (
PtrPreTD = PtrTD;
DataToggle ^= 1;
Ptr += PacketSize;
Data = (VOID *) ((UINT8 *) Data + PacketSize);
DataPhy += PacketSize;
DataLen -= PacketSize;
}
@@ -365,14 +440,19 @@ UhcControlTransfer (
// if has errors that cause host controller halt, then return EFI_DEVICE_ERROR directly.
//
if (!IsStatusOK (UhcDev, StatusReg)) {
ClearStatusReg (UhcDev, StatusReg);
*TransferResult |= EFI_USB_ERR_SYSTEM;
return EFI_DEVICE_ERROR;
Status = EFI_DEVICE_ERROR;
}
ClearStatusReg (UhcDev, StatusReg);
if (DataMap != NULL) {
IoMmuUnmap (UhcDev->IoMmu, DataMap);
}
if (RequestMap != NULL) {
IoMmuUnmap (UhcDev->IoMmu, RequestMap);
}
return Status;
}
@@ -431,8 +511,6 @@ UhcBulkTransfer (
TD_STRUCT *PtrPreTD;
UINT8 PktID;
UINT8 *PtrDataSource;
UINT8 *Ptr;
BOOLEAN IsFirstTD;
@@ -444,6 +522,9 @@ UhcBulkTransfer (
UINT16 CommandContent;
UINT8 *DataPhy;
VOID *DataMap;
UhcDev = PEI_RECOVERY_USB_UHC_DEV_FROM_UHCI_THIS (This);
//
@@ -467,7 +548,6 @@ UhcBulkTransfer (
PtrFirstTD = NULL;
PtrPreTD = NULL;
DataLen = 0;
Ptr = NULL;
ShortPacketEnable = FALSE;
@@ -495,33 +575,24 @@ UhcBulkTransfer (
ClearStatusReg (UhcDev, StatusReg);
//
// Map the source data buffer for bus master access,
// then create a list of TDs
//
if ((EndPointAddress & 0x80) != 0) {
TransferDirection = EfiUsbDataIn;
} else {
TransferDirection = EfiUsbDataOut;
}
switch (TransferDirection) {
Status = UhciMapUserData (UhcDev, TransferDirection, Data, DataLength, &PktID, &DataPhy, &DataMap);
case EfiUsbDataIn:
ShortPacketEnable = TRUE;
PktID = INPUT_PACKET_ID;
PtrDataSource = Data;
DataLen = (UINT32) *DataLength;
Ptr = PtrDataSource;
break;
case EfiUsbDataOut:
PktID = OUTPUT_PACKET_ID;
PtrDataSource = Data;
DataLen = (UINT32) *DataLength;
Ptr = PtrDataSource;
break;
default:
break;
if (EFI_ERROR (Status)) {
return Status;
}
DataLen = (UINT32) *DataLength;
PtrQH = UhcDev->BulkQH;
IsFirstTD = TRUE;
@@ -540,7 +611,8 @@ UhcBulkTransfer (
UhcDev,
DeviceAddress,
EndPointAddress,
Ptr,
Data,
DataPhy,
PacketSize,
PktID,
*DataToggle,
@@ -570,7 +642,8 @@ UhcBulkTransfer (
PtrPreTD = PtrTD;
*DataToggle ^= 1;
Ptr += PacketSize;
Data = (VOID *) ((UINT8 *) Data + PacketSize);
DataPhy += PacketSize;
DataLen -= PacketSize;
}
//
@@ -604,14 +677,16 @@ UhcBulkTransfer (
// if has errors that cause host controller halt, then return EFI_DEVICE_ERROR directly.
//
if (!IsStatusOK (UhcDev, StatusReg)) {
ClearStatusReg (UhcDev, StatusReg);
*TransferResult |= EFI_USB_ERR_SYSTEM;
return EFI_DEVICE_ERROR;
Status = EFI_DEVICE_ERROR;
}
ClearStatusReg (UhcDev, StatusReg);
if (DataMap != NULL) {
IoMmuUnmap (UhcDev->IoMmu, DataMap);
}
return Status;
}
@@ -1492,7 +1567,8 @@ CreateTD (
@param DevAddr Device address.
@param Endpoint Endpoint number.
@param DeviceSpeed Device Speed.
@param DevRequest Device reuquest.
@param DevRequest CPU memory address of request structure buffer to transfer.
@param RequestPhy PCI memory address of request structure buffer to transfer.
@param RequestLen Request length.
@param PtrTD TD_STRUCT generated.
@@ -1507,6 +1583,7 @@ GenSetupStageTD (
IN UINT8 Endpoint,
IN UINT8 DeviceSpeed,
IN UINT8 *DevRequest,
IN UINT8 *RequestPhy,
IN UINT8 RequestLen,
OUT TD_STRUCT **PtrTD
)
@@ -1583,7 +1660,11 @@ GenSetupStageTD (
TdStruct->PtrTDBuffer = (UINT8 *) DevRequest;
TdStruct->TDBufferLength = RequestLen;
SetTDDataBuffer (TdStruct);
//
// Set the beginning address of the buffer that will be used
// during the transaction.
//
TdStruct->TDData.TDBufferPtr = (UINT32) (UINTN) RequestPhy;
*PtrTD = TdStruct;
@@ -1596,7 +1677,8 @@ GenSetupStageTD (
@param UhcDev The UHCI device.
@param DevAddr Device address.
@param Endpoint Endpoint number.
@param PtrData Data buffer.
@param PtrData CPU memory address of user data buffer to transfer.
@param DataPhy PCI memory address of user data buffer to transfer.
@param Len Data length.
@param PktID PacketID.
@param Toggle Data toggle value.
@@ -1613,6 +1695,7 @@ GenDataTD (
IN UINT8 DevAddr,
IN UINT8 Endpoint,
IN UINT8 *PtrData,
IN UINT8 *DataPhy,
IN UINT8 Len,
IN UINT8 PktID,
IN UINT8 Toggle,
@@ -1700,7 +1783,11 @@ GenDataTD (
TdStruct->PtrTDBuffer = (UINT8 *) PtrData;
TdStruct->TDBufferLength = Len;
SetTDDataBuffer (TdStruct);
//
// Set the beginning address of the buffer that will be used
// during the transaction.
//
TdStruct->TDData.TDBufferPtr = (UINT32) (UINTN) DataPhy;
*PtrTD = TdStruct;
@@ -1803,7 +1890,11 @@ CreateStatusTD (
PtrTDStruct->PtrTDBuffer = NULL;
PtrTDStruct->TDBufferLength = 0;
SetTDDataBuffer (PtrTDStruct);
//
// Set the beginning address of the buffer that will be used
// during the transaction.
//
PtrTDStruct->TDData.TDBufferPtr = 0;
*PtrTD = PtrTDStruct;
@@ -2173,25 +2264,6 @@ SetTDTokenPacketID (
PtrTDStruct->TDData.TDTokenPID = PacketID;
}
/**
Set the beginning address of the data buffer that will be used
during the transaction.
@param PtrTDStruct Place to store TD_STRUCT pointer.
**/
VOID
SetTDDataBuffer (
IN TD_STRUCT *PtrTDStruct
)
{
//
// Set the beginning address of the data buffer that will be used
// during the transaction.
//
PtrTDStruct->TDData.TDBufferPtr = (UINT32) (UINTN) (PtrTDStruct->PtrTDBuffer);
}
/**
Detect whether the TD is active.
@@ -2773,25 +2845,29 @@ CreateMemoryBlock (
)
{
EFI_STATUS Status;
EFI_PHYSICAL_ADDRESS TempPtr;
UINT8 *TempPtr;
UINTN MemPages;
UINT8 *Ptr;
VOID *Mapping;
EFI_PHYSICAL_ADDRESS MappedAddr;
//
// Memory Block uses MemoryBlockSizeInPages pages,
// memory management header and bit array use 1 page
//
MemPages = MemoryBlockSizeInPages + 1;
Status = PeiServicesAllocatePages (
EfiBootServicesData,
Status = IoMmuAllocateBuffer (
UhcDev->IoMmu,
MemPages,
&TempPtr
(VOID **) &TempPtr,
&MappedAddr,
&Mapping
);
if (EFI_ERROR (Status)) {
return Status;
}
Ptr = (UINT8 *) ((UINTN) TempPtr);
Ptr = TempPtr;
ZeroMem (Ptr, MemPages * EFI_PAGE_SIZE);
@@ -2810,7 +2886,7 @@ CreateMemoryBlock (
//
// Memory block initial address
//
Ptr = (UINT8 *) ((UINTN) TempPtr);
Ptr = TempPtr;
Ptr += EFI_PAGE_SIZE;
(*MemoryHeader)->MemoryBlockPtr = Ptr;
//
@@ -3217,3 +3293,135 @@ DelinkMemoryBlock (
}
}
}
/**
Map address of request structure buffer.
@param Uhc The UHCI device.
@param Request The user request buffer.
@param MappedAddr Mapped address of request.
@param Map Identificaion of this mapping to return.
@return EFI_SUCCESS Success.
@return EFI_DEVICE_ERROR Fail to map the user request.
**/
EFI_STATUS
UhciMapUserRequest (
IN USB_UHC_DEV *Uhc,
IN OUT VOID *Request,
OUT UINT8 **MappedAddr,
OUT VOID **Map
)
{
EFI_STATUS Status;
UINTN Len;
EFI_PHYSICAL_ADDRESS PhyAddr;
Len = sizeof (EFI_USB_DEVICE_REQUEST);
Status = IoMmuMap (
Uhc->IoMmu,
EdkiiIoMmuOperationBusMasterRead,
Request,
&Len,
&PhyAddr,
Map
);
if (!EFI_ERROR (Status)) {
*MappedAddr = (UINT8 *) (UINTN) PhyAddr;
}
return Status;
}
/**
Map address of user data buffer.
@param Uhc The UHCI device.
@param Direction Direction of the data transfer.
@param Data The user data buffer.
@param Len Length of the user data.
@param PktId Packet identificaion.
@param MappedAddr Mapped address to return.
@param Map Identificaion of this mapping to return.
@return EFI_SUCCESS Success.
@return EFI_DEVICE_ERROR Fail to map the user data.
**/
EFI_STATUS
UhciMapUserData (
IN USB_UHC_DEV *Uhc,
IN EFI_USB_DATA_DIRECTION Direction,
IN VOID *Data,
IN OUT UINTN *Len,
OUT UINT8 *PktId,
OUT UINT8 **MappedAddr,
OUT VOID **Map
)
{
EFI_STATUS Status;
EFI_PHYSICAL_ADDRESS PhyAddr;
Status = EFI_SUCCESS;
switch (Direction) {
case EfiUsbDataIn:
//
// BusMasterWrite means cpu read
//
*PktId = INPUT_PACKET_ID;
Status = IoMmuMap (
Uhc->IoMmu,
EdkiiIoMmuOperationBusMasterWrite,
Data,
Len,
&PhyAddr,
Map
);
if (EFI_ERROR (Status)) {
goto EXIT;
}
*MappedAddr = (UINT8 *) (UINTN) PhyAddr;
break;
case EfiUsbDataOut:
*PktId = OUTPUT_PACKET_ID;
Status = IoMmuMap (
Uhc->IoMmu,
EdkiiIoMmuOperationBusMasterRead,
Data,
Len,
&PhyAddr,
Map
);
if (EFI_ERROR (Status)) {
goto EXIT;
}
*MappedAddr = (UINT8 *) (UINTN) PhyAddr;
break;
case EfiUsbNoData:
if ((Len != NULL) && (*Len != 0)) {
Status = EFI_INVALID_PARAMETER;
goto EXIT;
}
*PktId = OUTPUT_PACKET_ID;
*MappedAddr = NULL;
*Map = NULL;
break;
default:
Status = EFI_INVALID_PARAMETER;
}
EXIT:
return Status;
}