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
This commit is contained in:
Mitchell Horne 2025-02-28 20:04:38 -04:00
parent f32a255044
commit 125877c92d
4 changed files with 206 additions and 170 deletions

View file

@ -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();
}

View file

@ -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

178
sys/kern/subr_efi_map.c Normal file
View file

@ -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);
}

22
sys/sys/efi_map.h Normal file
View file

@ -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_ */