The branch main has been updated by mhorne: URL: https://cgit.FreeBSD.org/src/commit/?id=125877c92d1661c6a1f567aa9a3e9c399a514789
commit 125877c92d1661c6a1f567aa9a3e9c399a514789 Author: Mitchell Horne <mho...@freebsd.org> AuthorDate: 2025-03-01 00:04:38 +0000 Commit: Mitchell Horne <mho...@freebsd.org> CommitDate: 2025-03-03 16:12:15 +0000 arm64: Split EFI map parsing code to a common file Motivation: I would like to enable the EFI memory map parsing on riscv, which is preferable to parsing memory regions from the device tree. I could easily duplicate the functionality, but it can be shared without much extra effort. There are a couple of similar implementations existing already. In this commit, the arm64 version of the code is moved to the new file, and enabled for this platform. No functional change intended. armv7 and riscv will follow. amd64 could be converted to use this interface too, but it doesn't use the common "physmem" interfaces for managing physical memory, and would therefore require further tweaks. I am not inclined to make this change at this time. Reviewed by: imp, andrew Discussed with: emaste (copyright attribution) Sponsored by: The FreeBSD Foundation Differential Revision: https://reviews.freebsd.org/D47746 --- sys/arm64/arm64/machdep.c | 175 ++------------------------------------------- sys/conf/files.arm64 | 1 + sys/kern/subr_efi_map.c | 178 ++++++++++++++++++++++++++++++++++++++++++++++ sys/sys/efi_map.h | 22 ++++++ 4 files changed, 206 insertions(+), 170 deletions(-) diff --git a/sys/arm64/arm64/machdep.c b/sys/arm64/arm64/machdep.c index 22c74b21cf48..a94cdaa61775 100644 --- a/sys/arm64/arm64/machdep.c +++ b/sys/arm64/arm64/machdep.c @@ -40,6 +40,7 @@ #include <sys/csan.h> #include <sys/devmap.h> #include <sys/efi.h> +#include <sys/efi_map.h> #include <sys/exec.h> #include <sys/imgact.h> #include <sys/kdb.h> @@ -457,172 +458,6 @@ arm64_get_writable_addr(void *addr, void **out) return (false); } -typedef void (*efi_map_entry_cb)(struct efi_md *, void *argp); - -static void -foreach_efi_map_entry(struct efi_map_header *efihdr, efi_map_entry_cb cb, void *argp) -{ - struct efi_md *map, *p; - size_t efisz; - int ndesc, i; - - /* - * Memory map data provided by UEFI via the GetMemoryMap - * Boot Services API. - */ - efisz = (sizeof(struct efi_map_header) + 0xf) & ~0xf; - map = (struct efi_md *)((uint8_t *)efihdr + efisz); - - if (efihdr->descriptor_size == 0) - return; - ndesc = efihdr->memory_size / efihdr->descriptor_size; - - for (i = 0, p = map; i < ndesc; i++, - p = efi_next_descriptor(p, efihdr->descriptor_size)) { - cb(p, argp); - } -} - -/* - * Handle the EFI memory map list. - * - * We will make two passes at this, the first (exclude == false) to populate - * physmem with valid physical memory ranges from recognized map entry types. - * In the second pass we will exclude memory ranges from physmem which must not - * be used for general allocations, either because they are used by runtime - * firmware or otherwise reserved. - * - * Adding the runtime-reserved memory ranges to physmem and excluding them - * later ensures that they are included in the DMAP, but excluded from - * phys_avail[]. - * - * Entry types not explicitly listed here are ignored and not mapped. - */ -static void -handle_efi_map_entry(struct efi_md *p, void *argp) -{ - bool exclude = *(bool *)argp; - - switch (p->md_type) { - case EFI_MD_TYPE_RECLAIM: - /* - * The recomended location for ACPI tables. Map into the - * DMAP so we can access them from userspace via /dev/mem. - */ - case EFI_MD_TYPE_RT_CODE: - /* - * Some UEFI implementations put the system table in the - * runtime code section. Include it in the DMAP, but will - * be excluded from phys_avail. - */ - case EFI_MD_TYPE_RT_DATA: - /* - * Runtime data will be excluded after the DMAP - * region is created to stop it from being added - * to phys_avail. - */ - if (exclude) { - physmem_exclude_region(p->md_phys, - p->md_pages * EFI_PAGE_SIZE, EXFLAG_NOALLOC); - break; - } - /* FALLTHROUGH */ - case EFI_MD_TYPE_CODE: - case EFI_MD_TYPE_DATA: - case EFI_MD_TYPE_BS_CODE: - case EFI_MD_TYPE_BS_DATA: - case EFI_MD_TYPE_FREE: - /* - * We're allowed to use any entry with these types. - */ - if (!exclude) - physmem_hardware_region(p->md_phys, - p->md_pages * EFI_PAGE_SIZE); - break; - default: - /* Other types shall not be handled by physmem. */ - break; - } -} - -static void -add_efi_map_entries(struct efi_map_header *efihdr) -{ - bool exclude = false; - foreach_efi_map_entry(efihdr, handle_efi_map_entry, &exclude); -} - -static void -exclude_efi_map_entries(struct efi_map_header *efihdr) -{ - bool exclude = true; - foreach_efi_map_entry(efihdr, handle_efi_map_entry, &exclude); -} - -static void -print_efi_map_entry(struct efi_md *p, void *argp __unused) -{ - const char *type; - static const char *types[] = { - "Reserved", - "LoaderCode", - "LoaderData", - "BootServicesCode", - "BootServicesData", - "RuntimeServicesCode", - "RuntimeServicesData", - "ConventionalMemory", - "UnusableMemory", - "ACPIReclaimMemory", - "ACPIMemoryNVS", - "MemoryMappedIO", - "MemoryMappedIOPortSpace", - "PalCode", - "PersistentMemory" - }; - - if (p->md_type < nitems(types)) - type = types[p->md_type]; - else - type = "<INVALID>"; - printf("%23s %012lx %012lx %08lx ", type, p->md_phys, - p->md_virt, p->md_pages); - if (p->md_attr & EFI_MD_ATTR_UC) - printf("UC "); - if (p->md_attr & EFI_MD_ATTR_WC) - printf("WC "); - if (p->md_attr & EFI_MD_ATTR_WT) - printf("WT "); - if (p->md_attr & EFI_MD_ATTR_WB) - printf("WB "); - if (p->md_attr & EFI_MD_ATTR_UCE) - printf("UCE "); - if (p->md_attr & EFI_MD_ATTR_WP) - printf("WP "); - if (p->md_attr & EFI_MD_ATTR_RP) - printf("RP "); - if (p->md_attr & EFI_MD_ATTR_XP) - printf("XP "); - if (p->md_attr & EFI_MD_ATTR_NV) - printf("NV "); - if (p->md_attr & EFI_MD_ATTR_MORE_RELIABLE) - printf("MORE_RELIABLE "); - if (p->md_attr & EFI_MD_ATTR_RO) - printf("RO "); - if (p->md_attr & EFI_MD_ATTR_RT) - printf("RUNTIME"); - printf("\n"); -} - -static void -print_efi_map_entries(struct efi_map_header *efihdr) -{ - - printf("%23s %12s %12s %8s %4s\n", - "Type", "Physical", "Virtual", "#Pages", "Attr"); - foreach_efi_map_entry(efihdr, print_efi_map_entry, NULL); -} - /* * Map the passed in VA in EFI space to a void * using the efi memory table to * find the PA and return it in the DMAP, if it exists. We're used between the @@ -659,7 +494,7 @@ efi_early_map(vm_offset_t va) { struct early_map_data emd = { .va = va }; - foreach_efi_map_entry(efihdr, efi_early_map_entry, &emd); + efi_map_foreach_entry(efihdr, efi_early_map_entry, &emd); if (emd.pa == 0) return NULL; return (void *)PHYS_TO_DMAP(emd.pa); @@ -942,7 +777,7 @@ initarm(struct arm64_bootparams *abp) efihdr = (struct efi_map_header *)preload_search_info(preload_kmdp, MODINFO_METADATA | MODINFOMD_EFI_MAP); if (efihdr != NULL) - add_efi_map_entries(efihdr); + efi_map_add_entries(efihdr); #ifdef FDT else { /* Grab physical memory regions information from device tree. */ @@ -972,7 +807,7 @@ initarm(struct arm64_bootparams *abp) pmap_bootstrap(lastaddr - KERNBASE); /* Exclude entries needed in the DMAP region, but not phys_avail */ if (efihdr != NULL) - exclude_efi_map_entries(efihdr); + efi_map_exclude_entries(efihdr); /* Do the same for reserve entries in the EFI MEMRESERVE table */ if (efi_systbl_phys != 0) exclude_efi_memreserve(efi_systbl_phys); @@ -1051,7 +886,7 @@ initarm(struct arm64_bootparams *abp) if (boothowto & RB_VERBOSE) { if (efihdr != NULL) - print_efi_map_entries(efihdr); + efi_map_print_entries(efihdr); physmem_print_tables(); } diff --git a/sys/conf/files.arm64 b/sys/conf/files.arm64 index 13c7d9b1875d..de3134ad9124 100644 --- a/sys/conf/files.arm64 +++ b/sys/conf/files.arm64 @@ -6,6 +6,7 @@ kern/msi_if.m optional intrng kern/pic_if.m optional intrng kern/subr_devmap.c standard +kern/subr_efi_map.c standard kern/subr_intr.c optional intrng kern/subr_physmem.c standard libkern/strlen.c standard diff --git a/sys/kern/subr_efi_map.c b/sys/kern/subr_efi_map.c new file mode 100644 index 000000000000..29a94858f6c1 --- /dev/null +++ b/sys/kern/subr_efi_map.c @@ -0,0 +1,178 @@ +/* + * Copyright (c) 2014 The FreeBSD Foundation + * Copyright (c) 2018 Andrew Turner + * + * SPDX-License-Identifier: BSD-2-Clause + */ +#include <sys/param.h> +#include <sys/systm.h> +#include <sys/efi.h> +#include <sys/efi_map.h> +#include <sys/physmem.h> + +#include <machine/efi.h> +#include <machine/vmparam.h> + +void +efi_map_foreach_entry(struct efi_map_header *efihdr, efi_map_entry_cb cb, void *argp) +{ + struct efi_md *map, *p; + size_t efisz; + int ndesc, i; + + /* + * Memory map data provided by UEFI via the GetMemoryMap + * Boot Services API. + */ + efisz = (sizeof(struct efi_map_header) + 0xf) & ~0xf; + map = (struct efi_md *)((uint8_t *)efihdr + efisz); + + if (efihdr->descriptor_size == 0) + return; + ndesc = efihdr->memory_size / efihdr->descriptor_size; + + for (i = 0, p = map; i < ndesc; i++, + p = efi_next_descriptor(p, efihdr->descriptor_size)) { + cb(p, argp); + } +} + +/* + * Handle the EFI memory map list. + * + * We will make two passes at this, the first (exclude == false) to populate + * physmem with valid physical memory ranges from recognized map entry types. + * In the second pass we will exclude memory ranges from physmem which must not + * be used for general allocations, either because they are used by runtime + * firmware or otherwise reserved. + * + * Adding the runtime-reserved memory ranges to physmem and excluding them + * later ensures that they are included in the DMAP, but excluded from + * phys_avail[]. + * + * Entry types not explicitly listed here are ignored and not mapped. + */ +static void +handle_efi_map_entry(struct efi_md *p, void *argp) +{ + bool exclude = *(bool *)argp; + + switch (p->md_type) { + case EFI_MD_TYPE_RECLAIM: + /* + * The recomended location for ACPI tables. Map into the + * DMAP so we can access them from userspace via /dev/mem. + */ + case EFI_MD_TYPE_RT_CODE: + /* + * Some UEFI implementations put the system table in the + * runtime code section. Include it in the DMAP, but will + * be excluded from phys_avail. + */ + case EFI_MD_TYPE_RT_DATA: + /* + * Runtime data will be excluded after the DMAP + * region is created to stop it from being added + * to phys_avail. + */ + if (exclude) { + physmem_exclude_region(p->md_phys, + p->md_pages * EFI_PAGE_SIZE, EXFLAG_NOALLOC); + break; + } + /* FALLTHROUGH */ + case EFI_MD_TYPE_CODE: + case EFI_MD_TYPE_DATA: + case EFI_MD_TYPE_BS_CODE: + case EFI_MD_TYPE_BS_DATA: + case EFI_MD_TYPE_FREE: + /* + * We're allowed to use any entry with these types. + */ + if (!exclude) + physmem_hardware_region(p->md_phys, + p->md_pages * EFI_PAGE_SIZE); + break; + default: + /* Other types shall not be handled by physmem. */ + break; + } +} + +void +efi_map_add_entries(struct efi_map_header *efihdr) +{ + bool exclude = false; + efi_map_foreach_entry(efihdr, handle_efi_map_entry, &exclude); +} + +void +efi_map_exclude_entries(struct efi_map_header *efihdr) +{ + bool exclude = true; + efi_map_foreach_entry(efihdr, handle_efi_map_entry, &exclude); +} + +static void +print_efi_map_entry(struct efi_md *p, void *argp __unused) +{ + const char *type; + static const char *types[] = { + "Reserved", + "LoaderCode", + "LoaderData", + "BootServicesCode", + "BootServicesData", + "RuntimeServicesCode", + "RuntimeServicesData", + "ConventionalMemory", + "UnusableMemory", + "ACPIReclaimMemory", + "ACPIMemoryNVS", + "MemoryMappedIO", + "MemoryMappedIOPortSpace", + "PalCode", + "PersistentMemory" + }; + + if (p->md_type < nitems(types)) + type = types[p->md_type]; + else + type = "<INVALID>"; + printf("%23s %012lx %012lx %08lx ", type, p->md_phys, + p->md_virt, p->md_pages); + if (p->md_attr & EFI_MD_ATTR_UC) + printf("UC "); + if (p->md_attr & EFI_MD_ATTR_WC) + printf("WC "); + if (p->md_attr & EFI_MD_ATTR_WT) + printf("WT "); + if (p->md_attr & EFI_MD_ATTR_WB) + printf("WB "); + if (p->md_attr & EFI_MD_ATTR_UCE) + printf("UCE "); + if (p->md_attr & EFI_MD_ATTR_WP) + printf("WP "); + if (p->md_attr & EFI_MD_ATTR_RP) + printf("RP "); + if (p->md_attr & EFI_MD_ATTR_XP) + printf("XP "); + if (p->md_attr & EFI_MD_ATTR_NV) + printf("NV "); + if (p->md_attr & EFI_MD_ATTR_MORE_RELIABLE) + printf("MORE_RELIABLE "); + if (p->md_attr & EFI_MD_ATTR_RO) + printf("RO "); + if (p->md_attr & EFI_MD_ATTR_RT) + printf("RUNTIME"); + printf("\n"); +} + +void +efi_map_print_entries(struct efi_map_header *efihdr) +{ + + printf("%23s %12s %12s %8s %4s\n", + "Type", "Physical", "Virtual", "#Pages", "Attr"); + efi_map_foreach_entry(efihdr, print_efi_map_entry, NULL); +} diff --git a/sys/sys/efi_map.h b/sys/sys/efi_map.h new file mode 100644 index 000000000000..4edabd4e5812 --- /dev/null +++ b/sys/sys/efi_map.h @@ -0,0 +1,22 @@ +/* + * Copyright (c) 2014 The FreeBSD Foundation + * Copyright (c) 2018 Andrew Turner + * + * SPDX-License-Identifier: BSD-2-Clause + */ +#ifndef _SYS_EFI_MAP_H_ +#define _SYS_EFI_MAP_H_ + +#include <sys/efi.h> +#include <machine/metadata.h> + +typedef void (*efi_map_entry_cb)(struct efi_md *, void *argp); + +void efi_map_foreach_entry(struct efi_map_header *efihdr, efi_map_entry_cb cb, + void *argp); + +void efi_map_add_entries(struct efi_map_header *efihdr); +void efi_map_exclude_entries(struct efi_map_header *efihdr); +void efi_map_print_entries(struct efi_map_header *efihdr); + +#endif /* !_SYS_EFI_MAP_H_ */