| /* SPDX-License-Identifier: GPL-2.0-only */ |
| |
| #include <bootstate.h> |
| #include <stdint.h> |
| #include <stdbool.h> |
| #include <SilCommon.h> |
| #include <Sil-api.h> // needed above ApobCmn.h |
| #include <ApobCmn.h> |
| #include <device/device.h> |
| #include <xPRF-api.h> |
| #include <cpu/amd/mtrr.h> |
| #include <cbmem.h> |
| #include <amdblocks/memmap.h> |
| |
| #include "opensil.h" |
| |
| static const char *hole_info_type(MEMORY_HOLE_TYPES type) |
| { |
| const struct hole_type { |
| MEMORY_HOLE_TYPES type; |
| const char *string; |
| } types[] = { |
| {UMA, "UMA"}, |
| {MMIO, "MMIO"}, |
| {PrivilegedDRAM, "PrivilegedDRAM"}, |
| {Reserved1TbRemap, "Reserved1TbRemap"}, |
| {ReservedSLink, "ReservedSLink"}, |
| {ReservedSLinkAlignment, "ReservedSLinkAlignment"}, |
| {ReservedDrtm, "ReservedDrtm"}, |
| {ReservedCvip, "ReservedCvip"}, |
| {ReservedSmuFeatures, "ReservedSmuFeatures"}, |
| {ReservedFwtpm, "ReservedFwtpm"}, |
| {ReservedMpioC20, "ReservedMpioC20"}, |
| {ReservedNbif, "ReservedNbif"}, |
| {ReservedCxl, "ReservedCxl"}, |
| {ReservedCxlAlignment, "ReservedCxlAlignment"}, |
| {ReservedCpuTmr, "ReservedCpuTmr"}, |
| {ReservedRasEinj, "ReservedRasEinj"}, |
| {MaxMemoryHoleTypes, "MaxMemoryHoleTypes"}, |
| }; |
| |
| int i; |
| for (i = 0; i < ARRAY_SIZE(types); i++) |
| if (type == types[i].type) |
| break; |
| if (i == ARRAY_SIZE(types)) |
| return "Unknown type"; |
| return types[i].string; |
| } |
| |
| static uint64_t top_of_mem; |
| static uint32_t n_holes; |
| static MEMORY_HOLE_DESCRIPTOR *hole_info; |
| |
| static void get_hole_info(void) |
| { |
| static bool done; |
| if (done) |
| return; |
| SIL_STATUS status = xPrfGetSystemMemoryMap(&n_holes, &top_of_mem, (void **)&hole_info); |
| SIL_STATUS_report("xPrfGetSystemMemoryMap", status); |
| // Make sure hole_info does not get initialized to something odd by xPRF on failure |
| if (status != SilPass) |
| hole_info = NULL; |
| done = true; |
| } |
| |
| |
| static void print_memory_holes(void *unused) |
| { |
| get_hole_info(); |
| if (hole_info == NULL) |
| return; |
| |
| printk(BIOS_DEBUG, "APOB: top of memory 0x%016llx\n", top_of_mem); |
| printk(BIOS_DEBUG, "The following holes are reported in APOB\n"); |
| for (int hole = 0; hole < n_holes; hole++) { |
| printk(BIOS_DEBUG, " Base: 0x%016llx, Size: 0x%016llx, Type: %02d:%s\n", |
| hole_info[hole].Base, hole_info[hole].Size, hole_info[hole].Type, |
| hole_info_type(hole_info[hole].Type)); |
| } |
| } |
| |
| BOOT_STATE_INIT_ENTRY(BS_DEV_RESOURCES, BS_ON_ENTRY, print_memory_holes, NULL); |
| |
| // This assumes holes are allocated |
| unsigned long add_opensil_memmap(struct device *dev, unsigned long idx) |
| { |
| ram_from_to(dev, idx++, 0, 0xa0000); |
| mmio_from_to(dev, idx++, 0xa0000, 0xc0000); // legacy VGA |
| reserved_ram_from_to(dev, idx++, 0xc0000, 1 * MiB); // Option ROM |
| |
| uint32_t mem_usable = (uintptr_t)cbmem_top(); |
| uintptr_t early_reserved_dram_start, early_reserved_dram_end; |
| const struct memmap_early_dram *e = memmap_get_early_dram_usage(); |
| |
| early_reserved_dram_start = e->base; |
| early_reserved_dram_end = e->base + e->size; |
| |
| // 1MB - bottom of DRAM reserved for early coreboot usage |
| ram_from_to(dev, idx++, 1 * MiB, early_reserved_dram_start); |
| |
| // DRAM reserved for early coreboot usage |
| reserved_ram_from_to(dev, idx++, early_reserved_dram_start, |
| early_reserved_dram_end); |
| |
| // top of DRAM consumed early - low top usable RAM |
| // cbmem_top() accounts for low UMA and TSEG if they are used. |
| ram_from_to(dev, idx++, early_reserved_dram_end, |
| mem_usable); |
| |
| // Account for UMA and TSEG |
| const uint32_t top_mem = ALIGN_DOWN(rdmsr(TOP_MEM).lo, 1 * MiB); |
| if (mem_usable != top_mem) |
| reserved_ram_from_to(dev, idx++, mem_usable, top_mem); |
| |
| mmconf_resource(dev, idx++); |
| |
| // Check if we're done |
| if (top_of_mem <= 0x100000000) |
| return idx; |
| |
| // Holes in upper DRAM |
| // This assumes all the holes in upper DRAM are continuous |
| get_hole_info(); |
| if (hole_info == NULL) |
| return idx; |
| uint64_t lowest_upper_hole_base = top_of_mem; |
| uint64_t highest_upper_hole_end = 0x100000000; |
| for (int hole = 0; hole < n_holes; hole++) { |
| if (hole_info[hole].Type == MMIO) |
| continue; |
| if (hole_info[hole].Base < 0x100000000) |
| continue; |
| lowest_upper_hole_base = MIN(lowest_upper_hole_base, hole_info[hole].Base); |
| highest_upper_hole_end = MAX(highest_upper_hole_end, hole_info[hole].Base + hole_info[hole].Size); |
| if (hole_info[hole].Type == UMA) |
| mmio_range(dev, idx++, hole_info[hole].Base, hole_info[hole].Size); |
| else |
| reserved_ram_range(dev, idx++, hole_info[hole].Base, hole_info[hole].Size); |
| } |
| |
| ram_from_to(dev, idx++, 0x100000000, lowest_upper_hole_base); |
| |
| // Do we need this? |
| if (top_of_mem > highest_upper_hole_end) |
| ram_from_to(dev, idx++, highest_upper_hole_end, top_of_mem); |
| |
| return idx; |
| } |