| /** @file | |
| Framework PEIM to initialize memory on a Quark Memory Controller. | |
| Copyright (c) 2013 - 2016, Intel Corporation. | |
| This program and the accompanying materials | |
| are licensed and made available under the terms and conditions of the BSD License | |
| which accompanies this distribution. The full text of the license may be found at | |
| http://opensource.org/licenses/bsd-license.php | |
| THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, | |
| WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. | |
| **/ | |
| #include "CommonHeader.h" | |
| #include "MrcWrapper.h" | |
| #include <Ioh.h> | |
| #include "Platform.h" | |
| #include <Library/PlatformHelperLib.h> | |
| // | |
| // ------------------------ TSEG Base | |
| // | |
| // ------------------------ RESERVED_CPU_S3_SAVE_OFFSET | |
| // CPU S3 data | |
| // ------------------------ RESERVED_ACPI_S3_RANGE_OFFSET | |
| // S3 Memory base structure | |
| // ------------------------ TSEG + 1 page | |
| #define RESERVED_CPU_S3_SAVE_OFFSET (RESERVED_ACPI_S3_RANGE_OFFSET - sizeof (SMM_S3_RESUME_STATE)) | |
| // Strap configuration register specifying DDR setup | |
| #define QUARK_SCSS_REG_STPDDRCFG 0x00 | |
| // Macro counting array elements | |
| #define COUNT(a) (sizeof(a)/sizeof(*a)) | |
| EFI_MEMORY_TYPE_INFORMATION mDefaultQncMemoryTypeInformation[] = { | |
| { EfiReservedMemoryType, EDKII_RESERVED_SIZE_PAGES }, // BIOS Reserved | |
| { EfiACPIMemoryNVS, ACPI_NVS_SIZE_PAGES }, // S3, SMM, etc | |
| { EfiRuntimeServicesData, RUNTIME_SERVICES_DATA_SIZE_PAGES }, | |
| { EfiRuntimeServicesCode, RUNTIME_SERVICES_CODE_SIZE_PAGES }, | |
| { EfiACPIReclaimMemory, ACPI_RECLAIM_SIZE_PAGES }, // ACPI ASL | |
| { EfiMaxMemoryType, 0 } | |
| }; | |
| /** | |
| Configure Uart mmio base for MRC serial log purpose | |
| @param MrcData - MRC configuration data updated | |
| **/ | |
| VOID | |
| MrcUartConfig( | |
| MRC_PARAMS *MrcData | |
| ) | |
| { | |
| UINT8 UartIdx; | |
| UINT32 RegData32; | |
| UINT8 IohUartBus; | |
| UINT8 IohUartDev; | |
| UartIdx = PcdGet8(PcdIohUartFunctionNumber); | |
| IohUartBus = PcdGet8(PcdIohUartBusNumber); | |
| IohUartDev = PcdGet8(PcdIohUartDevNumber); | |
| RegData32 = PciRead32 (PCI_LIB_ADDRESS(IohUartBus, IohUartDev, UartIdx, PCI_BASE_ADDRESSREG_OFFSET)); | |
| MrcData->uart_mmio_base = RegData32 & 0xFFFFFFF0; | |
| } | |
| /** | |
| Configure MRC from memory controller fuse settings. | |
| @param MrcData - MRC configuration data to be updated. | |
| @return EFI_SUCCESS MRC Config parameters updated from platform data. | |
| **/ | |
| EFI_STATUS | |
| MrcConfigureFromMcFuses ( | |
| OUT MRC_PARAMS *MrcData | |
| ) | |
| { | |
| UINT32 McFuseStat; | |
| McFuseStat = QNCPortRead ( | |
| QUARK_NC_MEMORY_CONTROLLER_SB_PORT_ID, | |
| QUARK_NC_MEMORY_CONTROLLER_REG_DFUSESTAT | |
| ); | |
| DEBUG ((EFI_D_INFO, "MRC McFuseStat 0x%08x\n", McFuseStat)); | |
| if ((McFuseStat & B_DFUSESTAT_ECC_DIS) != 0) { | |
| DEBUG ((EFI_D_INFO, "MRC Fuse : fus_dun_ecc_dis.\n")); | |
| MrcData->ecc_enables = 0; | |
| } else { | |
| MrcData->ecc_enables = 1; | |
| } | |
| return EFI_SUCCESS; | |
| } | |
| /** | |
| Configure MRC from platform info hob. | |
| @param MrcData - MRC configuration data to be updated. | |
| @return EFI_SUCCESS MRC Config parameters updated from hob. | |
| @return EFI_NOT_FOUND Platform Info or MRC Config parameters not found. | |
| @return EFI_INVALID_PARAMETER Wrong params in hob. | |
| **/ | |
| EFI_STATUS | |
| MrcConfigureFromInfoHob ( | |
| OUT MRC_PARAMS *MrcData | |
| ) | |
| { | |
| PDAT_MRC_ITEM *ItemData; | |
| ItemData = (PDAT_MRC_ITEM *)PcdGetPtr (PcdMrcParameters); | |
| MrcData->channel_enables = ItemData->ChanMask; | |
| MrcData->channel_width = ItemData->ChanWidth; | |
| MrcData->address_mode = ItemData->AddrMode; | |
| // Enable scrambling if requested. | |
| MrcData->scrambling_enables = (ItemData->Flags & PDAT_MRC_FLAG_SCRAMBLE_EN) != 0; | |
| MrcData->ddr_type = ItemData->DramType; | |
| MrcData->dram_width = ItemData->DramWidth; | |
| MrcData->ddr_speed = ItemData->DramSpeed; | |
| // Enable ECC if requested. | |
| MrcData->rank_enables = ItemData->RankMask; | |
| MrcData->params.DENSITY = ItemData->DramDensity; | |
| MrcData->params.tCL = ItemData->tCL; | |
| MrcData->params.tRAS = ItemData->tRAS; | |
| MrcData->params.tWTR = ItemData->tWTR; | |
| MrcData->params.tRRD = ItemData->tRRD; | |
| MrcData->params.tFAW = ItemData->tFAW; | |
| MrcData->refresh_rate = ItemData->SrInt; | |
| MrcData->sr_temp_range = ItemData->SrTemp; | |
| MrcData->ron_value = ItemData->DramRonVal; | |
| MrcData->rtt_nom_value = ItemData->DramRttNomVal; | |
| MrcData->rd_odt_value = ItemData->SocRdOdtVal; | |
| DEBUG ((EFI_D_INFO, "MRC dram_width %d\n", MrcData->dram_width)); | |
| DEBUG ((EFI_D_INFO, "MRC rank_enables %d\n",MrcData->rank_enables)); | |
| DEBUG ((EFI_D_INFO, "MRC ddr_speed %d\n", MrcData->ddr_speed)); | |
| DEBUG ((EFI_D_INFO, "MRC flags: %s\n", | |
| (MrcData->scrambling_enables) ? L"SCRAMBLE_EN" : L"" | |
| )); | |
| DEBUG ((EFI_D_INFO, "MRC density=%d tCL=%d tRAS=%d tWTR=%d tRRD=%d tFAW=%d\n", | |
| MrcData->params.DENSITY, | |
| MrcData->params.tCL, | |
| MrcData->params.tRAS, | |
| MrcData->params.tWTR, | |
| MrcData->params.tRRD, | |
| MrcData->params.tFAW | |
| )); | |
| return EFI_SUCCESS; | |
| } | |
| /** | |
| Configure ECC scrub | |
| @param MrcData - MRC configuration | |
| **/ | |
| VOID | |
| EccScrubSetup( | |
| const MRC_PARAMS *MrcData | |
| ) | |
| { | |
| UINT32 BgnAdr = 0; | |
| UINT32 EndAdr = MrcData->mem_size; | |
| UINT32 BlkSize = PcdGet8(PcdEccScrubBlkSize) & SCRUB_CFG_BLOCKSIZE_MASK; | |
| UINT32 Interval = PcdGet8(PcdEccScrubInterval) & SCRUB_CFG_INTERVAL_MASK; | |
| if( MrcData->ecc_enables == 0 || MrcData->boot_mode == bmS3 || Interval == 0) { | |
| // No scrub configuration needed if ECC not enabled | |
| // On S3 resume reconfiguration is done as part of resume | |
| // script, see SNCS3Save.c ==> SaveRuntimeScriptTable() | |
| // Also if PCD disables scrub, then we do nothing. | |
| return; | |
| } | |
| QNCPortWrite (QUARK_NC_RMU_SB_PORT_ID, QUARK_NC_ECC_SCRUB_END_MEM_REG, EndAdr); | |
| QNCPortWrite (QUARK_NC_RMU_SB_PORT_ID, QUARK_NC_ECC_SCRUB_START_MEM_REG, BgnAdr); | |
| QNCPortWrite (QUARK_NC_RMU_SB_PORT_ID, QUARK_NC_ECC_SCRUB_NEXT_READ_REG, BgnAdr); | |
| QNCPortWrite (QUARK_NC_RMU_SB_PORT_ID, QUARK_NC_ECC_SCRUB_CONFIG_REG, | |
| Interval << SCRUB_CFG_INTERVAL_SHIFT | | |
| BlkSize << SCRUB_CFG_BLOCKSIZE_SHIFT); | |
| McD0PciCfg32 (QNC_ACCESS_PORT_MCR) = SCRUB_RESUME_MSG(); | |
| } | |
| /** Post InstallS3Memory / InstallEfiMemory tasks given MrcData context. | |
| @param[in] MrcData MRC configuration. | |
| @param[in] IsS3 TRUE if after InstallS3Memory. | |
| **/ | |
| VOID | |
| PostInstallMemory ( | |
| IN MRC_PARAMS *MrcData, | |
| IN BOOLEAN IsS3 | |
| ) | |
| { | |
| UINT32 RmuMainDestBaseAddress; | |
| UINT32 *RmuMainSrcBaseAddress; | |
| UINTN RmuMainSize; | |
| EFI_STATUS Status; | |
| // | |
| // Setup ECC policy (All boot modes). | |
| // | |
| QNCPolicyDblEccBitErr (V_WDT_CONTROL_DBL_ECC_BIT_ERR_WARM); | |
| // | |
| // Find the 64KB of memory for Rmu Main at the top of available memory. | |
| // | |
| InfoPostInstallMemory (&RmuMainDestBaseAddress, NULL, NULL); | |
| DEBUG ((EFI_D_INFO, "RmuMain Base Address : 0x%x\n", RmuMainDestBaseAddress)); | |
| // | |
| // Relocate RmuMain. | |
| // | |
| if (IsS3) { | |
| QNCSendOpcodeDramReady (RmuMainDestBaseAddress); | |
| } else { | |
| Status = PlatformFindFvFileRawDataSection (NULL, PcdGetPtr(PcdQuarkMicrocodeFile), (VOID **) &RmuMainSrcBaseAddress, &RmuMainSize); | |
| ASSERT_EFI_ERROR (Status); | |
| if (!EFI_ERROR (Status)) { | |
| DEBUG ((EFI_D_INFO, "Found Microcode ADDR:SIZE 0x%08x:0x%04x\n", (UINTN) RmuMainSrcBaseAddress, RmuMainSize)); | |
| } | |
| RmuMainRelocation (RmuMainDestBaseAddress, (UINT32) RmuMainSrcBaseAddress, RmuMainSize); | |
| QNCSendOpcodeDramReady (RmuMainDestBaseAddress); | |
| EccScrubSetup (MrcData); | |
| } | |
| } | |
| /** | |
| Do memory initialisation for QNC DDR3 SDRAM Controller | |
| @param FfsHeader Not used. | |
| @param PeiServices General purpose services available to every PEIM. | |
| @return EFI_SUCCESS Memory initialisation completed successfully. | |
| All other error conditions encountered result in an ASSERT. | |
| **/ | |
| EFI_STATUS | |
| MemoryInit ( | |
| IN EFI_PEI_SERVICES **PeiServices | |
| ) | |
| { | |
| MRC_PARAMS MrcData; | |
| EFI_BOOT_MODE BootMode; | |
| EFI_STATUS Status; | |
| EFI_PEI_READ_ONLY_VARIABLE2_PPI *VariableServices; | |
| EFI_STATUS_CODE_VALUE ErrorCodeValue; | |
| PEI_QNC_MEMORY_INIT_PPI *QncMemoryInitPpi; | |
| UINT16 PmswAdr; | |
| ErrorCodeValue = 0; | |
| // | |
| // It is critical that both of these data structures are initialized to 0. | |
| // This PEIM knows the number of DIMMs in the system and works with that | |
| // information. The MCH PEIM that consumes these data structures does not | |
| // know the number of DIMMs so it expects the entire structure to be | |
| // properly initialized. By initializing these to zero, all flags indicating | |
| // that the SPD is present or the row should be configured are set to false. | |
| // | |
| ZeroMem (&MrcData, sizeof(MrcData)); | |
| // | |
| // Get necessary PPI | |
| // | |
| Status = PeiServicesLocatePpi ( | |
| &gEfiPeiReadOnlyVariable2PpiGuid, // GUID | |
| 0, // INSTANCE | |
| NULL, // EFI_PEI_PPI_DESCRIPTOR | |
| (VOID **)&VariableServices // PPI | |
| ); | |
| ASSERT_EFI_ERROR (Status); | |
| // | |
| // Determine boot mode | |
| // | |
| Status = PeiServicesGetBootMode (&BootMode); | |
| ASSERT_EFI_ERROR (Status); | |
| // | |
| // Initialize Error type for reporting status code | |
| // | |
| switch (BootMode) { | |
| case BOOT_ON_FLASH_UPDATE: | |
| ErrorCodeValue = EFI_COMPUTING_UNIT_MEMORY + EFI_CU_MEMORY_EC_UPDATE_FAIL; | |
| break; | |
| case BOOT_ON_S3_RESUME: | |
| ErrorCodeValue = EFI_COMPUTING_UNIT_MEMORY + EFI_CU_MEMORY_EC_S3_RESUME_FAIL; | |
| break; | |
| default: | |
| ErrorCodeValue = EFI_COMPUTING_UNIT_MEMORY; | |
| break; | |
| } | |
| // | |
| // Specify MRC boot mode | |
| // | |
| switch (BootMode) { | |
| case BOOT_ON_S3_RESUME: | |
| case BOOT_ON_FLASH_UPDATE: | |
| MrcData.boot_mode = bmS3; | |
| break; | |
| case BOOT_ASSUMING_NO_CONFIGURATION_CHANGES: | |
| MrcData.boot_mode = bmFast; | |
| break; | |
| default: | |
| MrcData.boot_mode = bmCold; | |
| break; | |
| } | |
| // | |
| // Configure MRC input parameters. | |
| // | |
| Status = MrcConfigureFromMcFuses (&MrcData); | |
| ASSERT_EFI_ERROR (Status); | |
| Status = MrcConfigureFromInfoHob (&MrcData); | |
| ASSERT_EFI_ERROR (Status); | |
| MrcUartConfig(&MrcData); | |
| if (BootMode == BOOT_IN_RECOVERY_MODE) { | |
| // | |
| // Always do bmCold on recovery. | |
| // | |
| DEBUG ((DEBUG_INFO, "MemoryInit:Force bmCold on Recovery\n")); | |
| MrcData.boot_mode = bmCold; | |
| } else { | |
| // | |
| // Load Memory configuration data saved in previous boot from variable | |
| // | |
| Status = LoadConfig ( | |
| PeiServices, | |
| VariableServices, | |
| &MrcData | |
| ); | |
| if (EFI_ERROR (Status)) { | |
| switch (BootMode) { | |
| case BOOT_ON_S3_RESUME: | |
| case BOOT_ON_FLASH_UPDATE: | |
| REPORT_STATUS_CODE ( | |
| EFI_ERROR_CODE + EFI_ERROR_UNRECOVERED, | |
| ErrorCodeValue | |
| ); | |
| PeiServicesResetSystem (); | |
| break; | |
| default: | |
| MrcData.boot_mode = bmCold; | |
| break; | |
| } | |
| } | |
| } | |
| // | |
| // Locate Memory Reference Code PPI | |
| // | |
| Status = PeiServicesLocatePpi ( | |
| &gQNCMemoryInitPpiGuid, // GUID | |
| 0, // INSTANCE | |
| NULL, // EFI_PEI_PPI_DESCRIPTOR | |
| (VOID **)&QncMemoryInitPpi // PPI | |
| ); | |
| ASSERT_EFI_ERROR (Status); | |
| PmswAdr = (UINT16)(LpcPciCfg32 (R_QNC_LPC_GPE0BLK) & 0xFFFF) + R_QNC_GPE0BLK_PMSW; | |
| if( IoRead32 (PmswAdr) & B_QNC_GPE0BLK_PMSW_DRAM_INIT) { | |
| // MRC did not complete last execution, force cold boot path | |
| MrcData.boot_mode = bmCold; | |
| } | |
| // Mark MRC pending | |
| IoOr32 (PmswAdr, (UINT32)B_QNC_GPE0BLK_PMSW_DRAM_INIT); | |
| // | |
| // Call Memory Reference Code's Routines | |
| // | |
| QncMemoryInitPpi->MrcStart (&MrcData); | |
| // Mark MRC completed | |
| IoAnd32 (PmswAdr, ~(UINT32)B_QNC_GPE0BLK_PMSW_DRAM_INIT); | |
| // | |
| // Note emulation platform has to read actual memory size | |
| // MrcData.mem_size from PcdGet32 (PcdMemorySize); | |
| if (BootMode == BOOT_ON_S3_RESUME) { | |
| DEBUG ((EFI_D_INFO, "Following BOOT_ON_S3_RESUME boot path.\n")); | |
| Status = InstallS3Memory (PeiServices, VariableServices, MrcData.mem_size); | |
| if (EFI_ERROR (Status)) { | |
| REPORT_STATUS_CODE ( | |
| EFI_ERROR_CODE + EFI_ERROR_UNRECOVERED, | |
| ErrorCodeValue | |
| ); | |
| PeiServicesResetSystem (); | |
| } | |
| PostInstallMemory (&MrcData, TRUE); | |
| return EFI_SUCCESS; | |
| } | |
| // | |
| // Assign physical memory to PEI and DXE | |
| // | |
| DEBUG ((EFI_D_INFO, "InstallEfiMemory.\n")); | |
| Status = InstallEfiMemory ( | |
| PeiServices, | |
| VariableServices, | |
| BootMode, | |
| MrcData.mem_size | |
| ); | |
| ASSERT_EFI_ERROR (Status); | |
| PostInstallMemory (&MrcData, FALSE); | |
| // | |
| // Save current configuration into Hob and will save into Variable later in DXE | |
| // | |
| DEBUG ((EFI_D_INFO, "SaveConfig.\n")); | |
| Status = SaveConfig ( | |
| &MrcData | |
| ); | |
| ASSERT_EFI_ERROR (Status); | |
| DEBUG ((EFI_D_INFO, "MemoryInit Complete.\n")); | |
| return EFI_SUCCESS; | |
| } | |
| /** | |
| This function saves a config to a HOB. | |
| @param RowInfo The MCH row configuration information. | |
| @param TimingData Timing data to be saved. | |
| @param RowConfArray Row configuration information for each row in the system. | |
| @param SpdData SPD info read for each DIMM slot in the system. | |
| @return EFI_SUCCESS: The function completed successfully. | |
| **/ | |
| EFI_STATUS | |
| SaveConfig ( | |
| IN MRC_PARAMS *MrcData | |
| ) | |
| { | |
| // | |
| // Build HOB data for Memory Config | |
| // HOB data size (stored in variable) is required to be multiple of 8 bytes | |
| // | |
| BuildGuidDataHob ( | |
| &gEfiMemoryConfigDataGuid, | |
| (VOID *) &MrcData->timings, | |
| ((sizeof (MrcData->timings) + 0x7) & (~0x7)) | |
| ); | |
| DEBUG ((EFI_D_INFO, "IIO IoApicBase = %x IoApicLimit=%x\n", IOAPIC_BASE, (IOAPIC_BASE + IOAPIC_SIZE - 1))); | |
| DEBUG ((EFI_D_INFO, "IIO RcbaAddress = %x\n", (UINT32)PcdGet64 (PcdRcbaMmioBaseAddress))); | |
| return EFI_SUCCESS; | |
| } | |
| /** | |
| Load a configuration stored in a variable. | |
| @param TimingData Timing data to be loaded from NVRAM. | |
| @param RowConfArray Row configuration information for each row in the system. | |
| @return EFI_SUCCESS The function completed successfully. | |
| Other Could not read variable. | |
| **/ | |
| EFI_STATUS | |
| LoadConfig ( | |
| IN EFI_PEI_SERVICES **PeiServices, | |
| IN EFI_PEI_READ_ONLY_VARIABLE2_PPI *VariableServices, | |
| IN OUT MRC_PARAMS *MrcData | |
| ) | |
| { | |
| EFI_STATUS Status; | |
| UINTN BufferSize; | |
| PLATFORM_VARIABLE_MEMORY_CONFIG_DATA VarData; | |
| BufferSize = ((sizeof (VarData.timings) + 0x7) & (~0x7)); // HOB data size (stored in variable) is required to be multiple of 8bytes | |
| Status = VariableServices->GetVariable ( | |
| VariableServices, | |
| EFI_MEMORY_CONFIG_DATA_NAME, | |
| &gEfiMemoryConfigDataGuid, | |
| NULL, | |
| &BufferSize, | |
| &VarData.timings | |
| ); | |
| if (!EFI_ERROR (Status)) { | |
| CopyMem (&MrcData->timings, &VarData.timings, sizeof(MrcData->timings)); | |
| } | |
| return Status; | |
| } | |
| /** | |
| This function installs memory. | |
| @param PeiServices PEI Services table. | |
| @param BootMode The specific boot path that is being followed | |
| @param Mch Pointer to the DualChannelDdrMemoryInit PPI | |
| @param RowConfArray Row configuration information for each row in the system. | |
| @return EFI_SUCCESS The function completed successfully. | |
| EFI_INVALID_PARAMETER One of the input parameters was invalid. | |
| EFI_ABORTED An error occurred. | |
| **/ | |
| EFI_STATUS | |
| InstallEfiMemory ( | |
| IN EFI_PEI_SERVICES **PeiServices, | |
| IN EFI_PEI_READ_ONLY_VARIABLE2_PPI *VariableServices, | |
| IN EFI_BOOT_MODE BootMode, | |
| IN UINT32 TotalMemorySize | |
| ) | |
| { | |
| EFI_PHYSICAL_ADDRESS PeiMemoryBaseAddress; | |
| EFI_SMRAM_HOB_DESCRIPTOR_BLOCK *SmramHobDescriptorBlock; | |
| EFI_STATUS Status; | |
| EFI_PEI_HOB_POINTERS Hob; | |
| PEI_DUAL_CHANNEL_DDR_MEMORY_MAP_RANGE MemoryMap[MAX_RANGES]; | |
| UINT8 Index; | |
| UINT8 NumRanges; | |
| UINT8 SmramIndex; | |
| UINT8 SmramRanges; | |
| UINT64 PeiMemoryLength; | |
| UINTN BufferSize; | |
| UINTN PeiMemoryIndex; | |
| EFI_RESOURCE_ATTRIBUTE_TYPE Attribute; | |
| EFI_PHYSICAL_ADDRESS BadMemoryAddress; | |
| EFI_SMRAM_DESCRIPTOR DescriptorAcpiVariable; | |
| VOID *CapsuleBuffer; | |
| UINTN CapsuleBufferLength; | |
| PEI_CAPSULE_PPI *Capsule; | |
| VOID *LargeMemRangeBuf; | |
| UINTN LargeMemRangeBufLen; | |
| UINT8 MorControl; | |
| UINTN DataSize; | |
| // | |
| // Test the memory from 1M->TOM | |
| // | |
| if (BootMode != BOOT_ON_FLASH_UPDATE) { | |
| Status = BaseMemoryTest ( | |
| PeiServices, | |
| 0x100000, | |
| (TotalMemorySize - 0x100000), | |
| Quick, | |
| &BadMemoryAddress | |
| ); | |
| ASSERT_EFI_ERROR (Status); | |
| } | |
| // | |
| // Get the Memory Map | |
| // | |
| NumRanges = MAX_RANGES; | |
| ZeroMem (MemoryMap, sizeof (PEI_DUAL_CHANNEL_DDR_MEMORY_MAP_RANGE) * NumRanges); | |
| Status = GetMemoryMap ( | |
| PeiServices, | |
| TotalMemorySize, | |
| (PEI_DUAL_CHANNEL_DDR_MEMORY_MAP_RANGE *) MemoryMap, | |
| &NumRanges | |
| ); | |
| ASSERT_EFI_ERROR (Status); | |
| // | |
| // Find the highest memory range in processor native address space to give to | |
| // PEI. Then take the top. | |
| // | |
| PeiMemoryBaseAddress = 0; | |
| // | |
| // Query the platform for the minimum memory size | |
| // | |
| Status = GetPlatformMemorySize ( | |
| PeiServices, | |
| BootMode, | |
| &PeiMemoryLength | |
| ); | |
| ASSERT_EFI_ERROR (Status); | |
| // | |
| // Detect MOR request by the OS. | |
| // | |
| MorControl = 0; | |
| DataSize = sizeof (MorControl); | |
| Status = VariableServices->GetVariable ( | |
| VariableServices, | |
| MEMORY_OVERWRITE_REQUEST_VARIABLE_NAME, | |
| &gEfiMemoryOverwriteControlDataGuid, | |
| NULL, | |
| &DataSize, | |
| &MorControl | |
| ); | |
| PeiMemoryIndex = 0; | |
| for (Index = 0; Index < NumRanges; Index++) | |
| { | |
| DEBUG ((EFI_D_INFO, "Found 0x%x bytes at ", MemoryMap[Index].RangeLength)); | |
| DEBUG ((EFI_D_INFO, "0x%x.\n", MemoryMap[Index].PhysicalAddress)); | |
| // | |
| // If OS requested a memory overwrite perform it now. Only do it for memory | |
| // used by the OS. | |
| // | |
| if (MOR_CLEAR_MEMORY_VALUE (MorControl) && MemoryMap[Index].Type == DualChannelDdrMainMemory) { | |
| DEBUG ((EFI_D_INFO, "Clear memory per MOR request.\n")); | |
| if ((UINTN)MemoryMap[Index].RangeLength > 0) { | |
| if ((UINTN)MemoryMap[Index].PhysicalAddress == 0) { | |
| // | |
| // ZeroMem() generates an ASSERT() if Buffer parameter is NULL. | |
| // Clear byte at 0 and start clear operation at address 1. | |
| // | |
| *(UINT8 *)(0) = 0; | |
| ZeroMem ((VOID *)1, (UINTN)MemoryMap[Index].RangeLength - 1); | |
| } else { | |
| ZeroMem ( | |
| (VOID *)(UINTN)MemoryMap[Index].PhysicalAddress, | |
| (UINTN)MemoryMap[Index].RangeLength | |
| ); | |
| } | |
| } | |
| } | |
| if ((MemoryMap[Index].Type == DualChannelDdrMainMemory) && | |
| (MemoryMap[Index].PhysicalAddress + MemoryMap[Index].RangeLength < MAX_ADDRESS) && | |
| (MemoryMap[Index].PhysicalAddress >= PeiMemoryBaseAddress) && | |
| (MemoryMap[Index].RangeLength >= PeiMemoryLength)) { | |
| PeiMemoryBaseAddress = MemoryMap[Index].PhysicalAddress + | |
| MemoryMap[Index].RangeLength - | |
| PeiMemoryLength; | |
| PeiMemoryIndex = Index; | |
| } | |
| } | |
| // | |
| // Find the largest memory range excluding that given to PEI. | |
| // | |
| LargeMemRangeBuf = NULL; | |
| LargeMemRangeBufLen = 0; | |
| for (Index = 0; Index < NumRanges; Index++) { | |
| if ((MemoryMap[Index].Type == DualChannelDdrMainMemory) && | |
| (MemoryMap[Index].PhysicalAddress + MemoryMap[Index].RangeLength < MAX_ADDRESS)) { | |
| if (Index != PeiMemoryIndex) { | |
| if (MemoryMap[Index].RangeLength > LargeMemRangeBufLen) { | |
| LargeMemRangeBuf = (VOID *) ((UINTN) MemoryMap[Index].PhysicalAddress); | |
| LargeMemRangeBufLen = (UINTN) MemoryMap[Index].RangeLength; | |
| } | |
| } else { | |
| if ((MemoryMap[Index].RangeLength - PeiMemoryLength) >= LargeMemRangeBufLen) { | |
| LargeMemRangeBuf = (VOID *) ((UINTN) MemoryMap[Index].PhysicalAddress); | |
| LargeMemRangeBufLen = (UINTN) (MemoryMap[Index].RangeLength - PeiMemoryLength); | |
| } | |
| } | |
| } | |
| } | |
| Capsule = NULL; | |
| CapsuleBuffer = NULL; | |
| CapsuleBufferLength = 0; | |
| if (BootMode == BOOT_ON_FLASH_UPDATE) { | |
| Status = PeiServicesLocatePpi ( | |
| &gPeiCapsulePpiGuid, // GUID | |
| 0, // INSTANCE | |
| NULL, // EFI_PEI_PPI_DESCRIPTOR | |
| (VOID **)&Capsule // PPI | |
| ); | |
| ASSERT_EFI_ERROR (Status); | |
| if (Status == EFI_SUCCESS) { | |
| CapsuleBuffer = LargeMemRangeBuf; | |
| CapsuleBufferLength = LargeMemRangeBufLen; | |
| // | |
| // Call the Capsule PPI Coalesce function to coalesce the capsule data. | |
| // | |
| Status = Capsule->Coalesce ( | |
| PeiServices, | |
| &CapsuleBuffer, | |
| &CapsuleBufferLength | |
| ); | |
| // | |
| // If it failed, then NULL out our capsule PPI pointer so that the capsule | |
| // HOB does not get created below. | |
| // | |
| if (Status != EFI_SUCCESS) { | |
| Capsule = NULL; | |
| } | |
| } | |
| } | |
| // | |
| // Set up the IMR policy required for this platform | |
| // | |
| Status = SetPlatformImrPolicy ( | |
| PeiMemoryBaseAddress, | |
| PeiMemoryLength | |
| ); | |
| ASSERT_EFI_ERROR (Status); | |
| // | |
| // Carve out the top memory reserved for ACPI | |
| // | |
| Status = PeiServicesInstallPeiMemory (PeiMemoryBaseAddress, PeiMemoryLength); | |
| ASSERT_EFI_ERROR (Status); | |
| BuildResourceDescriptorHob ( | |
| EFI_RESOURCE_SYSTEM_MEMORY, // MemoryType, | |
| ( | |
| EFI_RESOURCE_ATTRIBUTE_PRESENT | | |
| EFI_RESOURCE_ATTRIBUTE_INITIALIZED | | |
| EFI_RESOURCE_ATTRIBUTE_TESTED | | |
| EFI_RESOURCE_ATTRIBUTE_UNCACHEABLE | | |
| EFI_RESOURCE_ATTRIBUTE_WRITE_COMBINEABLE | | |
| EFI_RESOURCE_ATTRIBUTE_WRITE_THROUGH_CACHEABLE | | |
| EFI_RESOURCE_ATTRIBUTE_WRITE_BACK_CACHEABLE | |
| ), | |
| PeiMemoryBaseAddress, // MemoryBegin | |
| PeiMemoryLength // MemoryLength | |
| ); | |
| // | |
| // Install physical memory descriptor hobs for each memory range. | |
| // | |
| SmramRanges = 0; | |
| for (Index = 0; Index < NumRanges; Index++) { | |
| Attribute = 0; | |
| if (MemoryMap[Index].Type == DualChannelDdrMainMemory) | |
| { | |
| if (Index == PeiMemoryIndex) { | |
| // | |
| // This is a partially tested Main Memory range, give it to EFI | |
| // | |
| BuildResourceDescriptorHob ( | |
| EFI_RESOURCE_SYSTEM_MEMORY, | |
| ( | |
| EFI_RESOURCE_ATTRIBUTE_PRESENT | | |
| EFI_RESOURCE_ATTRIBUTE_INITIALIZED | | |
| EFI_RESOURCE_ATTRIBUTE_UNCACHEABLE | | |
| EFI_RESOURCE_ATTRIBUTE_WRITE_COMBINEABLE | | |
| EFI_RESOURCE_ATTRIBUTE_WRITE_THROUGH_CACHEABLE | | |
| EFI_RESOURCE_ATTRIBUTE_WRITE_BACK_CACHEABLE | |
| ), | |
| MemoryMap[Index].PhysicalAddress, | |
| MemoryMap[Index].RangeLength - PeiMemoryLength | |
| ); | |
| } else { | |
| // | |
| // This is an untested Main Memory range, give it to EFI | |
| // | |
| BuildResourceDescriptorHob ( | |
| EFI_RESOURCE_SYSTEM_MEMORY, // MemoryType, | |
| ( | |
| EFI_RESOURCE_ATTRIBUTE_PRESENT | | |
| EFI_RESOURCE_ATTRIBUTE_INITIALIZED | | |
| EFI_RESOURCE_ATTRIBUTE_UNCACHEABLE | | |
| EFI_RESOURCE_ATTRIBUTE_WRITE_COMBINEABLE | | |
| EFI_RESOURCE_ATTRIBUTE_WRITE_THROUGH_CACHEABLE | | |
| EFI_RESOURCE_ATTRIBUTE_WRITE_BACK_CACHEABLE | |
| ), | |
| MemoryMap[Index].PhysicalAddress, // MemoryBegin | |
| MemoryMap[Index].RangeLength // MemoryLength | |
| ); | |
| } | |
| } else { | |
| if ((MemoryMap[Index].Type == DualChannelDdrSmramCacheable) || | |
| (MemoryMap[Index].Type == DualChannelDdrSmramNonCacheable)) { | |
| SmramRanges++; | |
| } | |
| if ((MemoryMap[Index].Type == DualChannelDdrSmramNonCacheable) || | |
| (MemoryMap[Index].Type == DualChannelDdrGraphicsMemoryNonCacheable)) { | |
| Attribute |= EFI_RESOURCE_ATTRIBUTE_UNCACHEABLE; | |
| } | |
| if ((MemoryMap[Index].Type == DualChannelDdrSmramCacheable) || | |
| (MemoryMap[Index].Type == DualChannelDdrGraphicsMemoryCacheable)) { | |
| // | |
| // TSEG and HSEG can be used with a write-back(WB) cache policy; however, | |
| // the specification requires that the TSEG and HSEG space be cached only | |
| // inside of the SMI handler. when using HSEG or TSEG an IA-32 processor | |
| // does not automatically write back and invalidate its cache before entering | |
| // SMM or before existing SMM therefore any MTRR defined for the active TSEG | |
| // or HSEG must be set to un-cacheable(UC) outside of SMM. | |
| // | |
| Attribute |= EFI_RESOURCE_ATTRIBUTE_WRITE_BACK_CACHEABLE | EFI_RESOURCE_ATTRIBUTE_UNCACHEABLE; | |
| } | |
| if (MemoryMap[Index].Type == DualChannelDdrReservedMemory) { | |
| Attribute |= EFI_RESOURCE_ATTRIBUTE_WRITE_BACK_CACHEABLE | | |
| EFI_RESOURCE_ATTRIBUTE_UNCACHEABLE; | |
| } | |
| // | |
| // Make sure non-system memory is marked as reserved | |
| // | |
| BuildResourceDescriptorHob ( | |
| EFI_RESOURCE_MEMORY_RESERVED, // MemoryType, | |
| Attribute, // MemoryAttribute | |
| MemoryMap[Index].PhysicalAddress, // MemoryBegin | |
| MemoryMap[Index].RangeLength // MemoryLength | |
| ); | |
| } | |
| } | |
| // | |
| // Allocate one extra EFI_SMRAM_DESCRIPTOR to describe a page of SMRAM memory that contains a pointer | |
| // to the SMM Services Table that is required on the S3 resume path | |
| // | |
| ASSERT (SmramRanges > 0); | |
| BufferSize = sizeof (EFI_SMRAM_HOB_DESCRIPTOR_BLOCK); | |
| BufferSize += ((SmramRanges - 1) * sizeof (EFI_SMRAM_DESCRIPTOR)); | |
| Hob.Raw = BuildGuidHob ( | |
| &gEfiSmmPeiSmramMemoryReserveGuid, | |
| BufferSize | |
| ); | |
| ASSERT (Hob.Raw); | |
| SmramHobDescriptorBlock = (EFI_SMRAM_HOB_DESCRIPTOR_BLOCK *) (Hob.Raw); | |
| SmramHobDescriptorBlock->NumberOfSmmReservedRegions = SmramRanges; | |
| SmramIndex = 0; | |
| for (Index = 0; Index < NumRanges; Index++) { | |
| if ((MemoryMap[Index].Type == DualChannelDdrSmramCacheable) || | |
| (MemoryMap[Index].Type == DualChannelDdrSmramNonCacheable) | |
| ) { | |
| // | |
| // This is an SMRAM range, create an SMRAM descriptor | |
| // | |
| SmramHobDescriptorBlock->Descriptor[SmramIndex].PhysicalStart = MemoryMap[Index].PhysicalAddress; | |
| SmramHobDescriptorBlock->Descriptor[SmramIndex].CpuStart = MemoryMap[Index].CpuAddress; | |
| SmramHobDescriptorBlock->Descriptor[SmramIndex].PhysicalSize = MemoryMap[Index].RangeLength; | |
| if (MemoryMap[Index].Type == DualChannelDdrSmramCacheable) { | |
| SmramHobDescriptorBlock->Descriptor[SmramIndex].RegionState = EFI_SMRAM_CLOSED | EFI_CACHEABLE; | |
| } else { | |
| SmramHobDescriptorBlock->Descriptor[SmramIndex].RegionState = EFI_SMRAM_CLOSED; | |
| } | |
| SmramIndex++; | |
| } | |
| } | |
| // | |
| // Build a HOB with the location of the reserved memory range. | |
| // | |
| CopyMem(&DescriptorAcpiVariable, &SmramHobDescriptorBlock->Descriptor[SmramRanges-1], sizeof(EFI_SMRAM_DESCRIPTOR)); | |
| DescriptorAcpiVariable.CpuStart += RESERVED_CPU_S3_SAVE_OFFSET; | |
| BuildGuidDataHob ( | |
| &gEfiAcpiVariableGuid, | |
| &DescriptorAcpiVariable, | |
| sizeof (EFI_SMRAM_DESCRIPTOR) | |
| ); | |
| // | |
| // If we found the capsule PPI (and we didn't have errors), then | |
| // call the capsule PEIM to allocate memory for the capsule. | |
| // | |
| if (Capsule != NULL) { | |
| Status = Capsule->CreateState (PeiServices, CapsuleBuffer, CapsuleBufferLength); | |
| } | |
| return EFI_SUCCESS; | |
| } | |
| /** | |
| Find memory that is reserved so PEI has some to use. | |
| @param PeiServices PEI Services table. | |
| @param VariableSevices Variable PPI instance. | |
| @return EFI_SUCCESS The function completed successfully. | |
| Error value from LocatePpi() | |
| Error Value from VariableServices->GetVariable() | |
| **/ | |
| EFI_STATUS | |
| InstallS3Memory ( | |
| IN EFI_PEI_SERVICES **PeiServices, | |
| IN EFI_PEI_READ_ONLY_VARIABLE2_PPI *VariableServices, | |
| IN UINT32 TotalMemorySize | |
| ) | |
| { | |
| EFI_STATUS Status; | |
| UINTN S3MemoryBase; | |
| UINTN S3MemorySize; | |
| UINT8 SmramRanges; | |
| UINT8 NumRanges; | |
| UINT8 Index; | |
| UINT8 SmramIndex; | |
| UINTN BufferSize; | |
| EFI_PEI_HOB_POINTERS Hob; | |
| EFI_SMRAM_HOB_DESCRIPTOR_BLOCK *SmramHobDescriptorBlock; | |
| PEI_DUAL_CHANNEL_DDR_MEMORY_MAP_RANGE MemoryMap[MAX_RANGES]; | |
| RESERVED_ACPI_S3_RANGE *S3MemoryRangeData; | |
| EFI_SMRAM_DESCRIPTOR DescriptorAcpiVariable; | |
| // | |
| // Get the Memory Map | |
| // | |
| NumRanges = MAX_RANGES; | |
| ZeroMem (MemoryMap, sizeof (PEI_DUAL_CHANNEL_DDR_MEMORY_MAP_RANGE) * NumRanges); | |
| Status = GetMemoryMap ( | |
| PeiServices, | |
| TotalMemorySize, | |
| (PEI_DUAL_CHANNEL_DDR_MEMORY_MAP_RANGE *) MemoryMap, | |
| &NumRanges | |
| ); | |
| ASSERT_EFI_ERROR (Status); | |
| // | |
| // Install physical memory descriptor hobs for each memory range. | |
| // | |
| SmramRanges = 0; | |
| for (Index = 0; Index < NumRanges; Index++) { | |
| if ((MemoryMap[Index].Type == DualChannelDdrSmramCacheable) || | |
| (MemoryMap[Index].Type == DualChannelDdrSmramNonCacheable)) { | |
| SmramRanges++; | |
| } | |
| } | |
| ASSERT (SmramRanges > 0); | |
| // | |
| // Allocate one extra EFI_SMRAM_DESCRIPTOR to describe a page of SMRAM memory that contains a pointer | |
| // to the SMM Services Table that is required on the S3 resume path | |
| // | |
| BufferSize = sizeof (EFI_SMRAM_HOB_DESCRIPTOR_BLOCK); | |
| if (SmramRanges > 0) { | |
| BufferSize += ((SmramRanges - 1) * sizeof (EFI_SMRAM_DESCRIPTOR)); | |
| } | |
| Hob.Raw = BuildGuidHob ( | |
| &gEfiSmmPeiSmramMemoryReserveGuid, | |
| BufferSize | |
| ); | |
| ASSERT (Hob.Raw); | |
| SmramHobDescriptorBlock = (EFI_SMRAM_HOB_DESCRIPTOR_BLOCK *) (Hob.Raw); | |
| SmramHobDescriptorBlock->NumberOfSmmReservedRegions = SmramRanges; | |
| SmramIndex = 0; | |
| for (Index = 0; Index < NumRanges; Index++) { | |
| if ((MemoryMap[Index].Type == DualChannelDdrSmramCacheable) || | |
| (MemoryMap[Index].Type == DualChannelDdrSmramNonCacheable) | |
| ) { | |
| // | |
| // This is an SMRAM range, create an SMRAM descriptor | |
| // | |
| SmramHobDescriptorBlock->Descriptor[SmramIndex].PhysicalStart = MemoryMap[Index].PhysicalAddress; | |
| SmramHobDescriptorBlock->Descriptor[SmramIndex].CpuStart = MemoryMap[Index].CpuAddress; | |
| SmramHobDescriptorBlock->Descriptor[SmramIndex].PhysicalSize = MemoryMap[Index].RangeLength; | |
| if (MemoryMap[Index].Type == DualChannelDdrSmramCacheable) { | |
| SmramHobDescriptorBlock->Descriptor[SmramIndex].RegionState = EFI_SMRAM_CLOSED | EFI_CACHEABLE; | |
| } else { | |
| SmramHobDescriptorBlock->Descriptor[SmramIndex].RegionState = EFI_SMRAM_CLOSED; | |
| } | |
| SmramIndex++; | |
| } | |
| } | |
| // | |
| // Build a HOB with the location of the reserved memory range. | |
| // | |
| CopyMem(&DescriptorAcpiVariable, &SmramHobDescriptorBlock->Descriptor[SmramRanges-1], sizeof(EFI_SMRAM_DESCRIPTOR)); | |
| DescriptorAcpiVariable.CpuStart += RESERVED_CPU_S3_SAVE_OFFSET; | |
| BuildGuidDataHob ( | |
| &gEfiAcpiVariableGuid, | |
| &DescriptorAcpiVariable, | |
| sizeof (EFI_SMRAM_DESCRIPTOR) | |
| ); | |
| // | |
| // Get the location and size of the S3 memory range in the reserved page and | |
| // install it as PEI Memory. | |
| // | |
| DEBUG ((EFI_D_INFO, "TSEG Base = 0x%08x\n", SmramHobDescriptorBlock->Descriptor[SmramRanges-1].PhysicalStart)); | |
| S3MemoryRangeData = (RESERVED_ACPI_S3_RANGE*)(UINTN) | |
| (SmramHobDescriptorBlock->Descriptor[SmramRanges-1].PhysicalStart + RESERVED_ACPI_S3_RANGE_OFFSET); | |
| S3MemoryBase = (UINTN) (S3MemoryRangeData->AcpiReservedMemoryBase); | |
| DEBUG ((EFI_D_INFO, "S3MemoryBase = 0x%08x\n", S3MemoryBase)); | |
| S3MemorySize = (UINTN) (S3MemoryRangeData->AcpiReservedMemorySize); | |
| DEBUG ((EFI_D_INFO, "S3MemorySize = 0x%08x\n", S3MemorySize)); | |
| Status = PeiServicesInstallPeiMemory (S3MemoryBase, S3MemorySize); | |
| ASSERT_EFI_ERROR (Status); | |
| // | |
| // Retrieve the system memory length and build memory hob for the system | |
| // memory above 1MB. So Memory Callback can set cache for the system memory | |
| // correctly on S3 boot path, just like it does on Normal boot path. | |
| // | |
| ASSERT ((S3MemoryRangeData->SystemMemoryLength - 0x100000) > 0); | |
| BuildResourceDescriptorHob ( | |
| EFI_RESOURCE_SYSTEM_MEMORY, | |
| ( | |
| EFI_RESOURCE_ATTRIBUTE_PRESENT | | |
| EFI_RESOURCE_ATTRIBUTE_INITIALIZED | | |
| EFI_RESOURCE_ATTRIBUTE_UNCACHEABLE | | |
| EFI_RESOURCE_ATTRIBUTE_WRITE_COMBINEABLE | | |
| EFI_RESOURCE_ATTRIBUTE_WRITE_THROUGH_CACHEABLE | | |
| EFI_RESOURCE_ATTRIBUTE_WRITE_BACK_CACHEABLE | |
| ), | |
| 0x100000, | |
| S3MemoryRangeData->SystemMemoryLength - 0x100000 | |
| ); | |
| for (Index = 0; Index < NumRanges; Index++) { | |
| if ((MemoryMap[Index].Type == DualChannelDdrMainMemory) && | |
| (MemoryMap[Index].PhysicalAddress + MemoryMap[Index].RangeLength < 0x100000)) { | |
| BuildResourceDescriptorHob ( | |
| EFI_RESOURCE_SYSTEM_MEMORY, | |
| ( | |
| EFI_RESOURCE_ATTRIBUTE_PRESENT | | |
| EFI_RESOURCE_ATTRIBUTE_INITIALIZED | | |
| EFI_RESOURCE_ATTRIBUTE_UNCACHEABLE | | |
| EFI_RESOURCE_ATTRIBUTE_WRITE_COMBINEABLE | | |
| EFI_RESOURCE_ATTRIBUTE_WRITE_THROUGH_CACHEABLE | | |
| EFI_RESOURCE_ATTRIBUTE_WRITE_BACK_CACHEABLE | |
| ), | |
| MemoryMap[Index].PhysicalAddress, | |
| MemoryMap[Index].RangeLength | |
| ); | |
| DEBUG ((EFI_D_INFO, "Build resource HOB for Legacy Region on S3 patch :")); | |
| DEBUG ((EFI_D_INFO, " Memory Base:0x%lX Length:0x%lX\n", MemoryMap[Index].PhysicalAddress, MemoryMap[Index].RangeLength)); | |
| } | |
| } | |
| return EFI_SUCCESS; | |
| } | |
| /** | |
| This function returns the memory ranges to be enabled, along with information | |
| describing how the range should be used. | |
| @param PeiServices PEI Services Table. | |
| @param TimingData Detected DDR timing parameters for installed memory. | |
| @param RowConfArray Pointer to an array of EFI_DUAL_CHANNEL_DDR_ROW_CONFIG structures. The number | |
| of items in the array must match MaxRows returned by the McGetRowInfo() function. | |
| @param MemoryMap Buffer to record details of the memory ranges tobe enabled. | |
| @param NumRanges On input, this contains the maximum number of memory ranges that can be described | |
| in the MemoryMap buffer. | |
| @return MemoryMap The buffer will be filled in | |
| NumRanges will contain the actual number of memory ranges that are to be anabled. | |
| EFI_SUCCESS The function completed successfully. | |
| **/ | |
| EFI_STATUS | |
| GetMemoryMap ( | |
| IN EFI_PEI_SERVICES **PeiServices, | |
| IN UINT32 TotalMemorySize, | |
| IN OUT PEI_DUAL_CHANNEL_DDR_MEMORY_MAP_RANGE *MemoryMap, | |
| IN OUT UINT8 *NumRanges | |
| ) | |
| { | |
| EFI_PHYSICAL_ADDRESS MemorySize; | |
| EFI_PHYSICAL_ADDRESS RowLength; | |
| EFI_STATUS Status; | |
| PEI_MEMORY_RANGE_PCI_MEMORY PciMemoryMask; | |
| PEI_MEMORY_RANGE_OPTION_ROM OptionRomMask; | |
| PEI_MEMORY_RANGE_SMRAM SmramMask; | |
| PEI_MEMORY_RANGE_SMRAM TsegMask; | |
| UINT32 BlockNum; | |
| UINT8 ExtendedMemoryIndex; | |
| UINT32 Register; | |
| if ((*NumRanges) < MAX_RANGES) { | |
| return EFI_BUFFER_TOO_SMALL; | |
| } | |
| *NumRanges = 0; | |
| // | |
| // Find out which memory ranges to reserve on this platform | |
| // | |
| Status = ChooseRanges ( | |
| &OptionRomMask, | |
| &SmramMask, | |
| &PciMemoryMask | |
| ); | |
| ASSERT_EFI_ERROR (Status); | |
| // | |
| // Generate Memory ranges for the memory map. | |
| // | |
| MemorySize = 0; | |
| RowLength = TotalMemorySize; | |
| // | |
| // Add memory below 640KB to the memory map. Make sure memory between | |
| // 640KB and 1MB are reserved, even if not used for SMRAM | |
| // | |
| MemoryMap[*NumRanges].PhysicalAddress = MemorySize; | |
| MemoryMap[*NumRanges].CpuAddress = MemorySize; | |
| MemoryMap[*NumRanges].RangeLength = 0xA0000; | |
| MemoryMap[*NumRanges].Type = DualChannelDdrMainMemory; | |
| (*NumRanges)++; | |
| // | |
| // Just mark this range reserved | |
| // | |
| MemoryMap[*NumRanges].PhysicalAddress = 0xA0000; | |
| MemoryMap[*NumRanges].CpuAddress = 0xA0000; | |
| MemoryMap[*NumRanges].RangeLength = 0x60000; | |
| MemoryMap[*NumRanges].Type = DualChannelDdrReservedMemory; | |
| (*NumRanges)++; | |
| RowLength -= (0x100000 - MemorySize); | |
| MemorySize = 0x100000; | |
| // | |
| // Add remaining memory to the memory map | |
| // | |
| MemoryMap[*NumRanges].PhysicalAddress = MemorySize; | |
| MemoryMap[*NumRanges].CpuAddress = MemorySize; | |
| MemoryMap[*NumRanges].RangeLength = RowLength; | |
| MemoryMap[*NumRanges].Type = DualChannelDdrMainMemory; | |
| (*NumRanges)++; | |
| MemorySize += RowLength; | |
| ExtendedMemoryIndex = (UINT8) (*NumRanges - 1); | |
| // See if we need to trim TSEG out of the highest memory range | |
| // | |
| if (SmramMask & PEI_MR_SMRAM_TSEG_MASK) {//pcd | |
| // | |
| // Create the new range for TSEG and remove that range from the previous SdrDdrMainMemory range | |
| // | |
| TsegMask = (SmramMask & PEI_MR_SMRAM_SIZE_MASK); | |
| BlockNum = 1; | |
| while (TsegMask) { | |
| TsegMask >>= 1; | |
| BlockNum <<= 1; | |
| } | |
| BlockNum >>= 1; | |
| if (BlockNum) { | |
| MemoryMap[*NumRanges].RangeLength = (BlockNum * 128 * 1024); | |
| Register = (UINT32)((MemorySize - 1) & SMM_END_MASK); | |
| MemorySize -= MemoryMap[*NumRanges].RangeLength; | |
| MemoryMap[*NumRanges].PhysicalAddress = MemorySize; | |
| MemoryMap[*NumRanges].CpuAddress = MemorySize; | |
| MemoryMap[ExtendedMemoryIndex].RangeLength -= MemoryMap[*NumRanges].RangeLength; | |
| // | |
| // Update QuarkNcSoc HSMMCTL register | |
| // | |
| Register |= (UINT32)(((RShiftU64(MemorySize, 16)) & SMM_START_MASK) + (SMM_WRITE_OPEN | SMM_READ_OPEN | SMM_CODE_RD_OPEN)); | |
| QncHsmmcWrite (Register); | |
| } | |
| // | |
| // Chipset only supports cacheable SMRAM | |
| // | |
| MemoryMap[*NumRanges].Type = DualChannelDdrSmramCacheable; | |
| (*NumRanges)++; | |
| } | |
| // | |
| // trim 64K memory from highest memory range for Rmu Main binary shadow | |
| // | |
| MemoryMap[*NumRanges].RangeLength = 0x10000; | |
| MemorySize -= MemoryMap[*NumRanges].RangeLength; | |
| MemoryMap[*NumRanges].PhysicalAddress = MemorySize; | |
| MemoryMap[*NumRanges].CpuAddress = MemorySize; | |
| MemoryMap[ExtendedMemoryIndex].RangeLength -= MemoryMap[*NumRanges].RangeLength; | |
| MemoryMap[*NumRanges].Type = DualChannelDdrReservedMemory; | |
| (*NumRanges)++; | |
| return EFI_SUCCESS; | |
| } | |
| /** | |
| Routine Description: | |
| Fill in bit masks to specify reserved memory ranges on the Lakeport platform | |
| Arguments: | |
| Returns: | |
| OptionRomMask - Bit mask specifying memory regions reserved for Legacy option | |
| ROM use (if any) | |
| SmramMask - Bit mask specifying memory regions reserved for SMM use (if any) | |
| **/ | |
| EFI_STATUS | |
| ChooseRanges ( | |
| IN OUT PEI_MEMORY_RANGE_OPTION_ROM *OptionRomMask, | |
| IN OUT PEI_MEMORY_RANGE_SMRAM *SmramMask, | |
| IN OUT PEI_MEMORY_RANGE_PCI_MEMORY *PciMemoryMask | |
| ) | |
| { | |
| // | |
| // Choose regions to reserve for Option ROM use | |
| // | |
| *OptionRomMask = PEI_MR_OPTION_ROM_NONE; | |
| // | |
| // Choose regions to reserve for SMM use (AB/H SEG and TSEG). Size is in 128K blocks | |
| // | |
| *SmramMask = PEI_MR_SMRAM_CACHEABLE_MASK | PEI_MR_SMRAM_TSEG_MASK | ((PcdGet32(PcdTSegSize)) >> 17); | |
| *PciMemoryMask = 0; | |
| return EFI_SUCCESS; | |
| } | |
| EFI_STATUS | |
| GetPlatformMemorySize ( | |
| IN EFI_PEI_SERVICES **PeiServices, | |
| IN EFI_BOOT_MODE BootMode, | |
| IN OUT UINT64 *MemorySize | |
| ) | |
| { | |
| EFI_STATUS Status; | |
| EFI_PEI_READ_ONLY_VARIABLE2_PPI *Variable; | |
| UINTN DataSize; | |
| EFI_MEMORY_TYPE_INFORMATION MemoryData [EfiMaxMemoryType + 1]; | |
| UINTN Index; | |
| DataSize = sizeof (MemoryData); | |
| if (BootMode == BOOT_IN_RECOVERY_MODE) { | |
| // | |
| // // Treat recovery as if variable not found (eg 1st boot). | |
| // | |
| Status = EFI_NOT_FOUND; | |
| } else { | |
| Status = PeiServicesLocatePpi ( | |
| &gEfiPeiReadOnlyVariable2PpiGuid, | |
| 0, | |
| NULL, | |
| (VOID **)&Variable | |
| ); | |
| ASSERT_EFI_ERROR (Status); | |
| DataSize = sizeof (MemoryData); | |
| Status = Variable->GetVariable ( | |
| Variable, | |
| EFI_MEMORY_TYPE_INFORMATION_VARIABLE_NAME, | |
| &gEfiMemoryTypeInformationGuid, | |
| NULL, | |
| &DataSize, | |
| &MemoryData | |
| ); | |
| } | |
| // | |
| // Accumulate maximum amount of memory needed | |
| // | |
| if (EFI_ERROR (Status)) { | |
| // | |
| // Start with minimum memory | |
| // | |
| *MemorySize = PEI_MIN_MEMORY_SIZE; | |
| for (Index = 0; Index < sizeof(mDefaultQncMemoryTypeInformation) / sizeof (EFI_MEMORY_TYPE_INFORMATION); Index++) { | |
| *MemorySize += mDefaultQncMemoryTypeInformation[Index].NumberOfPages * EFI_PAGE_SIZE; | |
| } | |
| // | |
| // Build the GUID'd HOB for DXE | |
| // | |
| BuildGuidDataHob ( | |
| &gEfiMemoryTypeInformationGuid, | |
| mDefaultQncMemoryTypeInformation, | |
| sizeof(mDefaultQncMemoryTypeInformation) | |
| ); | |
| } else { | |
| // | |
| // Start with at least PEI_MIN_MEMORY_SIZE pages of memory for the DXE Core and the DXE Stack | |
| // | |
| *MemorySize = PEI_MIN_MEMORY_SIZE; | |
| for (Index = 0; Index < DataSize / sizeof (EFI_MEMORY_TYPE_INFORMATION); Index++) { | |
| DEBUG ((EFI_D_INFO, "Index %d, Page: %d\n", Index, MemoryData[Index].NumberOfPages)); | |
| *MemorySize += MemoryData[Index].NumberOfPages * EFI_PAGE_SIZE; | |
| } | |
| // | |
| // Build the GUID'd HOB for DXE | |
| // | |
| BuildGuidDataHob ( | |
| &gEfiMemoryTypeInformationGuid, | |
| MemoryData, | |
| DataSize | |
| ); | |
| } | |
| return EFI_SUCCESS; | |
| } | |
| EFI_STATUS | |
| BaseMemoryTest ( | |
| IN EFI_PEI_SERVICES **PeiServices, | |
| IN EFI_PHYSICAL_ADDRESS BeginAddress, | |
| IN UINT64 MemoryLength, | |
| IN PEI_MEMORY_TEST_OP Operation, | |
| OUT EFI_PHYSICAL_ADDRESS *ErrorAddress | |
| ) | |
| { | |
| UINT32 TestPattern; | |
| EFI_PHYSICAL_ADDRESS TempAddress; | |
| UINT32 SpanSize; | |
| TestPattern = 0x5A5A5A5A; | |
| SpanSize = 0; | |
| // | |
| // Make sure we don't try and test anything above the max physical address range | |
| // | |
| ASSERT (BeginAddress + MemoryLength < MAX_ADDRESS); | |
| switch (Operation) { | |
| case Extensive: | |
| SpanSize = 0x4; | |
| break; | |
| case Sparse: | |
| case Quick: | |
| SpanSize = 0x40000; | |
| break; | |
| case Ignore: | |
| goto Done; | |
| break; | |
| } | |
| // | |
| // Write the test pattern into memory range | |
| // | |
| TempAddress = BeginAddress; | |
| while (TempAddress < BeginAddress + MemoryLength) { | |
| (*(UINT32 *) (UINTN) TempAddress) = TestPattern; | |
| TempAddress += SpanSize; | |
| } | |
| // | |
| // Read pattern from memory and compare it | |
| // | |
| TempAddress = BeginAddress; | |
| while (TempAddress < BeginAddress + MemoryLength) { | |
| if ((*(UINT32 *) (UINTN) TempAddress) != TestPattern) { | |
| *ErrorAddress = TempAddress; | |
| DEBUG ((EFI_D_ERROR, "Memory test failed at 0x%x.\n", TempAddress)); | |
| return EFI_DEVICE_ERROR; | |
| } | |
| TempAddress += SpanSize; | |
| } | |
| Done: | |
| return EFI_SUCCESS; | |
| } | |
| /** | |
| This function sets up the platform specific IMR protection for the various | |
| memory regions. | |
| @param PeiMemoryBaseAddress Base address of memory allocated for PEI. | |
| @param PeiMemoryLength Length in bytes of the PEI memory (includes ACPI memory). | |
| @return EFI_SUCCESS The function completed successfully. | |
| EFI_ACCESS_DENIED Access to IMRs failed. | |
| **/ | |
| EFI_STATUS | |
| SetPlatformImrPolicy ( | |
| IN EFI_PHYSICAL_ADDRESS PeiMemoryBaseAddress, | |
| IN UINT64 PeiMemoryLength | |
| ) | |
| { | |
| UINT8 Index; | |
| UINT32 Register; | |
| UINT16 DeviceId; | |
| // | |
| // Check what Soc we are running on (read Host bridge DeviceId) | |
| // | |
| DeviceId = QNCMmPci16(0, MC_BUS, MC_DEV, MC_FUN, PCI_DEVICE_ID_OFFSET); | |
| // | |
| // If any IMR register is locked then we cannot proceed | |
| // | |
| for (Index = (QUARK_NC_MEMORY_MANAGER_IMR0+QUARK_NC_MEMORY_MANAGER_IMRXL); Index <=(QUARK_NC_MEMORY_MANAGER_IMR7+QUARK_NC_MEMORY_MANAGER_IMRXL); Index=Index+4) | |
| { | |
| Register = QNCPortRead (QUARK_NC_MEMORY_MANAGER_SB_PORT_ID, Index); | |
| if (Register & IMR_LOCK) { | |
| return EFI_ACCESS_DENIED; | |
| } | |
| } | |
| // | |
| // Add IMR2 protection for shadowed RMU binary. | |
| // | |
| QncImrWrite ( | |
| QUARK_NC_MEMORY_MANAGER_IMR2, | |
| (UINT32)(((RShiftU64((PeiMemoryBaseAddress+PeiMemoryLength), 8)) & IMRH_MASK) | IMR_EN), | |
| (UINT32)((RShiftU64((PeiMemoryBaseAddress+PeiMemoryLength+PcdGet32(PcdFlashQNCMicrocodeSize)-1), 8)) & IMRH_MASK), | |
| (UINT32)(CPU_SNOOP + RMU + CPU0_NON_SMM), | |
| (UINT32)(CPU_SNOOP + RMU + CPU0_NON_SMM) | |
| ); | |
| // | |
| // Add IMR3 protection for the default SMRAM. | |
| // | |
| QncImrWrite ( | |
| QUARK_NC_MEMORY_MANAGER_IMR3, | |
| (UINT32)(((RShiftU64((SMM_DEFAULT_SMBASE), 8)) & IMRL_MASK) | IMR_EN), | |
| (UINT32)((RShiftU64((SMM_DEFAULT_SMBASE+SMM_DEFAULT_SMBASE_SIZE_BYTES-1), 8)) & IMRH_MASK), | |
| (UINT32)(CPU_SNOOP + CPU0_NON_SMM), | |
| (UINT32)(CPU_SNOOP + CPU0_NON_SMM) | |
| ); | |
| // | |
| // Enable IMR4 protection of eSRAM. | |
| // | |
| QncImrWrite ( | |
| QUARK_NC_MEMORY_MANAGER_IMR4, | |
| (UINT32)(((RShiftU64((UINTN)PcdGet32 (PcdEsramStage1Base), 8)) & IMRL_MASK) | IMR_EN), | |
| (UINT32)((RShiftU64(((UINTN)PcdGet32 (PcdEsramStage1Base) + (UINTN)PcdGet32 (PcdESramMemorySize) - 1), 8)) & IMRH_MASK), | |
| (UINT32)(CPU_SNOOP + CPU0_NON_SMM), | |
| (UINT32)(CPU_SNOOP + CPU0_NON_SMM) | |
| ); | |
| // | |
| // Enable Interrupt on IMR/SMM Violation | |
| // | |
| QNCPortWrite (QUARK_NC_MEMORY_MANAGER_SB_PORT_ID, QUARK_NC_MEMORY_MANAGER_BIMRVCTL, (UINT32)(EnableIMRInt)); | |
| if (DeviceId == QUARK2_MC_DEVICE_ID) { | |
| QNCPortWrite (QUARK_NC_MEMORY_MANAGER_SB_PORT_ID, QUARK_NC_MEMORY_MANAGER_BSMMVCTL, (UINT32)(EnableSMMInt)); | |
| } | |
| // | |
| // Disable IMR7 memory protection (eSRAM + DDR3 memory) since our policies | |
| // are now setup. | |
| // | |
| QncImrWrite ( | |
| QUARK_NC_MEMORY_MANAGER_IMR7, | |
| (UINT32)(IMRL_RESET & ~IMR_EN), | |
| (UINT32)IMRH_RESET, | |
| (UINT32)IMRX_ALL_ACCESS, | |
| (UINT32)IMRX_ALL_ACCESS | |
| ); | |
| return EFI_SUCCESS; | |
| } | |
| /** Return info derived from Installing Memory by MemoryInit. | |
| @param[out] RmuMainBaseAddressPtr Return RmuMainBaseAddress to this location. | |
| @param[out] SmramDescriptorPtr Return start of Smram descriptor list to this location. | |
| @param[out] NumSmramRegionsPtr Return numbers of Smram regions to this location. | |
| @return Address of RMU shadow region at the top of available memory. | |
| @return List of Smram descriptors for each Smram region. | |
| @return Numbers of Smram regions. | |
| **/ | |
| VOID | |
| EFIAPI | |
| InfoPostInstallMemory ( | |
| OUT UINT32 *RmuMainBaseAddressPtr OPTIONAL, | |
| OUT EFI_SMRAM_DESCRIPTOR **SmramDescriptorPtr OPTIONAL, | |
| OUT UINTN *NumSmramRegionsPtr OPTIONAL | |
| ) | |
| { | |
| EFI_STATUS Status; | |
| EFI_PEI_HOB_POINTERS Hob; | |
| UINT64 CalcLength; | |
| EFI_SMRAM_HOB_DESCRIPTOR_BLOCK *SmramHobDescriptorBlock; | |
| if ((RmuMainBaseAddressPtr == NULL) && (SmramDescriptorPtr == NULL) && (NumSmramRegionsPtr == NULL)) { | |
| return; | |
| } | |
| SmramHobDescriptorBlock = NULL; | |
| if (SmramDescriptorPtr != NULL) { | |
| *SmramDescriptorPtr = NULL; | |
| } | |
| if (NumSmramRegionsPtr != NULL) { | |
| *NumSmramRegionsPtr = 0; | |
| } | |
| // | |
| // Calculate RMU shadow region base address. | |
| // Set to 1 MB. Since 1MB cacheability will always be set | |
| // until override by CSM. | |
| // | |
| CalcLength = 0x100000; | |
| Status = PeiServicesGetHobList ((VOID **) &Hob.Raw); | |
| ASSERT_EFI_ERROR (Status); | |
| while (!END_OF_HOB_LIST (Hob)) { | |
| if (Hob.Header->HobType == EFI_HOB_TYPE_RESOURCE_DESCRIPTOR) { | |
| if (Hob.ResourceDescriptor->ResourceType == EFI_RESOURCE_SYSTEM_MEMORY) { | |
| // | |
| // Skip the memory region below 1MB | |
| // | |
| if (Hob.ResourceDescriptor->PhysicalStart >= 0x100000) { | |
| CalcLength += (UINT64) (Hob.ResourceDescriptor->ResourceLength); | |
| } | |
| } | |
| } else if (Hob.Header->HobType == EFI_HOB_TYPE_GUID_EXTENSION) { | |
| if (CompareGuid (&(Hob.Guid->Name), &gEfiSmmPeiSmramMemoryReserveGuid)) { | |
| SmramHobDescriptorBlock = (VOID*) (Hob.Raw + sizeof (EFI_HOB_GUID_TYPE)); | |
| if (SmramDescriptorPtr != NULL) { | |
| *SmramDescriptorPtr = SmramHobDescriptorBlock->Descriptor; | |
| } | |
| if (NumSmramRegionsPtr != NULL) { | |
| *NumSmramRegionsPtr = SmramHobDescriptorBlock->NumberOfSmmReservedRegions; | |
| } | |
| } | |
| } | |
| Hob.Raw = GET_NEXT_HOB (Hob); | |
| } | |
| if (RmuMainBaseAddressPtr != NULL) { | |
| *RmuMainBaseAddressPtr = (UINT32) CalcLength; | |
| } | |
| } |