From: Michael Brown Date: Wed, 14 May 2025 21:19:54 +0000 (+0100) Subject: [memmap] Define an API for managing the system memory map X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=bab3d7671790dd6a318cc5b72000419c5061e86f;p=thirdparty%2Fipxe.git [memmap] Define an API for managing the system memory map Define a generic system memory map API, based on the abstraction created for parsing the FDT memory map and adding a concept of hidden in-use memory regions as required to support patching the BIOS INT 15 memory map. Signed-off-by: Michael Brown --- diff --git a/src/config/defaults/efi.h b/src/config/defaults/efi.h index 0ba9f3ee7..ada08973e 100644 --- a/src/config/defaults/efi.h +++ b/src/config/defaults/efi.h @@ -16,6 +16,7 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #define CONSOLE_EFI #define TIMER_EFI #define UMALLOC_EFI +#define MEMMAP_NULL #define SMBIOS_EFI #define SANBOOT_EFI #define BOFM_EFI diff --git a/src/config/defaults/linux.h b/src/config/defaults/linux.h index fae144b3d..bb14fd9a7 100644 --- a/src/config/defaults/linux.h +++ b/src/config/defaults/linux.h @@ -13,6 +13,7 @@ FILE_LICENCE ( GPL2_OR_LATER ); #define TIMER_LINUX #define UACCESS_LINUX #define UMALLOC_LINUX +#define MEMMAP_NULL #define NAP_LINUX #define SMBIOS_LINUX #define SANBOOT_DUMMY diff --git a/src/config/defaults/pcbios.h b/src/config/defaults/pcbios.h index 13532e429..bdc62e8e0 100644 --- a/src/config/defaults/pcbios.h +++ b/src/config/defaults/pcbios.h @@ -17,6 +17,7 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #define CONSOLE_PCBIOS #define NAP_PCBIOS #define UMALLOC_MEMTOP +#define MEMMAP_NULL #define SMBIOS_PCBIOS #define SANBOOT_PCBIOS #define ENTROPY_RTC diff --git a/src/config/defaults/sbi.h b/src/config/defaults/sbi.h index 268bf0afe..e12cf1af9 100644 --- a/src/config/defaults/sbi.h +++ b/src/config/defaults/sbi.h @@ -19,6 +19,7 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #define CONSOLE_SBI #define REBOOT_SBI #define UMALLOC_SBI +#define MEMMAP_NULL #define ACPI_NULL #define MPAPI_NULL diff --git a/src/core/memmap.c b/src/core/memmap.c new file mode 100644 index 000000000..5b95b6c0a --- /dev/null +++ b/src/core/memmap.c @@ -0,0 +1,114 @@ +/* + * Copyright (C) 2025 Michael Brown . + * + * 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; either version 2 of the + * License, or (at your option) any later version. + * + * 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. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. + */ + +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); + +#include +#include + +/** @file + * + * System memory map + * + */ + +/** + * Update memory region descriptor + * + * @v region Memory region of interest to be updated + * @v start Start address of known region + * @v size Size of known region + * @v flags Flags for known region + * @v name Name of known region (for debugging) + * + * Update a memory region descriptor based on a known existent region. + */ +void memmap_update ( struct memmap_region *region, uint64_t start, + uint64_t size, unsigned int flags, const char *name ) { + uint64_t last; + + /* Sanity check */ + assert ( region->last >= region->addr ); + + /* Ignore empty regions */ + if ( ! size ) + return; + + /* Calculate last addresses (and truncate if necessary) */ + last = ( start + size - 1 ); + if ( last < start ) { + last = ~( ( uint64_t ) 0 ); + DBGC ( region, "MEMMAP [%#08llx,%#08llx] %s truncated " + "(invalid size %#08llx)\n", + ( ( unsigned long long ) start ), + ( ( unsigned long long ) last ), name, + ( ( unsigned long long ) size ) ); + } + + /* Ignore regions entirely below the region of interest */ + if ( last < region->addr ) + return; + + /* Ignore regions entirely above the region of interest */ + if ( start > region->last ) + return; + + /* Update region of interest as applicable */ + if ( start <= region->addr ) { + + /* Record this region as covering the region of interest */ + region->flags |= flags; + if ( name ) + region->name = name; + + /* Update last address if no closer boundary exists */ + if ( last < region->last ) + region->last = last; + + } else if ( start < region->last ) { + + /* Update last address if no closer boundary exists */ + region->last = ( start - 1 ); + } + + /* Sanity check */ + assert ( region->last >= region->addr ); +} + +/** + * Update memory region descriptor based on all in-use memory regions + * + * @v region Memory region of interest to be updated + */ +void memmap_update_used ( struct memmap_region *region ) { + struct used_region *used; + + /* Update descriptor to hide all in-use regions */ + for_each_table_entry ( used, USED_REGIONS ) { + memmap_update ( region, used->start, used->size, + MEMMAP_FL_USED, used->name ); + } +} + +PROVIDE_MEMMAP_INLINE ( null, memmap_describe ); +PROVIDE_MEMMAP_INLINE ( null, memmap_sync ); diff --git a/src/include/bits/memmap.h b/src/include/bits/memmap.h new file mode 100644 index 000000000..d51ee4993 --- /dev/null +++ b/src/include/bits/memmap.h @@ -0,0 +1,15 @@ +#ifndef _BITS_MEMMAP_H +#define _BITS_MEMMAP_H + +/** @file + * + * Dummy architecture-specific system memory map + * + * This file is included only if the architecture does not provide its + * own version of this file. + * + */ + +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); + +#endif /* _BITS_MEMMAP_H */ diff --git a/src/include/ipxe/memmap.h b/src/include/ipxe/memmap.h new file mode 100644 index 000000000..382032ee9 --- /dev/null +++ b/src/include/ipxe/memmap.h @@ -0,0 +1,235 @@ +#ifndef _IPXE_MEMMAP_H +#define _IPXE_MEMMAP_H + +/** @file + * + * System memory map + * + */ + +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); + +#include +#include +#include +#include +#include + +/** + * Calculate static inline memory map API function name + * + * @v _prefix Subsystem prefix + * @v _api_func API function + * @ret _subsys_func Subsystem API function + */ +#define MEMMAP_INLINE( _subsys, _api_func ) \ + SINGLE_API_INLINE ( MEMMAP_PREFIX_ ## _subsys, _api_func ) + +/** + * Provide a memory map API implementation + * + * @v _prefix Subsystem prefix + * @v _api_func API function + * @v _func Implementing function + */ +#define PROVIDE_MEMMAP( _subsys, _api_func, _func ) \ + PROVIDE_SINGLE_API ( MEMMAP_PREFIX_ ## _subsys, _api_func, _func ) + +/** + * Provide a static inline memory map API implementation + * + * @v _prefix Subsystem prefix + * @v _api_func API function + */ +#define PROVIDE_MEMMAP_INLINE( _subsys, _api_func ) \ + PROVIDE_SINGLE_API_INLINE ( MEMMAP_PREFIX_ ## _subsys, _api_func ) + +/** A memory region descriptor */ +struct memmap_region { + /** The address being described */ + uint64_t addr; + /** Last address with the same description + * + * Note that this is the last address with the same + * description as the address being described, not the first + * address with a different description. + */ + uint64_t last; + /** Region flags */ + unsigned int flags; + /** Region name (for debug messages) */ + const char *name; +}; + +#define MEMMAP_FL_MEMORY 0x0001 /**< Contains memory */ +#define MEMMAP_FL_RESERVED 0x0002 /**< Is reserved */ +#define MEMMAP_FL_USED 0x0004 /**< Is in use by iPXE */ +#define MEMMAP_FL_INACCESSIBLE 0x0008 /**< Outside of addressable range */ + +/** + * Initialise memory region descriptor + * + * @v addr Address within region + * @v region Region descriptor to fill in + */ +static inline __attribute__ (( always_inline )) void +memmap_init ( uint64_t addr, struct memmap_region *region ) { + + region->addr = addr; + region->last = ~( ( uint64_t ) 0 ); + region->flags = 0; + region->name = NULL; +} + +/** + * Check if memory region is usable + * + * @v region Region descriptor + * @ret is_usable Memory region is usable + */ +static inline __attribute__ (( always_inline )) int +memmap_is_usable ( const struct memmap_region *region ) { + + return ( region->flags == MEMMAP_FL_MEMORY ); +} + +/** + * Get remaining size of memory region (from the described address upwards) + * + * @v region Region descriptor + * @ret size Size of memory region + */ +static inline __attribute__ (( always_inline )) uint64_t +memmap_size ( const struct memmap_region *region ) { + + /* Calculate size, assuming overflow is known to be impossible */ + return ( ( region->last + 1 ) - region->addr ); +} + +/** An in-use memory region */ +struct used_region { + /** Region name */ + const char *name; + /** Start address */ + physaddr_t start; + /** Length of region */ + size_t size; +}; + +/** In-use memory region table */ +#define USED_REGIONS __table ( struct used_region, "used_regions" ) + +/** Declare an in-use memory region */ +#define __used_region __table_entry ( USED_REGIONS, 01 ) + +/* Include all architecture-independent ACPI API headers */ +#include + +/* Include all architecture-dependent ACPI API headers */ +#include + +/** + * Describe memory region from system memory map + * + * @v addr Address within region + * @v hide Hide in-use regions from the memory map + * @v region Region descriptor to fill in + */ +void memmap_describe ( uint64_t addr, int hide, struct memmap_region *region ); + +/** + * Synchronise in-use regions with the externally visible system memory map + * + * In environments such as x86 BIOS, we need to patch the global + * system memory map to hide our in-use regions, since there is no + * other way to communicate this information to external code. + */ +void memmap_sync ( void ); + +/** + * Update an in-use memory region + * + * @v used In-use memory region + * @v start Start address + * @v size Length of region + */ +static inline __attribute__ (( always_inline )) void +memmap_use ( struct used_region *used, physaddr_t start, size_t size ) { + + /* Record region */ + used->start = start; + used->size = size; + + /* Synchronise externally visible memory map */ + memmap_sync(); +} + +/** + * Iterate over memory regions from a given starting address + * + * @v region Region descriptor + * @v start Starting address + * @v hide Hide in-use regions from the memory map + */ +#define for_each_memmap_from( region, start, hide ) \ + for ( (region)->addr = (start), (region)->last = 0 ; \ + ( ( ( (region)->last + 1 ) != 0 ) && \ + ( memmap_describe ( (region)->addr, (hide), \ + (region) ), 1 ) ) ; \ + (region)->addr = ( (region)->last + 1 ) ) + +/** + * Iterate over memory regions + * + * @v region Region descriptor + * @v hide Hide in-use regions from the memory map + */ +#define for_each_memmap( region, hide ) \ + for_each_memmap_from ( (region), 0, (hide) ) + + +/** + * Dump memory region descriptor (for debugging) + * + * @v region Region descriptor + */ +static inline void memmap_dump ( const struct memmap_region *region ) { + const char *name = region->name; + unsigned int flags = region->flags; + + /* Dump region information */ + DBGC ( region, "MEMMAP (%s%s%s%s) [%#08llx,%#08llx]%s%s\n", + ( ( flags & MEMMAP_FL_MEMORY ) ? "M" : "-" ), + ( ( flags & MEMMAP_FL_RESERVED ) ? "R" : "-" ), + ( ( flags & MEMMAP_FL_USED ) ? "U" : "-" ), + ( ( flags & MEMMAP_FL_INACCESSIBLE ) ? "X" : "-" ), + ( ( unsigned long long ) region->addr ), + ( ( unsigned long long ) region->last ), + ( name ? " " : "" ), ( name ? name : "" ) ); +} + +/** + * Dump system memory map (for debugging) + * + * @v hide Hide in-use regions from the memory map + */ +static inline void memmap_dump_all ( int hide ) { + struct memmap_region region; + + /* Do nothing unless debugging is enabled */ + if ( ! DBG_LOG ) + return; + + /* Describe all memory regions */ + DBGC ( ®ion, "MEMMAP with in-use regions %s:\n", + ( hide ? "hidden" : "ignored" ) ); + for_each_memmap ( ®ion, hide ) + memmap_dump ( ®ion ); +} + +extern void memmap_update ( struct memmap_region *region, uint64_t start, + uint64_t size, unsigned int flags, + const char *name ); +extern void memmap_update_used ( struct memmap_region *region ); + +#endif /* _IPXE_MEMMAP_H */ diff --git a/src/include/ipxe/null_memmap.h b/src/include/ipxe/null_memmap.h new file mode 100644 index 000000000..43c0f2a29 --- /dev/null +++ b/src/include/ipxe/null_memmap.h @@ -0,0 +1,44 @@ +#ifndef _IPXE_NULL_MEMMAP_H +#define _IPXE_NULL_MEMMAP_H + +#include + +/** @file + * + * Null system memory map API + * + */ + +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); + +#ifdef MEMMAP_NULL +#define MEMMAP_PREFIX_null +#else +#define MEMMAP_PREFIX_null __null_ +#endif + +/** + * Describe memory region from system memory map + * + * @v addr Address within region + * @v hide Hide in-use regions from the memory map + * @v region Region descriptor to fill in + */ +static inline __attribute__ (( always_inline )) void +MEMMAP_INLINE ( null, memmap_describe ) ( uint64_t addr, int hide __unused, + struct memmap_region *region ) { + + /* Initialise region as empty */ + memmap_init ( addr, region ); +} + +/** + * Synchronise in-use regions with the externally visible system memory map + * + */ +static inline __attribute__ (( always_inline )) void +MEMMAP_INLINE ( null, memmap_sync ) ( void ) { + /* Nothing to do */ +} + +#endif /* _IPXE_NULL_MEMMAP_H */