/* SPDX-License-Identifier: GPL-2.0-only */ #if ENV_X86 #include <cpu/x86/pae.h> #else #define memset_pae(a, b, c, d, e) 0 #define MEMSET_PAE_PGTL_ALIGN 0 #define MEMSET_PAE_PGTL_SIZE 0 #define MEMSET_PAE_PGTL_SIZE 0 #define MEMSET_PAE_VMEM_ALIGN 0 #endif #include <memrange.h> #include <bootmem.h> #include <bootstate.h> #include <symbols.h> #include <console/console.h> #include <arch/memory_clear.h> #include <string.h> #include <security/memory/memory.h> #include <cbmem.h> #include <acpi/acpi.h> /* Helper to find free space for memset_pae. */ static uintptr_t get_free_memory_range(struct memranges *mem, const resource_t align, const resource_t size) { const struct range_entry *r; /* Find a spot for virtual memory address */ memranges_each_entry(r, mem) { if (range_entry_tag(r) != BM_MEM_RAM) continue; if (ALIGN_UP(range_entry_base(r) + size, align) + size > range_entry_end(r)) continue; return ALIGN_UP(range_entry_base(r) + size, align); } printk(BIOS_ERR, "%s: Couldn't find free memory range\n", __func__); return 0; } /* * Clears all memory regions marked as BM_MEM_RAM. * Uses memset_pae if the memory region can't be accessed by memset and * architecture is x86. * * @return 0 on success, 1 on error */ static void clear_memory(void *unused) { const struct range_entry *r; struct memranges mem; uintptr_t pgtbl, vmem_addr; if (acpi_is_wakeup_s3()) return; if (!security_clear_dram_request()) return; /* FSP1.0 is marked as MMIO and won't appear here */ memranges_init(&mem, IORESOURCE_MEM | IORESOURCE_FIXED | IORESOURCE_STORED | IORESOURCE_ASSIGNED | IORESOURCE_CACHEABLE, IORESOURCE_MEM | IORESOURCE_FIXED | IORESOURCE_STORED | IORESOURCE_ASSIGNED | IORESOURCE_CACHEABLE, BM_MEM_RAM); /* Add reserved entries */ void *baseptr = NULL; size_t size = 0; /* Only skip CBMEM, stage program, stack and heap are included there. */ cbmem_get_region(&baseptr, &size); memranges_insert(&mem, (uintptr_t)baseptr, size, BM_MEM_TABLE); if (ENV_X86) { /* Find space for PAE enabled memset */ pgtbl = get_free_memory_range(&mem, MEMSET_PAE_PGTL_ALIGN, MEMSET_PAE_PGTL_SIZE); /* Don't touch page tables while clearing */ memranges_insert(&mem, pgtbl, MEMSET_PAE_PGTL_SIZE, BM_MEM_TABLE); vmem_addr = get_free_memory_range(&mem, MEMSET_PAE_VMEM_ALIGN, MEMSET_PAE_PGTL_SIZE); printk(BIOS_SPEW, "%s: pgtbl at %p, virt memory at %p\n", __func__, (void *)pgtbl, (void *)vmem_addr); } /* Now clear all useable DRAM */ memranges_each_entry(r, &mem) { if (range_entry_tag(r) != BM_MEM_RAM) continue; printk(BIOS_DEBUG, "%s: Clearing DRAM %016llx-%016llx\n", __func__, range_entry_base(r), range_entry_end(r)); /* Does regular memset work? */ if (sizeof(resource_t) == sizeof(void *) || !(range_entry_end(r) >> (sizeof(void *) * 8))) { /* fastpath */ memset((void *)(uintptr_t)range_entry_base(r), 0, range_entry_size(r)); } /* Use PAE if available */ else if (ENV_X86) { if (memset_pae(range_entry_base(r), 0, range_entry_size(r), (void *)pgtbl, (void *)vmem_addr)) printk(BIOS_ERR, "%s: Failed to memset " "memory\n", __func__); } else { printk(BIOS_ERR, "%s: Failed to memset memory\n", __func__); } } if (ENV_X86) { /* Clear previously skipped memory reserved for pagetables */ printk(BIOS_DEBUG, "%s: Clearing DRAM %016lx-%016lx\n", __func__, pgtbl, pgtbl + MEMSET_PAE_PGTL_SIZE); memset((void *)pgtbl, 0, MEMSET_PAE_PGTL_SIZE); } memranges_teardown(&mem); } /* After DEV_INIT as MTRRs needs to be configured on x86 */ BOOT_STATE_INIT_ENTRY(BS_DEV_INIT, BS_ON_EXIT, clear_memory, NULL);