diff options
author | Kerry Sheh <shekairui@gmail.com> | 2012-01-31 20:39:37 +0800 |
---|---|---|
committer | Patrick Georgi <patrick@georgi-clan.de> | 2012-02-02 13:54:36 +0100 |
commit | 9292d89be84d6abf9257ddb872887d4f53b2a00e (patch) | |
tree | 9eaa548f1742745f57fc92a12734649fec8db1cd /src/vendorcode/amd/cimx/rd890/nbIommu.c | |
parent | 17670866a0d12839bc2a4c852210ccf11d3cb4b2 (diff) |
RD890 Northbridge: AMD RD890/SR56X0 Northbridge CIMX code
Change-Id: If9908ffeb5b707a660db38dc44f5118347cbcc06
Signed-off-by: Kerry Sheh <kerry.she@amd.com>
Signed-off-by: Kerry Sheh <shekairui@gmail.com>
Reviewed-on: http://review.coreboot.org/557
Tested-by: build bot (Jenkins)
Reviewed-by: Patrick Georgi <patrick@georgi-clan.de>
Diffstat (limited to 'src/vendorcode/amd/cimx/rd890/nbIommu.c')
-rw-r--r-- | src/vendorcode/amd/cimx/rd890/nbIommu.c | 1737 |
1 files changed, 1737 insertions, 0 deletions
diff --git a/src/vendorcode/amd/cimx/rd890/nbIommu.c b/src/vendorcode/amd/cimx/rd890/nbIommu.c new file mode 100644 index 0000000000..705b1a82fd --- /dev/null +++ b/src/vendorcode/amd/cimx/rd890/nbIommu.c @@ -0,0 +1,1737 @@ +/** + * @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); +} |