From a19b07fec13e713004e722f439e1ed7bc2e6ebd0 Mon Sep 17 00:00:00 2001 From: Patrick Rudolph Date: Thu, 21 Feb 2019 12:11:14 +0100 Subject: security/memory: Clear memory in ramstage * Add architecture independend way of clearing all DRAM * Implemented in ramstage as MTRRs need to be set to speed up clearing. Takes up to 15 seconds per GiB otherwise. * Use memset_pae on x86 * Add quirks for FSP1.0 Tested on P8H61M-Pro: * Clears 4GiB in less than 1 second Tested on wedge100s: * Clears 8GiB in 2 seconds Change-Id: Idaadb8fb438e5b95557c0f65a14534e8762fde20 Signed-off-by: Patrick Rudolph Reviewed-on: https://review.coreboot.org/c/coreboot/+/31550 Reviewed-by: Philipp Deppenwiese Tested-by: build bot (Jenkins) --- src/arch/x86/include/arch/memory_clear.h | 24 +++++ src/security/memory/Kconfig | 2 + src/security/memory/Makefile.inc | 2 + src/security/memory/memory_clear.c | 160 +++++++++++++++++++++++++++++++ 4 files changed, 188 insertions(+) create mode 100644 src/arch/x86/include/arch/memory_clear.h create mode 100644 src/security/memory/memory_clear.c diff --git a/src/arch/x86/include/arch/memory_clear.h b/src/arch/x86/include/arch/memory_clear.h new file mode 100644 index 0000000000..87ad7ada85 --- /dev/null +++ b/src/arch/x86/include/arch/memory_clear.h @@ -0,0 +1,24 @@ +/* + * This file is part of the coreboot project. + * + * Copyright (C) 2019 9elements Agency GmbH + * Copyright (C) 2019 Facebook Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef MEMORY_CLEAR_H +#define MEMORY_CLEAR_H + +#include + +int arch_clear_memranges(const struct memranges *mem_reserved); + +#endif /* MEMORY_CLEAR_H */ diff --git a/src/security/memory/Kconfig b/src/security/memory/Kconfig index 5436119ba5..d84b80d382 100644 --- a/src/security/memory/Kconfig +++ b/src/security/memory/Kconfig @@ -17,7 +17,9 @@ menu "Memory initialization" config PLATFORM_HAS_DRAM_CLEAR bool + default y if ARCH_X86 default n + depends on RELOCATABLE_RAMSTAGE help Selected by platforms that support clearing all DRAM after DRAM initialization. diff --git a/src/security/memory/Makefile.inc b/src/security/memory/Makefile.inc index 525c4dbb4d..0882ca3660 100644 --- a/src/security/memory/Makefile.inc +++ b/src/security/memory/Makefile.inc @@ -1,3 +1,5 @@ romstage-$(CONFIG_PLATFORM_HAS_DRAM_CLEAR) += memory.c postcar-$(CONFIG_PLATFORM_HAS_DRAM_CLEAR) += memory.c ramstage-$(CONFIG_PLATFORM_HAS_DRAM_CLEAR) += memory.c + +ramstage-$(CONFIG_PLATFORM_HAS_DRAM_CLEAR) += memory_clear.c diff --git a/src/security/memory/memory_clear.c b/src/security/memory/memory_clear.c new file mode 100644 index 0000000000..638c41a929 --- /dev/null +++ b/src/security/memory/memory_clear.c @@ -0,0 +1,160 @@ +/* + * This file is part of the coreboot project. + * + * Copyright (C) 2019 9elements Agency GmbH + * Copyright (C) 2019 Facebook Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#if CONFIG(ARCH_X86) +#include +#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 +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* 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, as RELOCATABLE_RAMSTAGE is a requirement, no need + * to separately protect stack or heap */ + + cbmem_get_region(&baseptr, &size); + memranges_insert(&mem, (uintptr_t)baseptr, size, BM_MEM_TABLE); + + if (CONFIG(PLATFORM_USES_FSP1_0)) { + /* Protect CBMEM pointer */ + memranges_insert(&mem, CBMEM_FSP_HOB_PTR, sizeof(void *), + BM_MEM_TABLE); + } + + if (CONFIG(ARCH_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 (!(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 (CONFIG(ARCH_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 (CONFIG(ARCH_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); -- cgit v1.2.3