[pcbios] Support arbitrary splits of the e820 memory map

Allow for an arbitrary number of splits of the system memory map via
INT 15,e820.

Features of the new map-mangling algorithm include:

  Supports random access to e820 map entries.

  Requires only sequential access support from the underlying e820
  map, even if our caller uses random access.

  Empty regions will always be stripped.

  Always terminates with %ebx=0, even if the underlying map terminates
  with CF=1.

  Allows for an arbitrary number of hidden regions, with underlying
  regions split into as many subregions as necessary.

Total size increase to achieve this is 193 bytes.
pull/1/head
Michael Brown 2008-08-18 07:14:27 +01:00
parent 9737095d49
commit 320b072c7a
5 changed files with 129 additions and 366 deletions

View File

@ -194,8 +194,8 @@ userptr_t urealloc ( userptr_t ptr, size_t new_size ) {
/* Collect any free blocks and update hidden memory region */ /* Collect any free blocks and update hidden memory region */
ecollect_free(); ecollect_free();
hide_region ( EXTMEM, user_to_phys ( bottom, -sizeof ( extmem ) ), hide_umalloc ( user_to_phys ( bottom, -sizeof ( extmem ) ),
user_to_phys ( top, 0 ) ); user_to_phys ( top, 0 ) );
return ( new_size ? new : UNOWHERE ); return ( new_size ? new : UNOWHERE );
} }

View File

@ -26,159 +26,82 @@
#define SMAP 0x534d4150 #define SMAP 0x534d4150
/**************************************************************************** /****************************************************************************
* Check for overlap
* *
* Parameters: * Allowed memory windows
* %edx:%eax Region start *
* %ecx:%ebx Region end * There are two ways to view this list. The first is as a list of
* %si Pointer to hidden region descriptor * (non-overlapping) allowed memory regions, sorted by increasing
* Returns: * address. The second is as a list of (non-overlapping) hidden
* CF set Region overlaps * memory regions, again sorted by increasing address. The second
* CF clear No overlap * view is offset by half an entry from the first: think about this
* for a moment and it should make sense.
*
* xxx_memory_window is used to indicate an "allowed region"
* structure, hidden_xxx_memory is used to indicate a "hidden region"
* structure. Each structure is 16 bytes in length.
*
**************************************************************************** ****************************************************************************
*/ */
.section ".text16" .section ".data16"
check_overlap: .align 16
/* If start >= hidden_end, there is no overlap. */ .globl hidemem_base
testl %edx, %edx .globl hidemem_umalloc
jnz no_overlap .globl hidemem_text
cmpl 4(%si), %eax memory_windows:
jae no_overlap base_memory_window: .long 0x00000000, 0x00000000 /* Start of memory */
/* If end <= hidden_start, there is no overlap; equivalently,
* if end > hidden_start, there is overlap. hidemem_base: .long 0x000a0000, 0x00000000 /* Changes at runtime */
*/ ext_memory_window: .long 0x000a0000, 0x00000000 /* 640kB mark */
testl %ecx, %ecx
jnz overlap hidemem_umalloc: .long 0xffffffff, 0xffffffff /* Changes at runtime */
cmpl 0(%si), %ebx .long 0xffffffff, 0xffffffff /* Changes at runtime */
ja overlap
no_overlap: hidemem_text: .long 0xffffffff, 0xffffffff /* Changes at runtime */
clc .long 0xffffffff, 0xffffffff /* Changes at runtime */
ret
overlap: .long 0xffffffff, 0xffffffff /* End of memory */
stc memory_windows_end:
ret
.size check_overlap, . - check_overlap
/**************************************************************************** /****************************************************************************
* Check for overflow/underflow * Truncate region to memory window
* *
* Parameters: * Parameters:
* %edx:%eax Region start * %edx:%eax Start of region
* %ecx:%ebx Region end * %ecx:%ebx Length of region
* %si Memory window
* Returns: * Returns:
* CF set start < end * %edx:%eax Start of windowed region
* CF clear start >= end * %ecx:%ebx Length of windowed region
**************************************************************************** ****************************************************************************
*/ */
.section ".text16" .section ".text16"
check_overflow: window_region:
pushl %ecx /* Convert (start,len) to (start, end) */
pushl %ebx
subl %eax, %ebx
sbbl %edx, %ecx
popl %ebx
popl %ecx
ret
.size check_overflow, . - check_overflow
/****************************************************************************
* Truncate towards start of region
*
* Parameters:
* %edx:%eax Region start
* %ecx:%ebx Region end
* %si Pointer to hidden region descriptor
* Returns:
* %edx:%eax Modified region start
* %ecx:%ebx Modified region end
* CF set Region was truncated
* CF clear Region was not truncated
****************************************************************************
*/
.section ".text16"
truncate_to_start:
/* If overlaps, set region end = hidden region start */
call check_overlap
jnc 99f
movl 0(%si), %ebx
xorl %ecx, %ecx
/* If region end < region start, set region end = region start */
call check_overflow
jnc 1f
movl %eax, %ebx
movl %edx, %ecx
1: stc
99: ret
.size truncate_to_start, . - truncate_to_start
/****************************************************************************
* Truncate towards end of region
*
* Parameters:
* %edx:%eax Region start
* %ecx:%ebx Region end
* %si Pointer to hidden region descriptor
* Returns:
* %edx:%eax Modified region start
* %ecx:%ebx Modified region end
* CF set Region was truncated
* CF clear Region was not truncated
****************************************************************************
*/
.section ".text16"
truncate_to_end:
/* If overlaps, set region start = hidden region end */
call check_overlap
jnc 99f
movl 4(%si), %eax
xorl %edx, %edx
/* If region start > region end, set region start = region end */
call check_overflow
jnc 1f
movl %ebx, %eax
movl %ecx, %edx
1: stc
99: ret
.size truncate_to_end, . - truncate_to_end
/****************************************************************************
* Truncate region
*
* Parameters:
* %edx:%eax Region start
* %ecx:%ebx Region length (*not* region end)
* %bp truncate_to_start or truncate_to_end
* Returns:
* %edx:%eax Modified region start
* %ecx:%ebx Modified region length
* CF set Region was truncated
* CF clear Region was not truncated
****************************************************************************
*/
.section ".text16"
truncate:
pushw %si
pushfw
/* Convert (start,len) to (start,end) */
addl %eax, %ebx addl %eax, %ebx
adcl %edx, %ecx adcl %edx, %ecx
/* Hide all hidden regions, truncating as directed */ /* Truncate to window start */
movw $hidden_regions, %si cmpl 4(%si), %edx
1: call *%bp jne 1f
jnc 2f cmpl 0(%si), %eax
popfw /* If CF was set, set stored CF in flags word on stack */ 1: jae 2f
stc movl 4(%si), %edx
pushfw movl 0(%si), %eax
2: addw $8, %si 2: /* Truncate to window end */
cmpl $0, 0(%si) cmpl 12(%si), %ecx
jne 1b jne 1f
/* Convert modified (start,end) back to (start,len) */ cmpl 8(%si), %ebx
1: jbe 2f
movl 12(%si), %ecx
movl 8(%si), %ebx
2: /* Convert (start, end) back to (start, len) */
subl %eax, %ebx subl %eax, %ebx
sbbl %edx, %ecx sbbl %edx, %ecx
popfw /* If length is <0, set length to 0 */
popw %si jae 1f
xorl %ebx, %ebx
xorl %ecx, %ecx
ret ret
.size truncate, . - truncate .size window_region, . - window_region
/**************************************************************************** /****************************************************************************
* Patch "memory above 1MB" figure * Patch "memory above 1MB" figure
@ -187,21 +110,19 @@ truncate:
* %ax Memory above 1MB, in 1kB blocks * %ax Memory above 1MB, in 1kB blocks
* Returns: * Returns:
* %ax Modified memory above 1M in 1kB blocks * %ax Modified memory above 1M in 1kB blocks
* CF set Region was truncated
* CF clear Region was not truncated
**************************************************************************** ****************************************************************************
*/ */
.section ".text16" .section ".text16"
patch_1m: patch_1m:
pushal pushal
/* Convert to (start,len) format and call truncate */ /* Convert to (start,len) format and call truncate */
movw $truncate_to_start, %bp
xorl %ecx, %ecx xorl %ecx, %ecx
movzwl %ax, %ebx movzwl %ax, %ebx
shll $10, %ebx shll $10, %ebx
xorl %edx, %edx xorl %edx, %edx
movl $0x100000, %eax movl $0x100000, %eax
call truncate movw $ext_memory_window, %si
call window_region
/* Convert back to "memory above 1MB" format and return via %ax */ /* Convert back to "memory above 1MB" format and return via %ax */
pushfw pushfw
shrl $10, %ebx shrl $10, %ebx
@ -219,20 +140,18 @@ patch_1m:
* %bx Memory above 16MB, in 64kB blocks * %bx Memory above 16MB, in 64kB blocks
* Returns: * Returns:
* %bx Modified memory above 16M in 64kB blocks * %bx Modified memory above 16M in 64kB blocks
* CF set Region was truncated
* CF clear Region was not truncated
**************************************************************************** ****************************************************************************
*/ */
.section ".text16" .section ".text16"
patch_16m: patch_16m:
pushal pushal
/* Convert to (start,len) format and call truncate */ /* Convert to (start,len) format and call truncate */
movw $truncate_to_start, %bp
xorl %ecx, %ecx xorl %ecx, %ecx
shll $16, %ebx shll $16, %ebx
xorl %edx, %edx xorl %edx, %edx
movl $0x1000000, %eax movl $0x1000000, %eax
call truncate movw $ext_memory_window, %si
call window_region
/* Convert back to "memory above 16MB" format and return via %bx */ /* Convert back to "memory above 16MB" format and return via %bx */
pushfw pushfw
shrl $16, %ebx shrl $16, %ebx
@ -252,19 +171,17 @@ patch_16m:
* Returns: * Returns:
* %ax Modified memory between 1MB and 16MB, in 1kB blocks * %ax Modified memory between 1MB and 16MB, in 1kB blocks
* %bx Modified memory above 16MB, in 64kB blocks * %bx Modified memory above 16MB, in 64kB blocks
* CF set Region was truncated
* CF clear Region was not truncated
**************************************************************************** ****************************************************************************
*/ */
.section ".text16" .section ".text16"
patch_1m_16m: patch_1m_16m:
call patch_1m call patch_1m
jc 1f
call patch_16m call patch_16m
ret /* If 1M region is no longer full-length, kill off the 16M region */
1: /* 1m region was truncated; kill the 16m region */ cmpw $( 15 * 1024 ), %ax
je 1f
xorw %bx, %bx xorw %bx, %bx
ret 1: ret
.size patch_1m_16m, . - patch_1m_16m .size patch_1m_16m, . - patch_1m_16m
/**************************************************************************** /****************************************************************************
@ -337,7 +254,7 @@ get_underlying_e820:
je 2f /* 'SMAP' missing: error */ je 2f /* 'SMAP' missing: error */
1: /* An error occurred: return values returned by underlying e820 call */ 1: /* An error occurred: return values returned by underlying e820 call */
stc /* Force CF set if SMAP was missing */ stc /* Force CF set if SMAP was missing */
leal 16(%esp), %esp /* avoid changing other flags */ addr32 leal 16(%esp), %esp /* avoid changing other flags */
ret ret
2: /* No error occurred */ 2: /* No error occurred */
movl %ebx, underlying_e820_ebx movl %ebx, underlying_e820_ebx
@ -400,18 +317,6 @@ underlying_e820_cache:
* *
**************************************************************************** ****************************************************************************
*/ */
.section ".tbl.data16.memory_windows.00"
.align 16
memory_windows:
// Dummy memory window encompassing entire 64-bit address space
.long 0, 0, 0xffffffff, 0xffffffff
.section ".tbl.data16.memory_windows.99"
.align 16
memory_windows_end:
.section ".text16" .section ".text16"
get_windowed_e820: get_windowed_e820:
@ -421,7 +326,6 @@ get_windowed_e820:
/* Split %ebx into %si:%bx, store original %bx in %bp */ /* Split %ebx into %si:%bx, store original %bx in %bp */
pushl %ebx pushl %ebx
xorl %esi, %esi
popw %bp popw %bp
popw %si popw %si
@ -441,30 +345,8 @@ get_windowed_e820:
movl %es:4(%di), %edx movl %es:4(%di), %edx
movl %es:8(%di), %ebx movl %es:8(%di), %ebx
movl %es:12(%di), %ecx movl %es:12(%di), %ecx
/* Convert (start,len) to (start, end) */ /* Truncate region to current window */
addl %eax, %ebx call window_region
adcl %edx, %ecx
/* Truncate to window start */
cmpl 4(%esi), %edx
jne 1f
cmpl 0(%esi), %eax
1: jae 2f
movl 4(%esi), %edx
movl 0(%esi), %eax
2: /* Truncate to window end */
cmpl 12(%esi), %ecx
jne 1f
cmpl 8(%esi), %ebx
1: jbe 2f
movl 12(%esi), %ecx
movl 8(%esi), %ebx
2: /* Convert (start, end) back to (start, len) */
subl %eax, %ebx
sbbl %edx, %ecx
/* If length is <0, set length to 0 */
jae 1f
xorl %ebx, %ebx
xorl %ecx, %ecx
1: /* Store modified values in e820 map entry */ 1: /* Store modified values in e820 map entry */
movl %eax, %es:0(%di) movl %eax, %es:0(%di)
movl %edx, %es:4(%di) movl %edx, %es:4(%di)
@ -537,7 +419,7 @@ get_nonempty_e820:
98: /* Clear CF */ 98: /* Clear CF */
clc clc
99: /* Return values from underlying call */ 99: /* Return values from underlying call */
leal 12(%esp), %esp /* avoid changing flags */ addr32 leal 12(%esp), %esp /* avoid changing flags */
ret ret
.size get_nonempty_e820, . - get_nonempty_e820 .size get_nonempty_e820, . - get_nonempty_e820
@ -572,7 +454,7 @@ get_mangled_e820:
popw %es popw %es
movw %sp, %di movw %sp, %di
call get_nonempty_e820 call get_nonempty_e820
leal 20(%esp), %esp /* avoid changing flags */ addr32 leal 20(%esp), %esp /* avoid changing flags */
popal popal
jnc 99f /* There are further nonempty regions */ jnc 99f /* There are further nonempty regions */
@ -583,137 +465,17 @@ get_mangled_e820:
ret ret
.size get_mangled_e820, . - get_mangled_e820 .size get_mangled_e820, . - get_mangled_e820
/****************************************************************************
* Patch E820 memory map entry
*
* Parameters:
* %es:di Pointer to E820 memory map descriptor
* %bp truncate_to_start or truncate_to_end
* Returns:
* %es:di Pointer to now-modified E820 memory map descriptor
* CF set Region was truncated
* CF clear Region was not truncated
****************************************************************************
*/
.section ".text16"
patch_e820:
pushal
movl %es:0(%di), %eax
movl %es:4(%di), %edx
movl %es:8(%di), %ebx
movl %es:12(%di), %ecx
call truncate
movl %eax, %es:0(%di)
movl %edx, %es:4(%di)
movl %ebx, %es:8(%di)
movl %ecx, %es:12(%di)
popal
ret
.size patch_e820, . - patch_e820
/****************************************************************************
* Split E820 memory map entry if necessary
*
* Parameters:
* As for INT 15,e820
* Returns:
* As for INT 15,e820
*
* Calls the underlying INT 15,e820 and returns a modified memory map.
* Regions will be split around any hidden regions.
****************************************************************************
*/
.section ".text16"
split_e820:
pushw %si
pushw %bp
/* Caller's %bx => %si, real %ebx to %ebx, call previous handler */
pushfw
movw %bx, %si
testl %ebx, %ebx
jnz 1f
movl %ebx, %cs:real_ebx
1: movl %cs:real_ebx, %ebx
// lcall *%cs:int15_vector
/* Hacked in call to get_mangled_e820 in place of underlying INT15 */
popfw
pushw %ds
pushw %cs:rm_ds
popw %ds
call get_mangled_e820
popw %ds
pushfw
/* Edit result */
pushw %ds
pushw %cs:rm_ds
popw %ds
movw $truncate_to_start, %bp
incw %si
jns 2f
movw $truncate_to_end, %bp
2: call patch_e820
jnc 3f
xorw $0x8000, %si
3: testw %si, %si
js 4f
movl %ebx, %cs:real_ebx
testl %ebx, %ebx
jz 5f
4: movw %si, %bx
5: popw %ds
/* Restore flags returned by previous handler and return */
popfw
popw %bp
popw %si
ret
.size split_e820, . - split_e820
.section ".text16.data"
real_ebx:
.long 0
.size real_ebx, . - real_ebx
/**************************************************************************** /****************************************************************************
* INT 15,e820 handler * INT 15,e820 handler
**************************************************************************** ****************************************************************************
*/ */
.section ".text16" .section ".text16"
int15_e820: int15_e820:
pushl %eax pushw %ds
pushl %ecx pushw %cs:rm_ds
pushl %edx popw %ds
call split_e820 call get_mangled_e820
pushfw popw %ds
/* If we've hit an error, exit immediately */
jc 99f
/* If region is non-empty, return this region */
pushl %eax
movl %es:8(%di), %eax
orl %es:12(%di), %eax
popl %eax
jnz 99f
/* Region is empty. If this is not the end of the map,
* skip over this region.
*/
testl %ebx, %ebx
jz 1f
popfw
popl %edx
popl %ecx
popl %eax
jmp int15_e820
1: /* Region is empty and this is the end of the map. Return
* with CF set to avoid placing an empty region at the end of
* the map.
*/
popfw
stc
pushfw
99: /* Restore flags from original INT 15,e820 call and return */
popfw
addr32 leal 12(%esp), %esp /* avoid changing flags */
lret $2 lret $2
.size int15_e820, . - int15_e820 .size int15_e820, . - int15_e820

View File

@ -15,6 +15,7 @@
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/ */
#include <assert.h>
#include <realmode.h> #include <realmode.h>
#include <biosint.h> #include <biosint.h>
#include <basemem.h> #include <basemem.h>
@ -25,7 +26,7 @@
#define ALIGN_HIDDEN 4096 /* 4kB page alignment should be enough */ #define ALIGN_HIDDEN 4096 /* 4kB page alignment should be enough */
/** /**
* A hidden region of Etherboot * A hidden region of gPXE
* *
* This represents a region that will be edited out of the system's * This represents a region that will be edited out of the system's
* memory map. * memory map.
@ -34,24 +35,23 @@
* changed. * changed.
*/ */
struct hidden_region { struct hidden_region {
/* Physical start address */ /** Physical start address */
physaddr_t start; uint64_t start;
/* Physical end address */ /** Physical end address */
physaddr_t end; uint64_t end;
}; };
/** /** Hidden base memory */
* List of hidden regions extern struct hidden_region __data16 ( hidemem_base );
* #define hidemem_base __use_data16 ( hidemem_base )
* Must be terminated by a zero entry.
*/ /** Hidden umalloc memory */
struct hidden_region __data16_array ( hidden_regions, [] ) = { extern struct hidden_region __data16 ( hidemem_umalloc );
[TEXT] = { 0, 0 }, #define hidemem_umalloc __use_data16 ( hidemem_umalloc )
[BASEMEM] = { ( 640 * 1024 ), ( 640 * 1024 ) },
[EXTMEM] = { 0, 0 }, /** Hidden text memory */
{ 0, 0, } /* Terminator */ extern struct hidden_region __data16 ( hidemem_text );
}; #define hidemem_text __use_data16 ( hidemem_text )
#define hidden_regions __use_data16 ( hidden_regions )
/** Assembly routine in e820mangler.S */ /** Assembly routine in e820mangler.S */
extern void int15(); extern void int15();
@ -60,14 +60,19 @@ extern void int15();
extern struct segoff __text16 ( int15_vector ); extern struct segoff __text16 ( int15_vector );
#define int15_vector __use_text16 ( int15_vector ) #define int15_vector __use_text16 ( int15_vector )
/* The linker defines these symbols for us */
extern char _text[];
extern char _end[];
/** /**
* Hide region of memory from system memory map * Hide region of memory from system memory map
* *
* @v region Hidden memory region
* @v start Start of region * @v start Start of region
* @v end End of region * @v end End of region
*/ */
void hide_region ( unsigned int region_id, physaddr_t start, physaddr_t end ) { static void hide_region ( struct hidden_region *region,
struct hidden_region *region = &hidden_regions[region_id]; physaddr_t start, physaddr_t end ) {
/* Some operating systems get a nasty shock if a region of the /* Some operating systems get a nasty shock if a region of the
* E820 map seems to start on a non-page boundary. Make life * E820 map seems to start on a non-page boundary. Make life
@ -76,21 +81,7 @@ void hide_region ( unsigned int region_id, physaddr_t start, physaddr_t end ) {
region->start = ( start & ~( ALIGN_HIDDEN - 1 ) ); region->start = ( start & ~( ALIGN_HIDDEN - 1 ) );
region->end = ( ( end + ALIGN_HIDDEN - 1 ) & ~( ALIGN_HIDDEN - 1 ) ); region->end = ( ( end + ALIGN_HIDDEN - 1 ) & ~( ALIGN_HIDDEN - 1 ) );
DBG ( "Hiding region %d [%lx,%lx)\n", DBG ( "Hiding region [%llx,%llx)\n", region->start, region->end );
region_id, region->start, region->end );
}
/**
* Hide Etherboot text
*
*/
static void hide_text ( void ) {
/* The linker defines these symbols for us */
extern char _text[];
extern char _end[];
hide_region ( TEXT, virt_to_phys ( _text ), virt_to_phys ( _end ) );
} }
/** /**
@ -102,7 +93,25 @@ void hide_basemem ( void ) {
* hide_region(), because we don't want this rounded to the * hide_region(), because we don't want this rounded to the
* nearest page boundary. * nearest page boundary.
*/ */
hidden_regions[BASEMEM].start = ( get_fbms() * 1024 ); hidemem_base.start = ( get_fbms() * 1024 );
}
/**
* Hide umalloc() region
*
*/
void hide_umalloc ( physaddr_t start, physaddr_t end ) {
assert ( end <= virt_to_phys ( _text ) );
hide_region ( &hidemem_umalloc, start, end );
}
/**
* Hide .text and .data
*
*/
void hide_text ( void ) {
hide_region ( &hidemem_text, virt_to_phys ( _text ),
virt_to_phys ( _end ) );
} }
/** /**
@ -114,8 +123,9 @@ void hide_basemem ( void ) {
static void hide_etherboot ( void ) { static void hide_etherboot ( void ) {
/* Initialise the hidden regions */ /* Initialise the hidden regions */
hide_text();
hide_basemem(); hide_basemem();
hide_umalloc ( virt_to_phys ( _text ), virt_to_phys ( _text ) );
hide_text();
/* Hook INT 15 */ /* Hook INT 15 */
hook_bios_interrupt ( 0x15, ( unsigned int ) int15, hook_bios_interrupt ( 0x15, ( unsigned int ) int15,

View File

@ -82,7 +82,6 @@ SECTIONS {
__data16 = .; __data16 = .;
*(.data16) *(.data16)
*(.data16.*) *(.data16.*)
*(SORT(.tbl.data16.*)) /* Various tables. See include/tables.h */
_edata16_progbits = .; _edata16_progbits = .;
} }
.bss16 : AT ( _data16_load_offset + __bss16 ) { .bss16 : AT ( _data16_load_offset + __bss16 ) {

View File

@ -8,16 +8,8 @@
* *
*/ */
/** #include <stdint.h>
* Unique IDs for hidden regions
*/
enum hidemem_region_id {
TEXT = 0,
BASEMEM,
EXTMEM,
};
extern void hide_region ( unsigned int region_id, physaddr_t start, extern void hide_umalloc ( physaddr_t start, physaddr_t end );
physaddr_t end );
#endif /* _GPXE_HIDEMEM_H */ #endif /* _GPXE_HIDEMEM_H */