From 4c4c94ca09473ecbfce7e47edd0012949f4d25a3 Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Thu, 15 May 2025 01:21:48 +0100 Subject: [PATCH] [bios] Update to use the generic system memory map API Provide an implementation of the system memory map API based on the assorted BIOS INT 15 calls, and a temporary implementation of the legacy get_memmap() function using the new API. Signed-off-by: Michael Brown --- src/arch/x86/include/bits/memmap.h | 14 ++ src/arch/x86/include/ipxe/int15.h | 20 +++ src/arch/x86/interface/pcbios/e820mangler.S | 8 ++ src/arch/x86/interface/pcbios/hidemem.c | 16 +++ src/arch/x86/interface/pcbios/int15.c | 136 +++++++++++--------- src/config/defaults/pcbios.h | 2 +- src/core/memmap.c | 25 ++++ 7 files changed, 157 insertions(+), 64 deletions(-) create mode 100644 src/arch/x86/include/bits/memmap.h create mode 100644 src/arch/x86/include/ipxe/int15.h diff --git a/src/arch/x86/include/bits/memmap.h b/src/arch/x86/include/bits/memmap.h new file mode 100644 index 000000000..8f821563c --- /dev/null +++ b/src/arch/x86/include/bits/memmap.h @@ -0,0 +1,14 @@ +#ifndef _BITS_MEMMAP_H +#define _BITS_MEMMAP_H + +/** @file + * + * x86-specific system memory map API implementations + * + */ + +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); + +#include + +#endif /* _BITS_MEMMAP_H */ diff --git a/src/arch/x86/include/ipxe/int15.h b/src/arch/x86/include/ipxe/int15.h new file mode 100644 index 000000000..fbd70f8d6 --- /dev/null +++ b/src/arch/x86/include/ipxe/int15.h @@ -0,0 +1,20 @@ +#ifndef _IPXE_INT15_H +#define _IPXE_INT15_H + +/** @file + * + * INT15-based memory map + * + */ + +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); + +#ifdef MEMMAP_INT15 +#define MEMMAP_PREFIX_int15 +#else +#define MEMMAP_PREFIX_int15 __int15_ +#endif + +extern void int15_intercept ( int intercept ); + +#endif /* _IPXE_INT15_H */ diff --git a/src/arch/x86/interface/pcbios/e820mangler.S b/src/arch/x86/interface/pcbios/e820mangler.S index ef5dc2754..b9e891dff 100644 --- a/src/arch/x86/interface/pcbios/e820mangler.S +++ b/src/arch/x86/interface/pcbios/e820mangler.S @@ -564,6 +564,8 @@ int15_88: int15: /* See if we want to intercept this call */ pushfw + cmpb $0, %cs:int15_intercept_flag + je 3f cmpw $0xe820, %ax jne 1f cmpl $SMAP, %edx @@ -587,3 +589,9 @@ int15: int15_vector: .long 0 .size int15_vector, . - int15_vector + + .section ".text16.data", "aw", @progbits + .globl int15_intercept_flag +int15_intercept_flag: + .byte 1 + .size int15_intercept_flag, . - int15_intercept_flag diff --git a/src/arch/x86/interface/pcbios/hidemem.c b/src/arch/x86/interface/pcbios/hidemem.c index 4063c0551..f201742cf 100644 --- a/src/arch/x86/interface/pcbios/hidemem.c +++ b/src/arch/x86/interface/pcbios/hidemem.c @@ -30,6 +30,7 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include #include #include +#include #include /** Set to true if you want to test a fake E820 map */ @@ -73,6 +74,10 @@ extern void int15(); extern struct segoff __text16 ( int15_vector ); #define int15_vector __use_text16 ( int15_vector ) +/** INT 15 interception flag */ +extern uint8_t __text16 ( int15_intercept_flag ); +#define int15_intercept_flag __use_text16 ( int15_intercept_flag ) + /* The linker defines these symbols for us */ extern char _textdata[]; extern char _etextdata[]; @@ -131,6 +136,17 @@ void hide_textdata ( void ) { virt_to_phys ( _etextdata ) ); } +/** + * Set INT 15 interception flag + * + * @v intercept Intercept INT 15 calls to modify memory map + */ +void int15_intercept ( int intercept ) { + + /* Set flag for INT 15 handler */ + int15_intercept_flag = intercept; +} + /** * Hide Etherboot * diff --git a/src/arch/x86/interface/pcbios/int15.c b/src/arch/x86/interface/pcbios/int15.c index 3bc1229aa..ff90f551c 100644 --- a/src/arch/x86/interface/pcbios/int15.c +++ b/src/arch/x86/interface/pcbios/int15.c @@ -26,10 +26,12 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include #include #include +#include #include #include #include #include +#include /** * @file @@ -152,7 +154,7 @@ static unsigned int extmemsize_88 ( void ) { * @ret extmem Extended memory size, in kB * * Note that this is only an approximation; for an accurate picture, - * use the E820 memory map obtained via get_memmap(); + * use the E820 memory map obtained via memmap_describe(); */ unsigned int extmemsize ( void ) { unsigned int extmem_e801; @@ -167,12 +169,13 @@ unsigned int extmemsize ( void ) { /** * Get e820 memory map * - * @v memmap Memory map to fill in + * @v region Memory region of interest to be updated * @ret rc Return status code */ -static int meme820 ( struct memory_map *memmap ) { - struct memory_region *region = memmap->regions; - struct memory_region *prev_region = NULL; +static int meme820 ( struct memmap_region *region ) { + unsigned int count = 0; + uint64_t start = 0; + uint64_t len = 0; uint32_t next = 0; uint32_t smap; uint32_t size; @@ -226,13 +229,6 @@ static int meme820 ( struct memory_map *memmap ) { break; } - /* If first region is not RAM, assume map is invalid */ - if ( ( memmap->count == 0 ) && - ( e820buf.type != E820_TYPE_RAM ) ) { - DBG ( "INT 15,e820 failed, first entry not RAM\n" ); - return -EINVAL; - } - DBG ( "INT 15,e820 region [%llx,%llx) type %d", e820buf.start, ( e820buf.start + e820buf.len ), ( int ) e820buf.type ); @@ -259,27 +255,36 @@ static int meme820 ( struct memory_map *memmap ) { continue; } - region->start = e820buf.start; - region->end = e820buf.start + e820buf.len; - /* Check for adjacent regions and merge them */ - if ( prev_region && ( region->start == prev_region->end ) ) { - prev_region->end = region->end; + if ( e820buf.start == ( start + len ) ) { + len += e820buf.len; } else { - prev_region = region; - region++; - memmap->count++; + start = e820buf.start; + len = e820buf.len; } - if ( memmap->count >= ( sizeof ( memmap->regions ) / - sizeof ( memmap->regions[0] ) ) ) { - DBG ( "INT 15,e820 too many regions returned\n" ); - /* Not a fatal error; what we've got so far at - * least represents valid regions of memory, - * even if we couldn't get them all. - */ - break; + /* Sanity check: first region (base memory) should + * start at address zero. + */ + if ( ( count == 0 ) && ( start != 0 ) ) { + DBG ( "INT 15,e820 region 0 starts at %llx (expected " + "0); assuming insane\n", start ); + return -EINVAL; + } + + /* Sanity check: second region (extended memory) + * should start at address 0x100000. + */ + if ( ( count == 1 ) && ( start != 0x100000 ) ) { + DBG ( "INT 15,e820 region 1 starts at %llx (expected " + "100000); assuming insane\n", start ); + return -EINVAL; } + + /* Update region of interest */ + memmap_update ( region, start, len, MEMMAP_FL_MEMORY, "e820" ); + count++; + } while ( next != 0 ); /* Sanity checks. Some BIOSes report complete garbage via INT @@ -288,19 +293,9 @@ static int meme820 ( struct memory_map *memmap ) { * region (starting at 0) and at least one high memory region * (starting at 0x100000). */ - if ( memmap->count < 2 ) { + if ( count < 2 ) { DBG ( "INT 15,e820 returned only %d regions; assuming " - "insane\n", memmap->count ); - return -EINVAL; - } - if ( memmap->regions[0].start != 0 ) { - DBG ( "INT 15,e820 region 0 starts at %llx (expected 0); " - "assuming insane\n", memmap->regions[0].start ); - return -EINVAL; - } - if ( memmap->regions[1].start != 0x100000 ) { - DBG ( "INT 15,e820 region 1 starts at %llx (expected 100000); " - "assuming insane\n", memmap->regions[0].start ); + "insane\n", count ); return -EINVAL; } @@ -308,37 +303,52 @@ static int meme820 ( struct memory_map *memmap ) { } /** - * Get memory map + * Describe memory region from system memory map * - * @v memmap Memory map to fill in + * @v addr Address within region + * @v hide Hide in-use regions from the memory map + * @v region Region descriptor to fill in */ -void x86_get_memmap ( struct memory_map *memmap ) { - unsigned int basemem, extmem; +static void int15_describe ( uint64_t addr, int hide, + struct memmap_region *region ) { + unsigned int basemem; + unsigned int extmem; + uint64_t inaccessible; int rc; - DBG ( "Fetching system memory map\n" ); + /* Initialise region */ + memmap_init ( addr, region ); - /* Clear memory map */ - memset ( memmap, 0, sizeof ( *memmap ) ); + /* Mark addresses above 4GB as inaccessible: we have no way to + * access them either in a 32-bit build or in a 64-bit build + * (since the 64-bit build identity-maps only the 32-bit + * address space). + */ + inaccessible = ( 1ULL << 32 ); + memmap_update ( region, inaccessible, -inaccessible, + MEMMAP_FL_INACCESSIBLE, NULL ); - /* Get base and extended memory sizes */ - basemem = basememsize(); - DBG ( "FBMS base memory size %d kB [0,%x)\n", - basemem, ( basemem * 1024 ) ); - extmem = extmemsize(); - - /* Try INT 15,e820 first */ - if ( ( rc = meme820 ( memmap ) ) == 0 ) { + /* Enable/disable INT 15 interception as applicable */ + int15_intercept ( hide ); + + /* Try INT 15,e820 first, falling back to constructing a map + * from basemem and extmem sizes + */ + if ( ( rc = meme820 ( region ) ) == 0 ) { DBG ( "Obtained system memory map via INT 15,e820\n" ); - return; + } else { + basemem = basememsize(); + DBG ( "FBMS base memory size %d kB [0,%x)\n", + basemem, ( basemem * 1024 ) ); + extmem = extmemsize(); + memmap_update ( region, 0, ( basemem * 1024 ), + MEMMAP_FL_MEMORY, "basemem" ); + memmap_update ( region, 0x100000, ( extmem * 1024 ), + MEMMAP_FL_MEMORY, "extmem" ); } - /* Fall back to constructing a map from basemem and extmem sizes */ - DBG ( "INT 15,e820 failed; constructing map\n" ); - memmap->regions[0].end = ( basemem * 1024 ); - memmap->regions[1].start = 0x100000; - memmap->regions[1].end = 0x100000 + ( extmem * 1024 ); - memmap->count = 2; + /* Restore INT 15 interception */ + int15_intercept ( 1 ); } -PROVIDE_IOAPI ( x86, get_memmap, x86_get_memmap ); +PROVIDE_MEMMAP ( int15, memmap_describe, int15_describe ); diff --git a/src/config/defaults/pcbios.h b/src/config/defaults/pcbios.h index bdc62e8e0..0f25669e4 100644 --- a/src/config/defaults/pcbios.h +++ b/src/config/defaults/pcbios.h @@ -17,7 +17,7 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #define CONSOLE_PCBIOS #define NAP_PCBIOS #define UMALLOC_MEMTOP -#define MEMMAP_NULL +#define MEMMAP_INT15 #define SMBIOS_PCBIOS #define SANBOOT_PCBIOS #define ENTROPY_RTC diff --git a/src/core/memmap.c b/src/core/memmap.c index 5b95b6c0a..a6ba5205e 100644 --- a/src/core/memmap.c +++ b/src/core/memmap.c @@ -24,6 +24,7 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include +#include #include /** @file @@ -110,5 +111,29 @@ void memmap_update_used ( struct memmap_region *region ) { } } +/** + * Get legacy system memory map + * + * @v memmap Legacy memory map to fill in + */ +void get_memmap ( struct memory_map *memmap ) { + struct memmap_region region; + struct memory_region *usable; + + /* Clear legacy memory map */ + memmap->count = 0; + + /* Populate legacy memory map */ + for_each_memmap ( ®ion, 1 ) { + if ( memmap_is_usable ( ®ion ) ) { + usable = &memmap->regions[memmap->count++]; + usable->start = region.addr; + usable->end = ( region.last + 1 ); + if ( memmap->count == MAX_MEMORY_REGIONS ) + break; + } + } +} + PROVIDE_MEMMAP_INLINE ( null, memmap_describe ); PROVIDE_MEMMAP_INLINE ( null, memmap_sync ); -- 2.47.2