| /** |
| * @file |
| * |
| * Routines for IOMMU. |
| * |
| * Implement the IOMMU init and ACPI feature. |
| * |
| * @xrefitem bom "File Content Label" "Release Content" |
| * @e project: CIMx-NB |
| * @e sub-project: |
| * @e \$Revision:$ @e \$Date:$ |
| * |
| */ |
| /***************************************************************************** |
| * |
| * Copyright (C) 2012 Advanced Micro Devices, Inc. |
| * All rights reserved. |
| * |
| * Redistribution and use in source and binary forms, with or without |
| * modification, are permitted provided that the following conditions are met: |
| * * Redistributions of source code must retain the above copyright |
| * notice, this list of conditions and the following disclaimer. |
| * * Redistributions in binary form must reproduce the above copyright |
| * notice, this list of conditions and the following disclaimer in the |
| * documentation and/or other materials provided with the distribution. |
| * * Neither the name of Advanced Micro Devices, Inc. nor the names of |
| * its contributors may be used to endorse or promote products derived |
| * from this software without specific prior written permission. |
| * |
| * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND |
| * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED |
| * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE |
| * DISCLAIMED. IN NO EVENT SHALL ADVANCED MICRO DEVICES, INC. BE LIABLE FOR ANY |
| * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES |
| * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; |
| * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND |
| * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
| * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS |
| * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
| * |
| * |
| ***************************************************************************/ |
| |
| /*---------------------------------------------------------------------------------------- |
| * M O D U L E S U S E D |
| *---------------------------------------------------------------------------------------- |
| */ |
| |
| #include "NbPlatform.h" |
| #include "amdDebugOutLib.h" |
| #include "amdSbLib.h" |
| |
| #define Int32FromChar(a,b,c,d) ((a) << 0 | (b) << 8 | (c) << 16 | (d) << 24) |
| |
| /*---------------------------------------------------------------------------------------- |
| * R D 8 9 0 / S D A T A |
| *---------------------------------------------------------------------------------------- |
| */ |
| |
| // IOMMU Initialization |
| |
| INDIRECT_REG_ENTRY |
| CONST |
| STATIC |
| IommuL1Table[] = { |
| // 01. 0x0C [30:28]=7 L1VirtOrderQueues Increase maximum number virtual queues |
| // for all devices |
| { L1CFG_SEL_WR_EN | L1CFG_SEL_GPP1 | L1REG_0C, 0x8FFFFFFF, 0x70000000 }, |
| { L1CFG_SEL_WR_EN | L1CFG_SEL_GPP2 | L1REG_0C, 0x8FFFFFFF, 0x70000000 }, |
| { L1CFG_SEL_WR_EN | L1CFG_SEL_SB | L1REG_0C, 0x8FFFFFFF, 0x70000000 }, |
| { L1CFG_SEL_WR_EN | L1CFG_SEL_GPP3A | L1REG_0C, 0x8FFFFFFF, 0x70000000 }, |
| { L1CFG_SEL_WR_EN | L1CFG_SEL_GPP3B | L1REG_0C, 0x8FFFFFFF, 0x70000000 }, |
| { L1CFG_SEL_WR_EN | L1CFG_SEL_VC1 | L1REG_0C, 0x8FFFFFFF, 0x70000000 }, |
| // 02. 0x07 [11] L1DEBUG_1 Multiple error logs possible |
| { L1CFG_SEL_WR_EN | L1CFG_SEL_GPP1 | L1REG_07, (UINT32)~(BIT8 + BIT9 + BIT10), BIT11 + BIT5 }, |
| { L1CFG_SEL_WR_EN | L1CFG_SEL_GPP2 | L1REG_07, (UINT32)~(BIT8 + BIT9 + BIT10), BIT11 + BIT5}, |
| { L1CFG_SEL_WR_EN | L1CFG_SEL_SB | L1REG_07, (UINT32)~(BIT8 + BIT9 + BIT10), BIT11 + BIT5}, |
| { L1CFG_SEL_WR_EN | L1CFG_SEL_GPP3A | L1REG_07, (UINT32)~(BIT8 + BIT9 + BIT10), BIT11 + BIT5}, |
| { L1CFG_SEL_WR_EN | L1CFG_SEL_GPP3B | L1REG_07, (UINT32)~(BIT8 + BIT9 + BIT10), BIT11 + BIT5}, |
| { L1CFG_SEL_WR_EN | L1CFG_SEL_VC1 | L1REG_07, (UINT32)~(BIT8 + BIT9 + BIT10), BIT11 + BIT5}, |
| // 02. 0x06 [0] L1DEBUG_0 Phantom function disable |
| { L1CFG_SEL_WR_EN | L1CFG_SEL_GPP1 | L1REG_06, (UINT32)~BIT0, 0 }, |
| { L1CFG_SEL_WR_EN | L1CFG_SEL_GPP2 | L1REG_06, (UINT32)~BIT0, 0 }, |
| { L1CFG_SEL_WR_EN | L1CFG_SEL_SB | L1REG_06, (UINT32)~BIT0, 0 }, |
| { L1CFG_SEL_WR_EN | L1CFG_SEL_GPP3A | L1REG_06, (UINT32)~BIT0, 0 }, |
| { L1CFG_SEL_WR_EN | L1CFG_SEL_GPP3B | L1REG_06, (UINT32)~BIT0, 0 }, |
| { L1CFG_SEL_WR_EN | L1CFG_SEL_VC1 | L1REG_06, (UINT32)~BIT0, 0 } |
| }; |
| |
| INDIRECT_REG_ENTRY |
| CONST |
| STATIC |
| IommuL2Table[] = { |
| // 01. 0x0C [29]=1 IFifoClientPriority Set attribute to VC1 L1 client high priority |
| { L2CFG_SEL_WR_EN | L2REG_0C, 0xD0000000, 0x20000000 }, |
| // 02. 0x10 [9:8]=2 DTCInvalidationSel DTC cache invalidation sequential precise |
| { L2CFG_SEL_WR_EN | L2REG_10, 0xFFFFFC00, 0x00000200 }, |
| // 03. 0x14 [9:8]=2 ITCInvalidationSel ... cache invalidation sequential precise |
| { L2CFG_SEL_WR_EN | L2REG_14, 0xFFFFFC00, 0x00000200 }, |
| // 04. 0x18 [9:8]=2 IPTCAInvalidationSel ... cache invalidation sequential precise |
| { L2CFG_SEL_WR_EN | L2REG_18, 0xFFFFFC00, 0x00000200 }, |
| // 05. 0x1C [9:8]=2 IPTCBInvalidationSel ... cache invalidation sequential precise |
| { L2CFG_SEL_WR_EN | L2REG_1C, 0xFFFFFC00, 0x00000200 }, |
| // 06. 0x50 [9:8]=2 PDCInvalidationSel ... cache invalidation sequential precise |
| { L2CFG_SEL_WR_EN | L2REG_50, 0xFFFFFC00, 0x00000200 }, |
| // 07. 0x10 [4]=1 DTCParityEn DTC cache parity protection |
| { L2CFG_SEL_WR_EN | L2REG_10, (UINT32)~BIT4, BIT4 }, |
| // 08. 0x14 [4]=1 ITCParityEn ... cache parity protection |
| { L2CFG_SEL_WR_EN | L2REG_14, (UINT32)~BIT4, BIT4 }, |
| // 09. 0x18 [4]=1 PTCAParityEn ... cache parity protection |
| { L2CFG_SEL_WR_EN | L2REG_18, (UINT32)~BIT4, BIT4 }, |
| // 10. 0x1C [4]=1 PTCBParityEn ... cache parity protection |
| { L2CFG_SEL_WR_EN | L2REG_1C, (UINT32)~BIT4, BIT4 }, |
| // 11. 0x50 [4]=1 PDCParityEn ... cache parity protection |
| { L2CFG_SEL_WR_EN | L2REG_50, (UINT32)~BIT4, BIT4 }, |
| // 12. 0x80 [0]=1 ERRRuleLock0 Lock fault detection rule sets |
| // 0x30 [0]=1 ERRRuleLock1 |
| { L2CFG_SEL_WR_EN | L2REG_80, (UINT32)~BIT0, BIT0 }, |
| { L2CFG_SEL_WR_EN | L2REG_30, (UINT32)~BIT0, BIT0 }, |
| // 13. 0x56 [2]=0 L2_CP_CONTROL Disable CP flush on invalidation |
| // 0x56 [1]=1 L2_CP_CONTROL Enable CP flush on wait |
| { L2CFG_SEL_WR_EN | L2REG_56, 0xFFFFFFF9, BIT1 }, |
| // A21 |
| { L2CFG_SEL_WR_EN | L2REG_06, 0xFFFFFFFF, BIT6 + BIT7 + BIT5 + BIT8 }, |
| { L2CFG_SEL_WR_EN | L2REG_47, 0xFFFFFFFF, BIT1 + BIT3 + BIT0 + BIT4 + BIT5 }, |
| { L2CFG_SEL_WR_EN | L2REG_07, 0xFFFFFFFF, BIT1 + BIT2 + BIT3 + BIT4 + BIT5 + BIT8 + BIT6}, |
| |
| }; |
| |
| // IOMMU ACPI Initialization |
| |
| IOMMU_IVRS_HEADER |
| STATIC |
| RD890S_DfltHeader = { |
| // 'SRVI', |
| Int32FromChar ('S', 'R', 'V', 'I'), |
| 48, |
| 1, |
| 0, |
| {'A', 'M', 'D', ' ', ' ', 0}, |
| {'R', 'D', '8', '9', '0', 'S', 0, 0}, |
| {'1', ' ', ' ', 0}, |
| {'A','M','D',' '}, |
| {'1', ' ', ' ', 0}, |
| 0, |
| 0 |
| }; |
| |
| IOMMU_EXCLUSIONTABLE |
| STATIC |
| RD890S_DfltExclusion = { |
| sizeof (UINTN) + sizeof (IOMMU_EXCLUSIONRANGE) * 0, |
| {{0, 0}} |
| }; |
| |
| IOMMU_DEVICELIST |
| STATIC |
| RD890S_DfltDevices = { |
| (sizeof (UINT16) + sizeof (UINT16) * 12), |
| { |
| DEVICEID_NB, // Type 2 entry, Device 0, Func 0 <-- NB all functions |
| DEVICEID_GPP1_0, // Type 2 entry, Device 2, Func 0 <-- GPP1 port 0 |
| DEVICEID_GPP1_1, // Type 2 entry, Device 3, Func 0 <-- GPP1 port 1 |
| DEVICEID_GPP3A_0, // Type 2 entry, Device 4, Func 0 <-- GPP3a port 0 |
| DEVICEID_GPP3A_1, // Type 2 entry, Device 5, Func 0 <-- GPP3a port 1 |
| DEVICEID_GPP3A_2, // Type 2 entry, Device 6, Func 0 <-- GPP3a port 2 |
| DEVICEID_GPP3A_3, // Type 2 entry, Device 7, Func 0 <-- GPP3a port 3 |
| DEVICEID_GPP3A_4, // Type 2 entry, Device 9, Func 0 <-- GPP3a port 4 |
| DEVICEID_GPP3A_5, // Type 2 entry, Device A, Func 0 <-- GPP3a port 5 |
| DEVICEID_GPP2_0, // Type 2 entry, Device B, Func 0 <-- GPP2 port 0 |
| DEVICEID_GPP2_1, // Type 2 entry, Device C, Func 0 <-- GPP2 port 1 |
| DEVICEID_GPP3B_0, // Type 2 entry, Device D, Func 0 <-- GPP3b port 0 |
| } |
| }; |
| |
| /*---------------------------------------------------------------------------------------- |
| * P R O T O T Y P E S O F L O C A L F U N C T I O N S |
| *---------------------------------------------------------------------------------------- |
| */ |
| |
| // IOMMU Library |
| |
| BOOLEAN |
| NbIommuEnabled ( |
| IN OUT AMD_NB_CONFIG_BLOCK *ConfigPtr |
| ); |
| |
| BOOLEAN |
| IommuCheckEnable ( |
| IN PCI_ADDR IommuPciAddress, |
| IN AMD_NB_CONFIG *pConfig |
| ); |
| |
| BOOLEAN |
| IommuCheckHp ( |
| IN UINT16 DeviceId, |
| IN AMD_NB_CONFIG *pConfig |
| ); |
| |
| BOOLEAN |
| IommuCheckPhantom ( |
| IN UINT16 DeviceId, |
| IN AMD_NB_CONFIG *pConfig |
| ); |
| |
| UINT32 |
| IommuGetL1 ( |
| IN UINT16 DeviceId |
| ); |
| |
| UINT8 |
| IommuGetLog2 ( |
| IN UINT32 Value |
| ); |
| |
| VOID |
| IommuRecordBusDevFuncInfo ( |
| IN OUT IOMMU_PCI_TOPOLOGY *PciPtr, |
| IN UINT16 DeviceId, |
| IN AMD_NB_CONFIG *pConfig |
| ); |
| |
| |
| AGESA_STATUS |
| IommuInit ( |
| IN OUT AMD_NB_CONFIG *pConfig |
| ); |
| |
| VOID |
| IommuInitL2CacheControl ( |
| IN IOMMU_PCI_TOPOLOGY *PciPtr, |
| IN OUT AMD_NB_CONFIG *pConfig |
| ); |
| |
| VOID |
| IommuPlaceHeader ( |
| IN OUT VOID *BufferPtr, |
| IN OUT AMD_NB_CONFIG *pConfig |
| ); |
| |
| VOID |
| IommuPlaceIvhdAndScanDevices ( |
| IN OUT VOID *BufferPtr, |
| IN OUT AMD_NB_CONFIG *pConfig |
| ); |
| |
| VOID |
| IommuPlaceIvmdAndExclusions ( |
| IN OUT VOID *BufferPtr, |
| IN OUT AMD_NB_CONFIG *pConfig |
| ); |
| |
| VOID |
| IommuIvhdNorthbridgeDevices ( |
| IN OUT IOMMU_PCI_TOPOLOGY *PciPtr, |
| IN OUT IOMMU_IVHD_ENTRY *IvhdPtr, |
| IN OUT AMD_NB_CONFIG *pConfig |
| ); |
| |
| VOID |
| IommuIvhdSouthbridgeDevices ( |
| IN OUT IOMMU_PCI_TOPOLOGY *PciPtr, |
| IN OUT IOMMU_IVHD_ENTRY *IvhdPtr, |
| IN OUT AMD_NB_CONFIG *pConfig |
| ); |
| |
| VOID |
| IommuIvhdApicsAndHpets ( |
| IN OUT IOMMU_IVHD_ENTRY *IvhdPtr, |
| IN OUT AMD_NB_CONFIG *pConfig |
| ); |
| |
| VOID |
| IommuCreateDeviceEntry ( |
| IN OUT IOMMU_PCI_TOPOLOGY *PciPtr, |
| IN UINT16 DeviceId, |
| IN OUT IOMMU_IVHD_ENTRY *IvhdPtr, |
| IN AMD_NB_CONFIG *pConfig |
| ); |
| |
| VOID |
| IommuCreate4ByteEntry ( |
| IN UINT8 Type, |
| IN UINT8 Data, |
| IN UINT16 Word1, |
| IN OUT IOMMU_IVHD_ENTRY *IvhdPtr |
| ); |
| |
| VOID |
| IommuCreate8ByteEntry ( |
| IN UINT8 Type, |
| IN UINT8 Data, |
| IN UINT16 Word1, |
| IN UINT8 Byte4, |
| IN UINT16 Word5, |
| IN UINT8 Byte7, |
| IN OUT IOMMU_IVHD_ENTRY *IvhdPtr |
| ); |
| |
| VOID |
| IommuFinalizeIvrs ( |
| IN OUT VOID *BufferPtr, |
| IN AMD_NB_CONFIG *pConfig |
| ); |
| |
| // IOMMU ACPI Final |
| |
| UINT64 |
| IommuGetApicBaseAddress ( |
| IN VOID *DevicePtr, |
| IN AMD_NB_CONFIG *pConfig |
| ); |
| |
| UINT8 |
| IommuGetApicId ( |
| IN UINT64 BaseAddress, |
| IN VOID *MadtPtr, |
| IN AMD_NB_CONFIG *pConfig |
| ); |
| |
| AGESA_STATUS |
| NbIommuHwInit ( |
| IN OUT AMD_NB_CONFIG_BLOCK *ConfigPtr |
| ); |
| |
| AGESA_STATUS |
| NbIommuHwTopologyInit ( |
| IN OUT AMD_NB_CONFIG_BLOCK *ConfigPtr |
| ); |
| |
| AGESA_STATUS |
| IommuTopologyInit ( |
| IN OUT AMD_NB_CONFIG *pConfig |
| ); |
| |
| /*---------------------------------------------------------------------------------------- |
| * E X P O R T E D |
| *---------------------------------------------------------------------------------------- |
| */ |
| |
| |
| /*----------------------------------------------------------------------------------------*/ |
| /** |
| * Check if IOMMU enable on platform |
| * |
| * @param[in] ConfigPtr Northbridges configuration block pointer. |
| * @retval AGESA_SUCCESS IOMMU initialized and table created |
| * @retval AGESA_UNSUPPORTED IOMMU not enabled or not found |
| * @retval AGESA_ERROR IOMMU initialization failed. |
| * |
| */ |
| BOOLEAN |
| NbIommuEnabled ( |
| IN OUT AMD_NB_CONFIG_BLOCK *ConfigPtr |
| ) |
| { |
| UINT8 NorthbridgeId; |
| BOOLEAN Result; |
| Result = FALSE; |
| for (NorthbridgeId = 0; NorthbridgeId <= ConfigPtr->NumberOfNorthbridges; NorthbridgeId++) { |
| if (ConfigPtr->Northbridges[NorthbridgeId].pNbConfig->IommuBaseAddress != 0) { |
| Result = TRUE; |
| break; |
| } |
| } |
| return Result; |
| } |
| |
| /*----------------------------------------------------------------------------------------*/ |
| /** |
| * Northbridge Iommu Initialization for all NB in system. |
| * |
| * @param[in] ConfigPtr Northbridges configuration block pointer. |
| * @retval AGESA_SUCCESS IOMMU initialized and table created |
| * @retval AGESA_UNSUPPORTED IOMMU not enabled or not found |
| * @retval AGESA_ERROR IOMMU initialization failed. |
| * |
| */ |
| AGESA_STATUS |
| NbIommuInit ( |
| IN OUT AMD_NB_CONFIG_BLOCK *ConfigPtr |
| ) |
| { |
| AGESA_STATUS Status; |
| |
| Status = AGESA_SUCCESS; |
| |
| CIMX_TRACE ((TRACE_DATA (ConfigPtr, CIMX_NB_TRACE), "[NBIOMMU]NbIommuInit Enter\n")); |
| |
| if (NbIommuEnabled (ConfigPtr)) { |
| NbIommuHwInit (ConfigPtr); |
| NbIommuAcpiInit (ConfigPtr); |
| NbIommuHwTopologyInit (ConfigPtr); |
| } else { |
| Status = AGESA_UNSUPPORTED; |
| } |
| |
| CIMX_TRACE ((TRACE_DATA (ConfigPtr, CIMX_NB_TRACE), "[NBIOMMU]NbIommuInit Exit [Status = 0x%x]\n", Status)); |
| return Status; |
| } |
| |
| |
| |
| |
| /*----------------------------------------------------------------------------------------*/ |
| /** |
| * Northbridge Iommu Initialization for all NB in system. |
| * |
| * @param[in] ConfigPtr Northbridges configuration block pointer. |
| * @retval AGESA_SUCCESS IOMMU initialized and table created |
| * @retval AGESA_UNSUPPORTED IOMMU not enabled or not found |
| * @retval AGESA_ERROR IOMMU initialization failed. |
| * |
| */ |
| AGESA_STATUS |
| NbIommuInitS3 ( |
| IN OUT AMD_NB_CONFIG_BLOCK *ConfigPtr |
| ) |
| { |
| AGESA_STATUS Status; |
| |
| Status = AGESA_SUCCESS; |
| CIMX_TRACE ((TRACE_DATA (ConfigPtr, CIMX_NB_TRACE), "[NBIOMMU]NbIommuInitS3 Enter\n")); |
| |
| if (NbIommuEnabled (ConfigPtr)) { |
| NbIommuHwInit (ConfigPtr); |
| NbIommuHwTopologyInit (ConfigPtr); |
| } else { |
| Status = AGESA_UNSUPPORTED; |
| } |
| CIMX_TRACE ((TRACE_DATA (ConfigPtr, CIMX_NB_TRACE), "[NBIOMMU]NbIommuInitS3 Exit [Status = 0x%x]\n", Status)); |
| return Status; |
| } |
| |
| |
| |
| |
| /*----------------------------------------------------------------------------------------*/ |
| /** |
| * Northbridge Iommu HW Initialization for all NB in system. |
| * |
| * @param[in] ConfigPtr Northbridges configuration block pointer. |
| * @retval AGESA_SUCCESS IOMMU initialized and table created |
| * @retval AGESA_ERROR IOMMU initialization failed. |
| * |
| */ |
| AGESA_STATUS |
| NbIommuHwInit ( |
| IN OUT AMD_NB_CONFIG_BLOCK *ConfigPtr |
| ) |
| { |
| UINT8 NorthbridgeId; |
| AMD_NB_CONFIG *pConfig; |
| |
| for (NorthbridgeId = 0; NorthbridgeId <= ConfigPtr->NumberOfNorthbridges; NorthbridgeId++) { |
| pConfig = &ConfigPtr->Northbridges[NorthbridgeId]; |
| ConfigPtr->CurrentNorthbridge = NorthbridgeId; |
| IommuInit (pConfig); |
| } |
| return AGESA_SUCCESS; |
| } |
| |
| /*----------------------------------------------------------------------------------------*/ |
| /** |
| * Northbridge Iommu HW Initialization for all NB in system. |
| * |
| * @param[in] ConfigPtr Northbridges configuration block pointer. |
| * @retval AGESA_SUCCESS IOMMU initialized and table created |
| * @retval AGESA_ERROR IOMMU initialization failed. |
| * |
| */ |
| AGESA_STATUS |
| NbIommuHwTopologyInit ( |
| IN OUT AMD_NB_CONFIG_BLOCK *ConfigPtr |
| ) |
| { |
| UINT8 NorthbridgeId; |
| AMD_NB_CONFIG *pConfig; |
| |
| for (NorthbridgeId = 0; NorthbridgeId <= ConfigPtr->NumberOfNorthbridges; NorthbridgeId++) { |
| pConfig = &ConfigPtr->Northbridges[NorthbridgeId]; |
| ConfigPtr->CurrentNorthbridge = NorthbridgeId; |
| IommuTopologyInit (pConfig); |
| } |
| return AGESA_SUCCESS; |
| } |
| |
| /*----------------------------------------------------------------------------------------*/ |
| /** |
| * Northbridge Iommu Initialization for all NB in system. |
| * |
| * @param[in] ConfigPtr Northbridges configuration block pointer. |
| * @retval AGESA_SUCCESS IOMMU initialized and table created |
| * @retval AGESA_UNSUPPORTED IOMMU not enabled or not found |
| * @retval AGESA_ERROR IOMMU initialization failed. |
| * |
| */ |
| AGESA_STATUS |
| NbIommuAcpiInit ( |
| IN OUT AMD_NB_CONFIG_BLOCK *ConfigPtr |
| ) |
| { |
| UINT8 NorthbridgeId; |
| AGESA_STATUS Status; |
| AMD_NB_CONFIG *pConfig; |
| NB_BUFFER_PARAMS Ivrs; |
| UINTN IvrsHandle; |
| |
| Status = AGESA_SUCCESS; |
| Ivrs.BufferLength = 0; |
| Ivrs.BufferHandle = IVRS_HANDLE; |
| Ivrs.BufferPtr = NULL; |
| pConfig = &ConfigPtr->Northbridges[0]; |
| ConfigPtr->CurrentNorthbridge = 0; |
| IvrsHandle = 0; |
| |
| CIMX_TRACE ((TRACE_DATA (ConfigPtr, CIMX_NB_TRACE), "[NBIOMMU]NbIommuAcpiInit Enter\n")); |
| |
| // Get a buffer for IVRS |
| Ivrs.BufferLength = IVRS_BUFFER_SIZE; |
| Status = LibNbCallBack (PHCB_AmdAllocateBuffer, (UINTN)&Ivrs, &ConfigPtr->Northbridges[0]); |
| if (Status != AGESA_SUCCESS || Ivrs.BufferPtr == NULL) { |
| // Table creation failed |
| return AGESA_ERROR; |
| } |
| |
| // Clear buffer before using |
| LibAmdMemFill (Ivrs.BufferPtr, 0, Ivrs.BufferLength, (AMD_CONFIG_PARAMS *)&(pConfig->sHeader)); |
| |
| // PLACE OUR ACPI IVRS TABLE |
| // 1. Create IVRS header |
| // 2. For each northbridge place IVHD |
| // 3. For northbridge 0 only, place IVMD exclusion entries |
| for (NorthbridgeId = 0; NorthbridgeId <= ConfigPtr->NumberOfNorthbridges; NorthbridgeId++) { |
| pConfig = &ConfigPtr->Northbridges[NorthbridgeId]; |
| ConfigPtr->CurrentNorthbridge = NorthbridgeId; |
| if (NorthbridgeId == 0) { |
| IommuPlaceHeader (Ivrs.BufferPtr, pConfig); |
| } |
| IommuPlaceIvhdAndScanDevices (Ivrs.BufferPtr, pConfig); |
| IommuPlaceIvmdAndExclusions (Ivrs.BufferPtr, pConfig); |
| } |
| IommuFinalizeIvrs (Ivrs.BufferPtr, pConfig); |
| |
| LibAmdSetAcpiTable (Ivrs.BufferPtr, TRUE, &IvrsHandle); |
| |
| CIMX_TRACE ((TRACE_DATA (ConfigPtr, CIMX_NB_TRACE), "[NBIOMMU]NbIommuAcpiInit [IVRS TableAddress = 0x%x]\n", (UINT32)(Ivrs.BufferPtr))); |
| CIMX_TRACE ((TRACE_DATA (ConfigPtr, CIMX_NB_TRACE), "[NBIOMMU]NbIommuAcpiInit Exit [Status = 0x%x]\n", Status)); |
| |
| return AGESA_SUCCESS; |
| } |
| |
| /*----------------------------------------------------------------------------------------*/ |
| /** |
| * Northbridge Iommu IVRS fixup for APICS |
| * |
| * @param[in] ConfigPtr Northbridges configuration block pointer. |
| * @retval AGESA_SUCCESS IOMMU initialized and table patched, or no patching required |
| * @retval AGESA_ERROR IOMMU enabled but no previously generated IVRC table found. |
| * |
| */ |
| AGESA_STATUS |
| NbIommuAcpiFixup ( |
| IN OUT AMD_NB_CONFIG_BLOCK *ConfigPtr |
| ) |
| { |
| AGESA_STATUS Status; |
| UINT8 ApicId; |
| UINT64 ApicBaseAddress; |
| UINT8 NorthbridgeId; |
| BOOLEAN IommuFound; |
| VOID *DevicePtr; |
| VOID *IvhdPtr; |
| VOID *IvrsPtr; |
| VOID *MadtPtr; |
| AMD_NB_CONFIG *pConfig; |
| PCI_ADDR IommuPciAddress; |
| UINTN IvrsHandle; |
| |
| |
| pConfig = &ConfigPtr->Northbridges[0]; |
| IommuFound = FALSE; |
| ApicId = 0xFF; |
| ApicBaseAddress = 0; |
| |
| CIMX_TRACE ((TRACE_DATA (ConfigPtr, CIMX_NB_TRACE), "[NBIOMMU]NbIommuAcpiFixup Enter\n")); |
| |
| for (NorthbridgeId = 0; NorthbridgeId <= ConfigPtr->NumberOfNorthbridges; NorthbridgeId++) { |
| pConfig = &ConfigPtr->Northbridges[NorthbridgeId]; |
| ConfigPtr->CurrentNorthbridge = NorthbridgeId; |
| IommuPciAddress = pConfig->NbPciAddress; |
| IommuPciAddress.Address.Function = NB_IOMMU; |
| if (IommuCheckEnable (IommuPciAddress, pConfig)) { |
| IommuFound = TRUE; |
| } |
| } |
| |
| // Any Iommus enabled? If no, we don't need to patch anything |
| if (!IommuFound) { |
| return AGESA_SUCCESS; |
| } |
| |
| // Check for an IVRS |
| // Check IVRS for a type 10 block (IVHD) |
| // Check for an MADT |
| // If these conditions fail, abort |
| |
| // Status = LibAmdGetAcpiTable ('SRVI', &IvrsPtr, &IvrsHandle); |
| Status = LibAmdGetAcpiTable (Int32FromChar ('S', 'R', 'V', 'I'), &IvrsPtr, &IvrsHandle); |
| if (Status != AGESA_SUCCESS) { |
| // REPORT_EVENT (AGESA_ERROR, GENERAL_ERROR_LOCATE_ACPI_TABLE, 'SRVI', 0, 0, 0, pConfig); |
| REPORT_EVENT (AGESA_ERROR, GENERAL_ERROR_LOCATE_ACPI_TABLE, Int32FromChar ('S', 'R', 'V', 'I'), 0, 0, 0, pConfig); |
| return AGESA_ERROR; |
| } |
| |
| // Status = LibAmdGetAcpiTable ('CIPA', &MadtPtr, NULL); |
| Status = LibAmdGetAcpiTable (Int32FromChar ('C', 'I', 'P', 'A'), &MadtPtr, NULL); |
| if (Status != AGESA_SUCCESS) { |
| // REPORT_EVENT (AGESA_ERROR, GENERAL_ERROR_LOCATE_ACPI_TABLE, 'CIPA', 0, 0, 0, pConfig); |
| REPORT_EVENT (AGESA_ERROR, GENERAL_ERROR_LOCATE_ACPI_TABLE, Int32FromChar ('C', 'I', 'P', 'A'), 0, 0, 0, pConfig); |
| return AGESA_ERROR; |
| } |
| |
| IvhdPtr = LibAmdGetFirstIvrsBlockEntry (TYPE_IVHD, IvrsPtr); |
| if (IvhdPtr == NULL) { |
| return AGESA_ERROR; |
| } |
| |
| // An IVRS can contain one or more IVHD entries (one per IOMMU) |
| // Each IVHD entry can contain one or more APIC entries |
| |
| while (IvhdPtr != NULL) { |
| DevicePtr = LibAmdGetFirstDeviceEntry (DE_SPECIAL, IvhdPtr); |
| do { |
| // Be sure to only fix APIC entries |
| if (*(UINT8*) ((UINT8*)DevicePtr + DE_SPECIAL_VARIETY) == VARIETY_IOAPIC) { |
| ApicBaseAddress = IommuGetApicBaseAddress (DevicePtr, pConfig); |
| ApicId = IommuGetApicId (ApicBaseAddress, MadtPtr, pConfig); |
| *(UINT8*)((UINT8*)DevicePtr + DE_SPECIAL_ID) = ApicId; |
| } |
| DevicePtr = LibAmdGetNextDeviceEntry (DE_SPECIAL, DevicePtr, IvhdPtr); |
| } while (DevicePtr != NULL); |
| |
| IvhdPtr = LibAmdGetNextIvrsBlockEntry (TYPE_IVHD, IvhdPtr, IvrsPtr); |
| } |
| |
| LibAmdSetAcpiTable (IvrsPtr, TRUE, &IvrsHandle); |
| |
| CIMX_TRACE ((TRACE_DATA (ConfigPtr, CIMX_NB_TRACE), "[NBIOMMU]NbIommuAcpiFixup [IVRS TableAddress = 0x%x]\n", (UINT32)IvrsPtr)); |
| CIMX_TRACE ((TRACE_DATA (ConfigPtr, CIMX_NB_TRACE), "[NBIOMMU]NbIommuAcpiFixup [APIC TableAddress = 0x%x]\n", (UINT32)MadtPtr)); |
| CIMX_TRACE ((TRACE_DATA (ConfigPtr, CIMX_NB_TRACE), "[NBIOMMU]NbIommuAcpiFixup Exit\n")); |
| |
| return AGESA_SUCCESS; |
| } |
| |
| /*---------------------------------------------------------------------------------------- |
| * P R I V A T E |
| *---------------------------------------------------------------------------------------- |
| */ |
| UINT32 IommuMmioInitTable[] = { |
| 0x8, 0x0, |
| 0xC, 0x08000000, |
| 0x10, 0x0, |
| 0x14, 0x08000000, |
| 0x2000, 0x0, |
| 0x2008, 0x0 |
| }; |
| |
| /*----------------------------------------------------------------------------------------*/ |
| /** |
| * Nb Iommu Initialization. |
| * |
| * @param[in] pConfig Northbridge configuration pointer |
| * @retval AGESA_SUCCESS IOMMU enable and initialized succesfully. |
| * @retval AGESA_UNSUPPORTED IOMMU not initialized. |
| */ |
| AGESA_STATUS |
| IommuInit ( |
| IN OUT AMD_NB_CONFIG *pConfig |
| ) |
| { |
| UINT8 CapBase; |
| PCI_ADDR IommuPciAddress; |
| UINTN i; |
| |
| IommuPciAddress = pConfig->NbPciAddress; |
| IommuPciAddress.Address.Function = NB_IOMMU; |
| |
| // If the base address = 0, don't enable IOMMU |
| if (pConfig->pNbConfig->IommuBaseAddress == 0) { |
| return AGESA_UNSUPPORTED; |
| } |
| |
| // NBMISCIND:0x75 IOC_FEATURE_CNTL_10_0[10]=1 |
| // 0=disable |
| // 1=enable |
| |
| LibNbPciIndexRMW (pConfig->NbPciAddress.AddressValue | NB_MISC_INDEX, NB_MISC_REG75, AccessS3SaveWidth32, (UINT32)~(BIT28), BIT0, pConfig); |
| |
| // Get Capabilities pointer 32h (points to 40h) - capability ID 0x0F. Not found, we have no IOMMU. |
| CapBase = LibNbFindPciCapability (IommuPciAddress.AddressValue, IOMMU_CAPID, pConfig); |
| if (CapBase == 0) { |
| return AGESA_UNSUPPORTED; |
| } |
| |
| // IOMMU_ADAPTER_ID_W - RW - 32 bits - nbconfigfunc2:0x68 |
| // SUBSYSTEM_VENDOR_ID_W 15:0 0x0 Sets the subsystem vendor ID register header |
| // SUBSYSTEM_ID_W 31:16 0x0 Sets the subsystem ID register in the configuration header |
| CIMX_TRACE ((TRACE_DATA (GET_BLOCK_CONFIG_PTR (pConfig), CIMX_NB_TRACE), "[NBIOMMU]Iommu Device Found [PCI Address = 0x%x]\n", IommuPciAddress.AddressValue)); |
| if (pConfig->pNbConfig->SSID == PCI_INVALID) { |
| LibNbPciRead (IommuPciAddress.AddressValue, AccessWidth32, &pConfig->pNbConfig->SSID, pConfig); |
| } |
| if (pConfig->pNbConfig->SSID) { |
| LibNbPciWrite (IommuPciAddress.AddressValue | 0x68, AccessS3SaveWidth32, &pConfig->pNbConfig->SSID, pConfig); |
| } |
| |
| // Get Capabilities pointer 32h (points to 40h) - capability ID 0x0F |
| // Set Cap_Offset+04h [31:14] Base Address Low [31:14] |
| // Set Cap_Offset+08h [31:0] Base Address High [64:32] |
| // Set Cap_Offset+04h [0] Enable |
| LibNbPciRMW ((IommuPciAddress.AddressValue | (CapBase + 8)), AccessS3SaveWidth32, 0x0, ((UINT32*)&pConfig->pNbConfig->IommuBaseAddress)[1], pConfig); |
| LibNbPciRMW ((IommuPciAddress.AddressValue | (CapBase + 4)), AccessS3SaveWidth32, 0x0, ((UINT32*)&pConfig->pNbConfig->IommuBaseAddress)[0], pConfig); |
| |
| // Enable zeroing of address for zero-byte reads when IOMMU enabled |
| LibNbPciIndexRMW (pConfig->NbPciAddress.AddressValue | NB_MISC_INDEX, NB_MISC_REG01, AccessS3SaveWidth32, (UINT32)~(BIT8 | BIT9), BIT8, pConfig); |
| |
| // 8.3.1 L1 Initialization |
| LibNbIndirectTableInit (IommuPciAddress.AddressValue | L1CFG_INDEX, |
| 0, |
| (INDIRECT_REG_ENTRY*)FIX_PTR_ADDR(&IommuL1Table[0],NULL), |
| (sizeof (IommuL1Table) / sizeof (INDIRECT_REG_ENTRY)), |
| pConfig |
| ); |
| |
| // 8.3.3.1 L2 Common Initialization |
| LibNbIndirectTableInit (IommuPciAddress.AddressValue | L2CFG_INDEX, |
| 0, |
| (INDIRECT_REG_ENTRY*)FIX_PTR_ADDR(&IommuL2Table[0], NULL), |
| (sizeof (IommuL2Table) / sizeof (INDIRECT_REG_ENTRY)), |
| pConfig |
| ); |
| //Configure PDC cache to 12-way set associative cache for A21 |
| if (LibNbGetRevisionInfo (pConfig).Revision > NB_REV_A11) { |
| LibNbPciIndexRMW (IommuPciAddress.AddressValue | L2CFG_INDEX, L2CFG_SEL_WR_EN | L2REG_52, AccessS3SaveWidth32, 0x0, 0xF0000002 , pConfig); |
| } |
| // Start and lock the Iommu settings |
| LibNbPciRMW ((IommuPciAddress.AddressValue | (CapBase + 4)), AccessS3SaveWidth32, 0xFFFFFFFF, (UINT32)BIT0, pConfig); |
| |
| //Reset IOMMU MMIO registers on system reset |
| for (i = 0; i < (sizeof (IommuMmioInitTable) / sizeof (UINT32)); i = i + 2) { |
| LibNbMemRMW (pConfig->pNbConfig->IommuBaseAddress + IommuMmioInitTable[i], AccessS3SaveWidth32, 0x0, IommuMmioInitTable[i + 1], pConfig); |
| } |
| return AGESA_SUCCESS; |
| } |
| |
| |
| /*----------------------------------------------------------------------------------------*/ |
| /** |
| * Iommu Initialization of topology specific data. |
| * |
| * @param[in] pConfig Northbridge configuration pointer |
| * @retval AGESA_SUCCESS IOMMU enable and initialized succesfully. |
| * @retval AGESA_UNSUPPORTED IOMMU not initialized. |
| */ |
| AGESA_STATUS |
| IommuTopologyInit ( |
| IN OUT AMD_NB_CONFIG *pConfig |
| ) |
| { |
| // Set L2 Caches Hash Control based on maximum bus, device, function |
| IommuInitL2CacheControl ((IOMMU_PCI_TOPOLOGY*) &pConfig->pNbConfig->IommuTpologyInfo, pConfig); |
| return AGESA_SUCCESS; |
| } |
| |
| L2_HASH_CONTROL HashControls[] = { |
| { |
| L2_DTC_CONTROL |
| }, |
| { |
| L2_ITC_CONTROL |
| }, |
| { |
| L2_PTC_A_CONTROL |
| }, |
| { |
| L2_PTC_B_CONTROL |
| }, |
| { |
| L2_PDC_CONTROL |
| } |
| }; |
| |
| /*----------------------------------------------------------------------------------------*/ |
| /** |
| * Set L2 Cache Hash Control based on maximum Bus, Dev, Function found |
| * |
| * @param[in] PciPtr Array of bus, device, function |
| * @param[in] pConfig Northbridge configuration structure pointer. |
| */ |
| VOID |
| IommuInitL2CacheControl ( |
| IN IOMMU_PCI_TOPOLOGY *PciPtr, |
| IN OUT AMD_NB_CONFIG *pConfig |
| ) |
| { |
| UINT32 Value; |
| PCI_ADDR IommuPciAddress; |
| UINTN i; |
| UINT8 FuncBitsUsed; |
| UINT8 DevBitsUsed; |
| UINT8 BusBitsUsed; |
| |
| IommuPciAddress = pConfig->NbPciAddress; |
| IommuPciAddress.Address.Function = NB_IOMMU; |
| |
| CIMX_TRACE ((TRACE_DATA (GET_BLOCK_CONFIG_PTR (pConfig), CIMX_NB_TRACE), " L2Cache Init Max Bus = 0x%x Max Device = 0x%x Mux Func = 0x%x\n", PciPtr->MaxBus, PciPtr->MaxDevice, PciPtr->MaxFunction)); |
| |
| FuncBitsUsed = CIMX_MAX (IommuGetLog2 (PciPtr->MaxFunction + 1), 3); |
| DevBitsUsed = IommuGetLog2 (PciPtr->MaxDevice + 1); |
| BusBitsUsed = IommuGetLog2 (PciPtr->MaxBus + 1); |
| |
| for (i = 0; i < (sizeof (HashControls) / sizeof (L2_HASH_CONTROL)); i++) { |
| UINT8 NBits; |
| UINT8 NFuncBits; |
| UINT8 NDevBits; |
| UINT8 NBusBits; |
| LibNbPciIndexRead (IommuPciAddress.AddressValue | L2CFG_INDEX, L2CFG_SEL_WR_EN | HashControls[i].HashControl, AccessWidth32, &Value, pConfig); |
| NBits = (UINT8) (Value >> 28) - IommuGetLog2 ((Value >> 16) & 0xff); |
| NFuncBits = CIMX_MIN (NBits, 0x3); |
| NBits = NBits - NFuncBits; |
| NDevBits = CIMX_MIN ( NBits, DevBitsUsed + FuncBitsUsed - NFuncBits); |
| NBusBits = NBits - NDevBits; |
| CIMX_TRACE ((TRACE_DATA (GET_BLOCK_CONFIG_PTR (pConfig), CIMX_NB_TRACE), " NBusBits = %d, NDevBits = %d, NFuncBits = %d \n", NBusBits, NDevBits, NFuncBits)); |
| LibNbPciIndexRMW ( |
| IommuPciAddress.AddressValue | L2CFG_INDEX, |
| L2CFG_SEL_WR_EN | (HashControls[i].HashControl + 1), |
| AccessS3SaveWidth32, |
| 0xFFFFFE00, |
| (NFuncBits | (NDevBits << 2) | (NBusBits << 5)) & 0x1FF, |
| pConfig |
| ); |
| } |
| } |
| |
| /*----------------------------------------------------------------------------------------*/ |
| /** |
| * Check to see if current PCI Address is an IOMMU |
| * |
| * @param[in] IommuPciAddress PCI Address to check |
| * @param[in] pConfig Northbridge configuration structure pointer. |
| * @retval TRUE if Iommu is enabled and found |
| */ |
| BOOLEAN |
| IommuCheckEnable ( |
| IN PCI_ADDR IommuPciAddress, |
| IN AMD_NB_CONFIG *pConfig |
| ) |
| { |
| UINT8 CapBase; |
| |
| if (pConfig->pNbConfig->IommuBaseAddress == 0x0) { |
| return FALSE; |
| } |
| CapBase = LibNbFindPciCapability (IommuPciAddress.AddressValue, IOMMU_CAPID, pConfig); |
| if (CapBase == 0) { |
| return FALSE; |
| } else { |
| return TRUE; |
| } |
| } |
| |
| /*----------------------------------------------------------------------------------------*/ |
| /** |
| * Check an RD890 PCIE bridge to see if hot plug is enabled |
| * |
| * @param[in] DeviceId Device Id to check |
| * @param[in] pConfig Northbridge configuration structure pointer. |
| * @retval TRUE if current device supports hotplug |
| */ |
| BOOLEAN |
| IommuCheckHp ( |
| IN UINT16 DeviceId, |
| IN AMD_NB_CONFIG *pConfig |
| ) |
| { |
| UINT32 PciData; |
| PCI_ADDR PciAddress; |
| |
| PciAddress.AddressValue = MAKE_SBDFO (0, (DeviceId >> 8) & 0xFF, (DeviceId >> 3) & 0x1F, DeviceId & 0x7, 0); |
| |
| LibNbPciRead (PciAddress.AddressValue | NB_PCIP_REG6C, AccessWidth32, &PciData, pConfig); |
| |
| // Check for hot plug by reading PCIE_SLOT_CAP pcieConfigDev[13:2]:0x6C [6] HOTPLUG_CAPABLE |
| PciData &= BIT6; |
| if (PciData != 0) { |
| return TRUE; |
| } else { |
| return FALSE; |
| } |
| } |
| |
| /*----------------------------------------------------------------------------------------*/ |
| /** |
| * Check a PCIE device to see if it supports phantom functions |
| * |
| * @param[in] DeviceId Device Id to check |
| * @param[in] pConfig Northbridge configuration structure pointer. |
| * @retval TRUE if current device supports phantom functions |
| */ |
| BOOLEAN |
| IommuCheckPhantom ( |
| IN UINT16 DeviceId, |
| IN AMD_NB_CONFIG *pConfig |
| ) |
| { |
| UINT32 PciData; |
| UINT8 PcieCapBase; |
| PCI_ADDR PciAddress; |
| |
| PciAddress.AddressValue = MAKE_SBDFO (0, (DeviceId >> 8) & 0xFF, (DeviceId >> 3) & 0x1F, DeviceId & 0x7, 0); |
| |
| // Check for phantom functions by reading PCIE Device Capabilities register (base + 4) [4:3] 0 = not supported |
| PcieCapBase = LibNbFindPciCapability (PciAddress.AddressValue, PCIE_CAPID, pConfig); |
| if (PcieCapBase != 0) { |
| LibNbPciRead (((PciAddress.AddressValue) | (PcieCapBase + 4)), AccessWidth32, &PciData, pConfig); |
| PciData &= PCIE_PHANTOMMASK; |
| if (PciData != 0) { |
| return TRUE; |
| } |
| } |
| return FALSE; |
| } |
| |
| /*----------------------------------------------------------------------------------------*/ |
| /** |
| * Check to see if current PCI Address is a multi-port PCIE core |
| * |
| * @param[in] DeviceId 16-bit device id to check |
| * @retval L1 configuration select |
| */ |
| UINT32 |
| IommuGetL1 ( |
| IN UINT16 DeviceId |
| ) |
| { |
| // This function translates an RD890 multi-port pci core to the offset of the L1 entry |
| // corresponding to it. An unknown device returns as invalid |
| switch (DeviceId) { |
| case DEVICEID_GPP1_0 : |
| return L1CFG_SEL_GPP1; |
| case DEVICEID_GPP1_1 : |
| return L1CFG_SEL_GPP1; |
| case DEVICEID_GPP2_0 : |
| return L1CFG_SEL_GPP2; |
| case DEVICEID_GPP2_1 : |
| return L1CFG_SEL_GPP2; |
| case DEVICEID_GPP3A_0 : |
| return L1CFG_SEL_GPP3A; |
| case DEVICEID_GPP3A_1 : |
| return L1CFG_SEL_GPP3A; |
| case DEVICEID_GPP3A_2 : |
| return L1CFG_SEL_GPP3A; |
| case DEVICEID_GPP3A_3 : |
| return L1CFG_SEL_GPP3A; |
| case DEVICEID_GPP3A_4 : |
| return L1CFG_SEL_GPP3A; |
| case DEVICEID_GPP3A_5 : |
| return L1CFG_SEL_GPP3A; |
| case DEVICEID_GPP3B_0 : |
| return L1CFG_SEL_GPP3B; |
| default: |
| return PCI_INVALID; |
| } |
| } |
| |
| /*----------------------------------------------------------------------------------------*/ |
| /** |
| * Place IVHD Device Entries |
| * |
| * @param[in] Value Value to find the logarithm of |
| * @retval Logarithm of input Value |
| */ |
| UINT8 |
| IommuGetLog2 ( |
| IN UINT32 Value |
| ) |
| { |
| UINT8 Result; |
| |
| Result = 0; |
| |
| // This code will round a 32bit value to the next highest power of 2 |
| Value--; |
| Value |= Value >> 1; |
| Value |= Value >> 2; |
| Value |= Value >> 4; |
| Value |= Value >> 8; |
| Value |= Value >> 16; |
| Value++; |
| |
| // Calculate the logarithm |
| while (Value >>= 1) { |
| Result++; |
| } |
| |
| return Result; |
| } |
| |
| /*----------------------------------------------------------------------------------------*/ |
| /** |
| * Place IVRS Header for IOMMU ACPI table |
| * |
| * @param[in, out] BufferPtr Pointer to buffer to return IVRS. |
| * @param[in] pConfig Northbridge configuration structure pointer. |
| */ |
| VOID |
| IommuPlaceHeader ( |
| IN OUT VOID *BufferPtr, |
| IN OUT AMD_NB_CONFIG *pConfig |
| ) |
| { |
| IOMMU_IVRS_HEADER *HeaderPtr; |
| HeaderPtr = (IOMMU_IVRS_HEADER *)BufferPtr; |
| LibAmdMemCopy (HeaderPtr, &RD890S_DfltHeader, sizeof (IOMMU_IVRS_HEADER), (AMD_CONFIG_PARAMS *)&(pConfig->sHeader)); |
| } |
| |
| /*----------------------------------------------------------------------------------------*/ |
| /** |
| * Place IVMD (memory device) Create all IVMD entries for a single exclusion table |
| * |
| * @param[in, out] BufferPtr Pointer to text buffer to return IVRS |
| * @param[in] pConfig Northbridge configuration pointer |
| */ |
| VOID |
| IommuPlaceIvmdAndExclusions ( |
| IN OUT VOID *BufferPtr, |
| IN OUT AMD_NB_CONFIG *pConfig |
| ) |
| { |
| UINT8 EntryCount; |
| UINT8 CurrentExclusion; |
| AGESA_STATUS Status; |
| IOMMU_EXCLUSIONTABLE *pExclusion; |
| IOMMU_IVRS_HEADER *HeaderPtr; |
| IOMMU_IVMD_ENTRY *IvmdPtr; |
| |
| pExclusion = &RD890S_DfltExclusion; |
| HeaderPtr = (IOMMU_IVRS_HEADER *)BufferPtr; |
| IvmdPtr = (IOMMU_IVMD_ENTRY *)BufferPtr; |
| |
| Status = LibNbCallBack (PHCB_AmdGetExclusionTable, (UINTN)&pExclusion, pConfig); |
| if (Status == AGESA_SUCCESS) { |
| EntryCount = (UINT8) ((pExclusion->TableLength - sizeof (UINTN)) / sizeof (IOMMU_EXCLUSIONRANGE)); |
| for (CurrentExclusion = 0; CurrentExclusion < EntryCount; CurrentExclusion++) { |
| IvmdPtr = (IOMMU_IVMD_ENTRY*) ((UINT8*)HeaderPtr + HeaderPtr->Length); |
| IvmdPtr->Type = TYPE_IVMD_ALL; // 20h = All peripherals |
| IvmdPtr->Flags = 0x7; // Exclusion range |
| IvmdPtr->Length = 32; // 32 byte structure |
| IvmdPtr->DeviceId = 0; // Reserved for type 20h |
| IvmdPtr->AuxData = 0; // Reserved for type 20h |
| IvmdPtr->Reserved = 0; |
| IvmdPtr->BlockStartAddress = pExclusion->ExclusionRange[CurrentExclusion].Start; |
| IvmdPtr->BlockLength = pExclusion->ExclusionRange[CurrentExclusion].Length; |
| HeaderPtr->Length += 32; // Update size of IVRS |
| } |
| } |
| } |
| |
| /*----------------------------------------------------------------------------------------*/ |
| /** |
| * Place IVHD (hardware device) Entry for IOMMU ACPI table |
| * |
| * @param[in, out] BufferPtr Pointer to text buffer to return IVRS |
| * @param[in] pConfig Northbridge configuration pointer |
| */ |
| VOID |
| IommuPlaceIvhdAndScanDevices ( |
| IN OUT VOID *BufferPtr, |
| IN OUT AMD_NB_CONFIG *pConfig |
| ) |
| { |
| UINT32 Value; |
| IOMMU_PCI_TOPOLOGY PciFlags; |
| IOMMU_IVRS_HEADER *HeaderPtr; |
| IOMMU_IVHD_ENTRY *IvhdPtr; |
| PCI_ADDR IommuPciAddress; |
| |
| HeaderPtr = (IOMMU_IVRS_HEADER *)BufferPtr; |
| IvhdPtr = (IOMMU_IVHD_ENTRY *)BufferPtr; |
| //PciFlags.PhantomFunction = FALSE; |
| PciFlags.MaxBus = 0; |
| PciFlags.MaxDevice = 0; |
| PciFlags.MaxFunction = 0; |
| IommuPciAddress = pConfig->NbPciAddress; |
| IommuPciAddress.Address.Function = NB_IOMMU; |
| |
| IvhdPtr = (IOMMU_IVHD_ENTRY*) ((UINT8*)HeaderPtr + HeaderPtr->Length); |
| IvhdPtr->Type = TYPE_IVHD; // Hardware block |
| IvhdPtr->Flags = FLAGS_COHERENT | FLAGS_IOTLBSUP | FLAGS_ISOC | FLAGS_RESPASSPW | FLAGS_PASSPW; |
| IvhdPtr->Length = 24; // Length = 24 with no devices |
| IvhdPtr->DeviceId = (UINT16)((IommuPciAddress.AddressValue >> 12) & 0xFFFF); // Change 32 bit ID into 16 bit |
| IvhdPtr->CapabilityOffset = (UINT16) (LibNbFindPciCapability (IommuPciAddress.AddressValue, IOMMU_CAPID, pConfig)); |
| IvhdPtr->BaseAddress = pConfig->pNbConfig->IommuBaseAddress; |
| IvhdPtr->PciSegment = 0; |
| LibNbPciRead (IommuPciAddress.AddressValue | (IvhdPtr->CapabilityOffset + 0x10), AccessWidth32, &Value, pConfig); |
| IvhdPtr->IommuInfo = (UINT16)(Value & 0x1f); //Set MSInum. |
| IvhdPtr->IommuInfo |= ((0x13) << 8); //set UnitID |
| IvhdPtr->Reserved = 0; |
| |
| IommuIvhdNorthbridgeDevices (&PciFlags, IvhdPtr, pConfig); |
| if (IommuPciAddress.Address.Bus == 0) { |
| IommuIvhdSouthbridgeDevices (&PciFlags, IvhdPtr, pConfig); |
| } |
| IommuIvhdApicsAndHpets (IvhdPtr, pConfig); |
| pConfig->pNbConfig->IommuTpologyInfo = *((UINT32*) &PciFlags); |
| HeaderPtr->Length += IvhdPtr->Length; |
| } |
| |
| /*----------------------------------------------------------------------------------------*/ |
| /** |
| * Place IVHD Device Entries |
| * |
| * @param[in, out] PciPtr Pci topology flags |
| * @param[in, out] IvhdPtr Pointer to IVHD where entry is appended |
| * @param[in] pConfig NB config block |
| */ |
| VOID |
| IommuIvhdNorthbridgeDevices ( |
| IN OUT IOMMU_PCI_TOPOLOGY *PciPtr, |
| IN OUT IOMMU_IVHD_ENTRY *IvhdPtr, |
| IN OUT AMD_NB_CONFIG *pConfig |
| ) |
| { |
| UINT16 CurrentDevice; |
| UINT16 DeviceId; |
| UINT8 EntryCount; |
| IOMMU_DEVICELIST *pDevices; |
| PCI_ADDR NbPciAddress; |
| PCI_ADDR IommuPciAddress; |
| |
| pDevices = &RD890S_DfltDevices; |
| NbPciAddress = pConfig->NbPciAddress; |
| IommuPciAddress = pConfig->NbPciAddress; |
| |
| IommuPciAddress.Address.Function = NB_IOMMU; |
| EntryCount = (UINT8) ((pDevices->TableLength - sizeof (UINT16)) / sizeof (UINT16)); |
| |
| // Run RD890S device table, fixed for current bus |
| for (CurrentDevice = 0; CurrentDevice < EntryCount; CurrentDevice++) { |
| DeviceId = (UINT16) (NbPciAddress.Address.Bus << 8) | pDevices->Device[CurrentDevice]; |
| IommuCreateDeviceEntry (PciPtr, DeviceId, IvhdPtr, pConfig); |
| |
| // CHECK HOTPLUG OR PHANTOM FUNCTION SUPPORT |
| // For each device, reset PhantomEnable, but set it as a one-shot. If any device under the northbridge PCIE bridge |
| // device has phantom function support enabled, set the L1. Additionally, check the bridge for hotplug, and set the |
| // L1 if so. |
| |
| //PciPtr->PhantomFunction = FALSE; |
| //if (PciPtr->PhantomFunction || IommuCheckHp (DeviceId, pConfig)) { |
| // if (IommuGetL1 (DeviceId) != PCI_INVALID && LibNbGetRevisionInfo (pConfig).Revision != NB_REV_A11) { |
| // // Determine from deviceID which L1 |
| // LibNbPciIndexRMW (IommuPciAddress.AddressValue | L1CFG_INDEX, L1CFG_SEL_WR_EN | IommuGetL1 (DeviceId), AccessS3SaveWidth32, (UINT32)~BIT0, BIT0, pConfig); |
| // } |
| //} |
| //PciPtr->PhantomFunction = FALSE; |
| } |
| } |
| |
| /*----------------------------------------------------------------------------------------*/ |
| /** |
| * Place IVHD Device Entries |
| * |
| * @param[in, out] PciPtr PCI topology flags |
| * @param[in, out] IvhdPtr Pointer to IVHD where entry is appended |
| * @param[in] pConfig NB config structute |
| */ |
| VOID |
| IommuIvhdSouthbridgeDevices ( |
| IN OUT IOMMU_PCI_TOPOLOGY *PciPtr, |
| IN OUT IOMMU_IVHD_ENTRY *IvhdPtr, |
| IN OUT AMD_NB_CONFIG *pConfig |
| ) |
| { |
| UINT16 DeviceId; |
| PCI_ADDR IommuPciAddress; |
| IommuPciAddress = pConfig->NbPciAddress; |
| IommuPciAddress.Address.Function = NB_IOMMU; |
| |
| // Assume Device 0x10 Function 0 - Device 0x17 Function 7 belong to the SB |
| //PciPtr->PhantomFunction = FALSE; |
| for (DeviceId = (0x10 << 3); (DeviceId < (0x18 << 3)); DeviceId++) { |
| IommuCreateDeviceEntry (PciPtr, DeviceId, IvhdPtr, pConfig); |
| //if (PciPtr->PhantomFunction) { |
| // // Enable SB phantom enable |
| // LibNbPciIndexRMW (IommuPciAddress.AddressValue | L1CFG_INDEX, L1CFG_SEL_WR_EN | L1CFG_SEL_SB, AccessS3SaveWidth32, (UINT32)~BIT0, BIT0, pConfig); |
| //} |
| //PciPtr->PhantomFunction = FALSE; |
| } |
| |
| #if defined (IVHD_APIC_SUPPORT) || defined (IVHD_HPET_SUPPORT) |
| DeviceId = (SB_DEV << 3); // Bus 0 Dev 14 Func 0 |
| #endif |
| |
| #ifdef IVHD_APIC_SUPPORT |
| // Southbridge IOAPIC |
| IommuCreate8ByteEntry (DE_SPECIAL, DATA_ALLINTS, 0, 0xFF, DeviceId, VARIETY_IOAPIC, IvhdPtr); |
| #endif |
| |
| #ifdef IVHD_HPET_SUPPORT |
| // Southbridge HPET |
| IommuCreate8ByteEntry (DE_SPECIAL, DATA_ALLINTS, 0, 0, DeviceId, VARIETY_HPET, IvhdPtr); |
| #endif |
| |
| } |
| |
| /*----------------------------------------------------------------------------------------*/ |
| /** |
| * Place IVHD Device Entries |
| * |
| * @param[in, out] IvhdPtr Pointer to buffer to return IVRS. |
| * @param[in] pConfig NB config structute |
| */ |
| VOID |
| IommuIvhdApicsAndHpets ( |
| IN OUT IOMMU_IVHD_ENTRY *IvhdPtr, |
| IN OUT AMD_NB_CONFIG *pConfig |
| ) |
| { |
| #ifdef IVHD_APIC_SUPPORT |
| PCI_ADDR PciAddress; |
| UINT16 DeviceId; |
| UINT32 PciData; |
| |
| PciAddress = pConfig->NbPciAddress; |
| |
| // Northbridge IOAPIC |
| DeviceId = (UINT16)((PciAddress.Address.Bus << 8)) | 1; // Bus X Dev 0 Func 0 |
| LibNbPciRead (PciAddress.AddressValue | 0x4C, AccessWidth32, &PciData, pConfig); |
| if (PciData & (UINT32)BIT1) { |
| IommuCreate8ByteEntry (DE_SPECIAL, DATA_NOINTS, 0, 0xFF, DeviceId, VARIETY_IOAPIC, IvhdPtr); |
| } |
| #endif |
| } |
| |
| /*----------------------------------------------------------------------------------------*/ |
| /** |
| * Place IVHD device entry (type 2 for single function or 3/4 for multifunction) at end of IVHD entry |
| * |
| * @param[in, out] PciPtr Pci topology flags |
| * @param[in] DeviceId DeviceID of entry to potentially create |
| * @param[in, out] IvhdPtr Pointer to IVHD |
| * @param[in] pConfig NB config structute |
| */ |
| VOID |
| IommuCreateDeviceEntry ( |
| IN OUT IOMMU_PCI_TOPOLOGY *PciPtr, |
| IN UINT16 DeviceId, |
| IN OUT IOMMU_IVHD_ENTRY *IvhdPtr, |
| IN AMD_NB_CONFIG *pConfig |
| ) |
| { |
| // 8 BYTE DEVICE ENTRY: |
| // [0] Type (0x2 = Device, 0x3 = Device Range Start, 0x4 = Device Range End |
| // [1] DeviceID LSB (Device/Function) |
| // [2] DeviceID MSB (Bus) |
| // [3] Data (0 = No legacy interrupts) |
| |
| // DEVICEID |
| // A 16 bit bus/device/function DeviceId consists of: |
| // [15:8] Bus |
| // [7:3] Device |
| // [2:0] Function |
| |
| PCI_ADDR PciAddress; |
| UINT32 PciData; |
| UINT8 DeviceCount; |
| UINT8 FunctionCount; |
| UINT8 PcieCapBase; |
| UINT8 PcixCapBase; |
| |
| BOOLEAN LegacyBridge; |
| BOOLEAN MultiFunction; |
| BOOLEAN SubFunction; |
| BOOLEAN DiscreteEntry; |
| UINT8 HighFunction; |
| UINT32 ClassCode; |
| UINT16 ExtendedCapabilityPtr; |
| SB_INFO SbInfo; |
| UINT8 SataEnableRegValue; |
| BOOLEAN SrIovDevice; |
| |
| PcieCapBase = 0; |
| PcixCapBase = 0; |
| LegacyBridge = FALSE; |
| MultiFunction = FALSE; |
| SubFunction = FALSE; |
| DiscreteEntry = FALSE; |
| HighFunction = 0; |
| SataEnableRegValue = 0; |
| SrIovDevice = FALSE; |
| |
| //For SB700, get combined mode status |
| SbInfo = LibAmdSbGetRevisionInfo ((pConfig == NULL)?NULL:GET_BLOCK_CONFIG_PTR (pConfig)); |
| if (SbInfo.Type == SB_SB700) { |
| LibNbPciRead (MAKE_SBDFO (0, 0, 0x14, 0, SATA_ENABLE_REG), AccessWidth8, &SataEnableRegValue , pConfig); |
| } |
| |
| |
| // If the device to check does not exist, exit |
| PciAddress.AddressValue = MAKE_SBDFO (0, (DeviceId >> 8) & 0xFF, (DeviceId >> 3) & 0x1F, DeviceId & 0x7, 0); |
| if (!LibNbIsDevicePresent (PciAddress, pConfig)) { |
| return; |
| }; |
| LibNbPciRead (PciAddress.AddressValue | PCI_CLASS, AccessWidth32, &ClassCode, pConfig); |
| ClassCode = (ClassCode >> 16) & 0xFFFF; // Keep class code and sub-class only |
| |
| // THREE STAGES TO THIS FUNCTION |
| // 1. Check for multifunction or special devices |
| // 2. Place device entry for the current device ID |
| // 3. If a bridge, decide if we need to traverse further |
| |
| // STEP 1 - CHECK FUNCTIONS ON THIS DEVICE |
| // To make decisions, we will need several pieces of information about this device not found with current SBDFO |
| // 1. Multifunction device - To determine if a device entry, or device range entry is needed - check function 0 only |
| // 2. DisableRange - We will create single entries a device containing a PCI or PCIE bridge |
| // 3. How many functions on this device? |
| |
| PciAddress.AddressValue = MAKE_SBDFO (0, (DeviceId >> 8) & 0xFF, (DeviceId >> 3) & 0x1F, 0, 0); |
| LibNbPciRead (PciAddress.AddressValue | PCI_HEADER, AccessWidth32, &PciData, pConfig); |
| if ((PciData & PCI_MULTIFUNCTION) != 0) { |
| MultiFunction = TRUE; |
| } else { |
| DiscreteEntry = TRUE; |
| } |
| if ((DeviceId & 0x7) != 0 && MultiFunction) { |
| SubFunction = TRUE; |
| } |
| |
| if (MultiFunction) { |
| for (FunctionCount = 0; FunctionCount < 8; FunctionCount++) { |
| PciAddress.AddressValue = MAKE_SBDFO (0, (DeviceId >> 8) & 0xFF, (DeviceId >> 3) & 0x1F, FunctionCount, 0); |
| LibNbPciRead (PciAddress.AddressValue | PCI_HEADER, AccessWidth32, &PciData, pConfig); |
| if (PciData != PCI_INVALID) { |
| HighFunction = FunctionCount; |
| } |
| LibNbPciRead (PciAddress.AddressValue | PCI_CLASS, AccessWidth32, &PciData, pConfig); |
| if (((PciData >> 16) & 0xFFFF) == PCI_BRIDGE_CLASS) { |
| DiscreteEntry = TRUE; |
| } |
| } |
| } |
| |
| // For SR IOV devices set for all functions to be available |
| if (MultiFunction && (!DiscreteEntry) && (!SubFunction)) { |
| PciAddress.AddressValue = MAKE_SBDFO (0, (DeviceId >> 8) & 0xFF, (DeviceId >> 3) & 0x1F, 0, 0); |
| ExtendedCapabilityPtr = LibNbFindPcieExtendedCapability (PciAddress.AddressValue, 0x10, pConfig); |
| if (ExtendedCapabilityPtr != 0) { |
| SrIovDevice = TRUE; |
| } |
| } |
| // STEP 2 - PLACE DEVICE ENTRY |
| // We have already decided whether we should use discrete type2 entries, or ranged type3/4 entries |
| // Place each device entry at the end of the current IVHD |
| // In each case, increment the maximum bus/device/function for L2 cache control done after the IVHD is created |
| |
| CIMX_TRACE ((TRACE_DATA (GET_BLOCK_CONFIG_PTR (pConfig), CIMX_NB_TRACE), "[NBIOMMU]Placing Entry for Device [0x%x]\n", DeviceId)); |
| |
| //if (IommuCheckPhantom (DeviceId, pConfig)) { |
| // PciPtr->PhantomFunction = TRUE; |
| //} |
| |
| if (!MultiFunction || DiscreteEntry) { |
| //For Device 0x14, function 0, Set DATA_ALLINTS |
| if (DeviceId == (SB_DEV << 3)) { |
| IommuCreate4ByteEntry (DE_SELECT, DATA_ALLINTS, DeviceId, IvhdPtr); |
| } else if (DeviceId == DEVICEID_IDE) { |
| // For IDE device 0x14, function 1, first check if in combined mode |
| if (SataEnableRegValue & SATA_COMBINED_MODE) { |
| // Create Alias entry in combined mode |
| IommuCreate8ByteEntry (DE_ALIASSELECT, DATA_NOINTS, DEVICEID_IDE, 0, DEVICEID_SATA, 00, IvhdPtr); |
| } else { |
| //Create select entry if not in the combined mode |
| IommuCreate4ByteEntry (DE_SELECT, 0, DeviceId, IvhdPtr); |
| } |
| } else { |
| // For all other single function devices other than device 0x14, functions 0 or 1, create select entry |
| IommuCreate4ByteEntry (DE_SELECT, 0, DeviceId, IvhdPtr); |
| } |
| |
| // Record the largest bus, device, function which will be used as a mask by the Iommu L2 cache |
| // Record if phantom device present for current device. Only set it if present, do not clear. |
| // if (IommuCheckPhantom (DeviceId, pConfig)) { |
| // PciPtr->PhantomFunction = TRUE; |
| // } |
| IommuRecordBusDevFuncInfo (PciPtr, DeviceId, pConfig); |
| } |
| |
| if (MultiFunction && (!DiscreteEntry) && (!SubFunction)) { |
| |
| // This is a multifunction device without a bridge, so create a type 3 and 4 device entry |
| IommuCreate4ByteEntry (DE_START, 0, DeviceId, IvhdPtr); |
| if (SrIovDevice) { |
| IommuCreate4ByteEntry (DE_END, 0, (DeviceId | 0x00FF), IvhdPtr); |
| } else { |
| IommuCreate4ByteEntry (DE_END, 0, DeviceId + HighFunction, IvhdPtr); |
| } |
| |
| // Record the largest bus, device, function which will be used as a mask by the Iommu L2 cache |
| // Record if phantom device present for current device. Only set it if present, do not clear. |
| // if (IommuCheckPhantom (DeviceId, pConfig)) { |
| // PciPtr->PhantomFunction = TRUE; |
| // } |
| IommuRecordBusDevFuncInfo (PciPtr, DeviceId + HighFunction, pConfig); |
| } |
| |
| if (ClassCode == PCI_BRIDGE_CLASS) { |
| UINTN Type; |
| UINT32 BusData; |
| // STEP 3 - BRIDGE DEVICE |
| // These are treated a little differently. We already created the entry for the bridge itself... |
| // For a PCIe bridge, continue down the bridge device creating more entries until we find an endpoint |
| // For a PCI bridge, define the entire sub-bus range as belonging to this source id |
| // For a PCIX bridge, figure out which mode it is operating in |
| PciAddress.AddressValue = MAKE_SBDFO (0, (DeviceId >> 8) & 0xFF, (DeviceId >> 3) & 0x1F, DeviceId & 0x7, 0); |
| LibNbPciRead (PciAddress.AddressValue | PCI_BUS, AccessWidth32, &BusData, pConfig); |
| PcieCapBase = LibNbFindPciCapability (PciAddress.AddressValue, PCIE_CAPID, pConfig); |
| PcixCapBase = LibNbFindPciCapability (PciAddress.AddressValue, PCIX_CAPID, pConfig); |
| |
| Type = 0; |
| if (PcieCapBase != 0) { |
| Type = 1; |
| LibNbPciRead (PciAddress.AddressValue | PcieCapBase, AccessWidth32, &PciData, pConfig); |
| PciData = (PciData >> 16) & PCIE_PORTMASK; // [7:4] are Device/Port type, 01 |
| if (PciData == PCIE_PCIE2PCIX) { |
| Type = 2; |
| } |
| } |
| if (PcixCapBase != 0) { |
| Type = 2; |
| } |
| |
| //For Hot plug capable devices, create 'Start of range' and 'End of range' IVRS'. |
| // This will override Type 1 and Type 2. |
| if (IommuCheckHp (DeviceId, pConfig)) { |
| Type = 3; |
| } |
| |
| switch (Type) { |
| case 0: |
| //PCI |
| IommuRecordBusDevFuncInfo (PciPtr, DeviceId, pConfig); |
| IommuCreate8ByteEntry (DE_ALIASSTART, DATA_NOINTS, (UINT16) (BusData & 0xFF00), 0, DeviceId, 0, IvhdPtr); |
| IommuCreate4ByteEntry (DE_END, 0, (UINT16) (((BusData & 0xFF0000) >> 8) + 0xFF), IvhdPtr); |
| break; |
| case 1: |
| //Pcie (non hot plug) |
| for (DeviceCount = 0; DeviceCount <= 0x1f; DeviceCount++) { |
| for (FunctionCount = 0; FunctionCount <= 0x7; FunctionCount++) { |
| IommuCreateDeviceEntry (PciPtr, ((UINT16) (BusData & 0xFF00)) | (DeviceCount << 3) | FunctionCount, IvhdPtr, pConfig); |
| } |
| } |
| break; |
| case 2: |
| //PCIx |
| IommuRecordBusDevFuncInfo (PciPtr, (UINT16) (BusData & 0xFF00), pConfig); |
| IommuCreate8ByteEntry (DE_ALIASSTART, DATA_NOINTS, (UINT16) ((BusData & 0xFF00) | ( 1 << 3)), 0, (UINT16) (BusData & 0xFF00), 0, IvhdPtr); |
| IommuCreate4ByteEntry (DE_END, 0, (UINT16) (((BusData & 0xFF0000) >> 8) + 0xFF), IvhdPtr); |
| break; |
| case 3: |
| //For Hot plug ports, set all devices and functions behind the secondary bus. |
| IommuCreate4ByteEntry (DE_START, 0, (UINT16) (BusData & 0xFF00), IvhdPtr); // Secondary bus, Device 0, Function 0 |
| IommuCreate4ByteEntry (DE_END, 0, (UINT16) ((BusData & 0xFF00) | (0x1F << 3) | 7), IvhdPtr); // Secondary bus, Device 1f, Function 7 |
| break; |
| default: |
| CIMX_ASSERT (FALSE); |
| } |
| } |
| } |
| |
| /*----------------------------------------------------------------------------------------*/ |
| /** |
| * Place IVHD device entry (type 2 for single function or 3/4 for multifunction) at end of IVHD entry |
| * |
| * @param[in, out] PciPtr Pci topology flags |
| * @param[in] DeviceId DeviceID |
| * @param[in] pConfig NB config structute |
| */ |
| VOID |
| IommuRecordBusDevFuncInfo ( |
| IN OUT IOMMU_PCI_TOPOLOGY *PciPtr, |
| IN UINT16 DeviceId, |
| IN AMD_NB_CONFIG *pConfig |
| ) |
| { |
| UINT16 ExtendedCapabilityPtr; |
| PCI_ADDR Device; |
| Device.AddressValue = MAKE_SBDFO (0, DeviceId >> 8, (DeviceId >> 3) & 0x1f, DeviceId & 0x7, 0); |
| #ifdef EXCLUDE_SB_DEVICE_FROM_L2_HASH |
| if ((UINT8)Device.Address.Bus == 0) { |
| AMD_NB_CONFIG_BLOCK *ConfigPtr = GET_BLOCK_CONFIG_PTR (pConfig); |
| if (ConfigPtr->PlatformType == ServerPlatform) { |
| return; |
| } |
| } |
| #endif |
| Device.AddressValue = MAKE_SBDFO (0, DeviceId >> 8, (DeviceId >> 3) & 0x1f, DeviceId & 0x7, 0); |
| CIMX_TRACE ((TRACE_DATA (GET_BLOCK_CONFIG_PTR (pConfig), CIMX_NB_TRACE), " Device Data For L2 Hash Bus = 0x%x Device = 0x%x Func = 0x%x\n", Device.Address.Bus, Device.Address.Device, Device.Address.Function)); |
| ExtendedCapabilityPtr = LibNbFindPcieExtendedCapability (Device.AddressValue, 0x10, pConfig); |
| if (ExtendedCapabilityPtr != 0) { |
| UINT16 TotalVF; |
| LibNbPciRead (Device.AddressValue | (ExtendedCapabilityPtr + 0xE), AccessWidth16, &TotalVF, pConfig); |
| PciPtr->MaxFunction = CIMX_MAX (PciPtr->MaxFunction, TotalVF); |
| } |
| PciPtr->MaxBus = CIMX_MAX (PciPtr->MaxBus, (UINT8)Device.Address.Bus); |
| PciPtr->MaxDevice = CIMX_MAX (PciPtr->MaxDevice, (UINT8)Device.Address.Device); |
| PciPtr->MaxFunction = CIMX_MAX (PciPtr->MaxFunction, (UINT16) (UINT8)Device.Address.Function); |
| } |
| |
| |
| /*----------------------------------------------------------------------------------------*/ |
| /** |
| * Append data entry to IVRS |
| * |
| * @param[in] Type IVRC entry type |
| * @param[in] Data IVRC entry data |
| * @param[in] Word1 IVRC entry data |
| * @param[in, out] IvhdPtr Current IVHD pointer |
| * |
| */ |
| VOID |
| IommuCreate4ByteEntry ( |
| IN UINT8 Type, |
| IN UINT8 Data, |
| IN UINT16 Word1, |
| IN OUT IOMMU_IVHD_ENTRY *IvhdPtr |
| ) |
| { |
| UINT32 Buffer; |
| UINT16 AlignedDeviceEntryIndex; |
| UINT16 DeviceEntryIndex; |
| |
| Buffer = Type + (Word1 << 8) + (Data << 24); |
| DeviceEntryIndex = (IvhdPtr->Length - 24) / sizeof (UINT32); |
| AlignedDeviceEntryIndex = DeviceEntryIndex; |
| |
| #ifdef IVHD_MIN_8BYTE_ALIGNMENT |
| AlignedDeviceEntryIndex = (DeviceEntryIndex + 1) & 0xfffe; |
| #endif |
| |
| IvhdPtr->DeviceEntry[AlignedDeviceEntryIndex] = Buffer; |
| IvhdPtr->Length += (4 + (AlignedDeviceEntryIndex - DeviceEntryIndex) * 4); |
| CIMX_TRACE ((TRACE_DATA (NULL, CIMX_NB_TRACE), "[NBIOMMU]Added entry - [0x%x]\n", Buffer)); |
| } |
| |
| /*----------------------------------------------------------------------------------------*/ |
| /** |
| * Append data entry to IVRS |
| * |
| * @param[in] Type IVRC entry type |
| * @param[in] Data IVRC entry data |
| * @param[in] Word1 IVRC entry data |
| * @param[in] Byte4 IVRC entry data |
| * @param[in] Word5 IVRC entry data |
| * @param[in] Byte7 IVRC entry data |
| * @param[in, out] IvhdPtr Current IVHD pointer |
| * |
| */ |
| VOID |
| IommuCreate8ByteEntry ( |
| IN UINT8 Type, |
| IN UINT8 Data, |
| IN UINT16 Word1, |
| IN UINT8 Byte4, |
| IN UINT16 Word5, |
| IN UINT8 Byte7, |
| IN OUT IOMMU_IVHD_ENTRY *IvhdPtr |
| ) |
| { |
| UINT16 AlignedDeviceEntryIndex; |
| UINT16 DeviceEntryIndex; |
| UINT64 Buffer; |
| |
| Buffer = Type + (Word1 << 8) + ((UINT32)Data << 24); |
| ((UINT32*)&Buffer)[1] = Byte4 + (Word5 << 8) + (Byte7 << 24); |
| DeviceEntryIndex = (IvhdPtr->Length - 24) / sizeof (UINT32); |
| AlignedDeviceEntryIndex = DeviceEntryIndex; |
| |
| #if defined (IVHD_MIN_8BYTE_ALIGNMENT) || defined (IVHD_SIZE_ALIGNMENT) |
| AlignedDeviceEntryIndex = (DeviceEntryIndex + 1) & 0xfffe; |
| #endif |
| IvhdPtr->DeviceEntry[AlignedDeviceEntryIndex] = ((UINT32*)&Buffer)[0]; |
| IvhdPtr->DeviceEntry[AlignedDeviceEntryIndex + 1] = ((UINT32*)&Buffer)[1]; |
| IvhdPtr->Length += (8 + (AlignedDeviceEntryIndex - DeviceEntryIndex) * 4); |
| CIMX_TRACE ((TRACE_DATA (NULL, CIMX_NB_TRACE), "[NBIOMMU]Added entry - [0x%llx]\n", Buffer)); |
| } |
| |
| /*----------------------------------------------------------------------------------------*/ |
| /** |
| * Set checksum, IvInfo, finish IVRS table |
| * |
| * @param[in, out] BufferPtr Pointer to text buffer to return IVRS. |
| * @param[in, out] pConfig Northbridge configuration structure pointer. |
| * |
| */ |
| VOID |
| IommuFinalizeIvrs ( |
| IN OUT VOID *BufferPtr, |
| IN AMD_NB_CONFIG *pConfig |
| ) |
| { |
| IOMMU_IVRS_HEADER *HeaderPtr; |
| PCI_ADDR IommuPciAddress; |
| UINT32 PciData; |
| |
| HeaderPtr = (IOMMU_IVRS_HEADER *)BufferPtr; |
| IommuPciAddress = pConfig->NbPciAddress; |
| IommuPciAddress.Address.Function = NB_IOMMU; |
| |
| // Find common IvInfo (largest shared) 0x50 |
| // [22] = ATS Translation Reserved |
| // [21:15] = VA Size |
| // [14:8] = PA Size |
| |
| LibNbPciRead (IommuPciAddress.AddressValue | RD890S_CAP_MISC, AccessWidth32, &PciData, pConfig); |
| PciData &= (IVINFO_ATSMASK | IVINFO_VAMASK | IVINFO_PAMASK); |
| HeaderPtr->IvInfo = PciData; |
| |
| //LibAmdUpdateAcpiTableChecksum (HeaderPtr); |
| } |
| |
| /*----------------------------------------------------------------------------------------*/ |
| /** |
| * Nb Iommu Fixup of IVRS APIC entries |
| * |
| * @param[in] DevicePtr Pointer to current device entry |
| * @param[in] pConfig Northbridge configuration structure pointer. |
| * |
| */ |
| UINT64 |
| IommuGetApicBaseAddress ( |
| IN VOID *DevicePtr, |
| IN AMD_NB_CONFIG *pConfig |
| ) |
| { |
| PCI_ADDR PciAddress; |
| UINT16 DeviceId; |
| UINT32 Data; |
| |
| // If no pointer provided, return no base address |
| if (DevicePtr == NULL) { |
| return 0; |
| } |
| |
| // Special entry can be IOAPIC or other(HPET). We only care about the IOAPIC. |
| if (*(UINT8*) ((UINT8*)DevicePtr + DE_SPECIAL_VARIETY) != VARIETY_IOAPIC) { |
| return 0; |
| } |
| |
| DeviceId = *(UINT16*) ((UINT8*)DevicePtr + DE_DEVICEID); |
| PciAddress.AddressValue = MAKE_SBDFO (0, (DeviceId >> 8) & 0xFF, (DeviceId >> 3) & 0x1F, 0, 0); |
| |
| // An APIC entry will only be created for AMD northbridge or southbridges, so |
| // we can assume PCI dev/func = 0, 0 will be a northbridge IOAPIC device |
| // and any other will be a southbridge IOAPIC device. If the device was not |
| // already enabled and known to be an AMD device, no entry would have been created. |
| |
| if ((PciAddress.Address.Device == NB_PCI_DEV) && (PciAddress.Address.Function == NB_HOST)) { |
| |
| // We have an AMD NB, check function 0 |
| Data = 1; |
| PciAddress.Address.Function = 0; |
| LibNbPciWrite (PciAddress.AddressValue | 0xF8, AccessS3SaveWidth32, &Data, pConfig); |
| LibNbPciRead (PciAddress.AddressValue | 0xFC, AccessWidth32, &Data, pConfig); |
| } else { |
| SB_INFO SbInfo; |
| SbInfo = LibAmdSbGetRevisionInfo ((pConfig == NULL)?NULL:GET_BLOCK_CONFIG_PTR (pConfig)); |
| if (SbInfo.Type == SB_SB700) { |
| PciAddress.Address.Function = 0; |
| LibNbPciRead (PciAddress.AddressValue | 0x74, AccessWidth32, &Data, pConfig); |
| } else { |
| LibAmdSbPmioRead ( 0x34, AccessWidth32, &Data, pConfig); |
| } |
| } |
| return ((UINT64) (Data & 0xFFFFFF00)); |
| } |
| |
| /*----------------------------------------------------------------------------------------*/ |
| /** |
| * Nb Iommu Fixup of IVRS APIC entries |
| * |
| * @param[in] BaseAddress Base address to match |
| * @param[in] MadtPtr Pointer to current device entry |
| * @param[in] pConfig Northbridge configuration structure pointer. |
| * |
| */ |
| UINT8 |
| IommuGetApicId ( |
| IN UINT64 BaseAddress, |
| IN VOID *MadtPtr, |
| IN AMD_NB_CONFIG *pConfig |
| ) |
| { |
| VOID *EntryPtr; |
| |
| EntryPtr = LibAmdGetFirstMadtStructure (MADT_APIC_TYPE, MadtPtr); |
| |
| do { |
| // If our base address for a known device matches this MADT, get the APIC ID |
| if (*(UINT32*) ((UINT8*)EntryPtr + MADT_APIC_BASE) == (UINT32)BaseAddress) { |
| return *(UINT8*) ((UINT8*)EntryPtr + MADT_APIC_ID); |
| } |
| EntryPtr = LibAmdGetNextMadtStructure (MADT_APIC_TYPE, EntryPtr, MadtPtr); |
| } while (EntryPtr != NULL); |
| return 0xFF; |
| } |
| |
| /*----------------------------------------------------------------------------------------*/ |
| /** |
| * Disconnect unused PCIe core from IOMMU block. |
| * |
| * @param[in] CoreId Pcie Core Id |
| * @param[in] pConfig Northbridge configuration structure pointer. |
| * |
| */ |
| VOID |
| NbIommuDisconnectPcieCore ( |
| IN CORE CoreId, |
| IN AMD_NB_CONFIG *pConfig |
| ) |
| { |
| PCI_ADDR IommuPciAddress; |
| UINT32 Value; |
| IommuPciAddress = pConfig->NbPciAddress; |
| IommuPciAddress.Address.Function = NB_IOMMU; |
| Value = 1 << ((0x4310 >> (CoreId * 4)) & 0xf); |
| LibNbPciIndexRMW (IommuPciAddress.AddressValue | L2CFG_INDEX, L2CFG_SEL_WR_EN | L2REG_46, AccessS3SaveWidth32, 0xFFFFFFFF, Value , pConfig); |
| } |