Skip to content

Commit

Permalink
[pcbios] Prepare for multiple splits of hidden e820 memory regions
Browse files Browse the repository at this point in the history
Define a list of N allowed memory regions, and split each underlying
e820 region into up to N subregions.  Strip resulting empty regions
out of the map, avoiding using the "return with CF set to strip last
empty region" trick, because it seems that bootmgr.exe in Win2k8 gets
upset if the memory map is terminated with CF set.

This is an intermediate checkin that defines a single allowed memory
region covering the entire 64-bit address space, and uses the existing
map-mangling code on top of the new region-splitting code.  This
sanitises the memory map to the point that Win2k8 is able to boot even
on a system that defines a final zero-length region at the 4GB mark.

I'm checking this in because it may be useful for future debugging
efforts to be able to run with the existing and known-working map
mangling code together with the map sanitisation capabilities of the
new map mangling code.
  • Loading branch information
Michael Brown committed Aug 18, 2008
1 parent 23bca8f commit 9737095
Show file tree
Hide file tree
Showing 2 changed files with 328 additions and 2 deletions.
329 changes: 327 additions & 2 deletions src/arch/i386/firmware/pcbios/e820mangler.S
Expand Up @@ -267,6 +267,322 @@ patch_1m_16m:
ret
.size patch_1m_16m, . - patch_1m_16m

/****************************************************************************
* Get underlying e820 memory region to underlying_e820 buffer
*
* Parameters:
* As for INT 15,e820
* Returns:
* As for INT 15,e820
*
* Wraps the underlying INT 15,e820 call so that the continuation
* value (%ebx) is a 16-bit simple sequence counter (with the high 16
* bits ignored), and termination is always via CF=1 rather than
* %ebx=0.
*
****************************************************************************
*/
.section ".text16"
get_underlying_e820:

/* If the requested region is in the cache, return it */
cmpw %bx, underlying_e820_index
jne 1f
pushw %di
pushw %si
movw $underlying_e820_cache, %si
movw $20, %cx
rep movsb
popw %si
popw %di
movw $20, %cx
incw %bx
movl %edx, %eax
ret
1:
/* If the requested region is earlier than the cached region,
* invalidate the cache.
*/
cmpw %bx, underlying_e820_index
jbe 1f
movw $0xffff, underlying_e820_index
1:
/* If the cache is invalid, reset the underlying %ebx */
cmpw $0xffff, underlying_e820_index
jne 1f
andl $0, underlying_e820_ebx
1:
/* If the cache is valid but the continuation value is zero,
* this means that the previous underlying call returned with
* %ebx=0. Return with CF=1 in this case.
*/
cmpw $0xffff, underlying_e820_index
je 1f
cmpl $0, underlying_e820_ebx
jne 1f
stc
ret
1:
/* Get the next region into the cache */
pushl %eax
pushl %ebx
pushl %ecx
pushl %edx
movl underlying_e820_ebx, %ebx
stc
pushfw
lcall *%cs:int15_vector
jc 1f /* CF set: error */
cmpl $SMAP, %eax
je 2f /* 'SMAP' missing: error */
1: /* An error occurred: return values returned by underlying e820 call */
stc /* Force CF set if SMAP was missing */
leal 16(%esp), %esp /* avoid changing other flags */
ret
2: /* No error occurred */
movl %ebx, underlying_e820_ebx
popl %edx
popl %ecx
popl %ebx
popl %eax
/* Copy result to cache */
pushw %es
pushw %fs
pushw %si
pushw %di
pushw %cx
pushw %es
popw %fs
movw %di, %si
pushw %ds
popw %es
movw $underlying_e820_cache, %di
movw $20, %cx
fs rep movsb
popw %cx
popw %di
popw %si
popw %fs
popw %es
/* Mark cache as containing this result */
incw underlying_e820_index

/* Loop until found */
jmp get_underlying_e820
.size get_underlying_e820, . - get_underlying_e820

.section ".data16"
underlying_e820_index:
.word 0xffff /* Initialise to an invalid value */
.size underlying_e820_index, . - underlying_e820_index

.section ".bss16"
underlying_e820_ebx:
.long 0
.size underlying_e820_ebx, . - underlying_e820_ebx

.section ".bss16"
underlying_e820_cache:
.space 20
.size underlying_e820_cache, . - underlying_e820_cache

/****************************************************************************
* Get windowed e820 region, without empty region stripping
*
* Parameters:
* As for INT 15,e820
* Returns:
* As for INT 15,e820
*
* Wraps the underlying INT 15,e820 call so that each underlying
* region is returned N times, windowed to fit within N visible-memory
* windows. Termination is always via CF=1.
*
****************************************************************************
*/

.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"
get_windowed_e820:

/* Preserve registers */
pushl %esi
pushw %bp

/* Split %ebx into %si:%bx, store original %bx in %bp */
pushl %ebx
xorl %esi, %esi
popw %bp
popw %si

/* %si == 0 => start of memory_windows list */
testw %si, %si
jne 1f
movw $memory_windows, %si
1:
/* Get (cached) underlying e820 region to buffer */
call get_underlying_e820
jc 99f /* Abort on error */

/* Preserve registers */
pushal
/* start => %edx:%eax, len => %ecx:%ebx */
movl %es:0(%di), %eax
movl %es:4(%di), %edx
movl %es:8(%di), %ebx
movl %es:12(%di), %ecx
/* Convert (start,len) to (start, end) */
addl %eax, %ebx
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 */
movl %eax, %es:0(%di)
movl %edx, %es:4(%di)
movl %ebx, %es:8(%di)
movl %ecx, %es:12(%di)
/* Restore registers */
popal

/* Derive continuation value for next call */
addw $16, %si
cmpw $memory_windows_end, %si
jne 1f
/* End of memory windows: reset %si and allow %bx to continue */
xorw %si, %si
jmp 2f
1: /* More memory windows to go: restore original %bx */
movw %bp, %bx
2: /* Construct %ebx from %si:%bx */
pushw %si
pushw %bx
popl %ebx

98: /* Clear CF */
clc
99: /* Restore registers and return */
popw %bp
popl %esi
ret
.size get_windowed_e820, . - get_windowed_e820

/****************************************************************************
* Get windowed e820 region, with empty region stripping
*
* Parameters:
* As for INT 15,e820
* Returns:
* As for INT 15,e820
*
* Wraps the underlying INT 15,e820 call so that each underlying
* region is returned up to N times, windowed to fit within N
* visible-memory windows. Empty windows are never returned.
* Termination is always via CF=1.
*
****************************************************************************
*/
.section ".text16"
get_nonempty_e820:

/* Record entry parameters */
pushl %eax
pushl %ecx
pushl %edx

/* Get next windowed region */
call get_windowed_e820
jc 99f /* abort on error */

/* If region is non-empty, finish here */
cmpl $0, %es:8(%di)
jne 98f
cmpl $0, %es:12(%di)
jne 98f

/* Region was empty: restore entry parameters and go to next region */
popl %edx
popl %ecx
popl %eax
jmp get_nonempty_e820

98: /* Clear CF */
clc
99: /* Return values from underlying call */
leal 12(%esp), %esp /* avoid changing flags */
ret
.size get_nonempty_e820, . - get_nonempty_e820

/****************************************************************************
* Get mangled e820 region, with empty region stripping
*
* Parameters:
* As for INT 15,e820
* Returns:
* As for INT 15,e820
*
* Wraps the underlying INT 15,e820 call so that underlying regions
* are windowed to the allowed memory regions. Empty regions are
* stripped from the map. Termination is always via %ebx=0.
*
****************************************************************************
*/
.section ".text16"
get_mangled_e820:

/* Get a nonempty region */
call get_nonempty_e820
jc 99f /* Abort on error */

/* Peek ahead to see if there are any further nonempty regions */
pushal
subw $20, %sp
movl $0xe820, %eax
movl $SMAP, %edx
movl $20, %ecx
pushw %ss
popw %es
movw %sp, %di
call get_nonempty_e820
leal 20(%esp), %esp /* avoid changing flags */
popal
jnc 99f /* There are further nonempty regions */

/* No futher nonempty regions: zero %ebx and clear CF */
xorl %ebx, %ebx

99: /* Return */
ret
.size get_mangled_e820, . - get_mangled_e820

/****************************************************************************
* Patch E820 memory map entry
*
Expand Down Expand Up @@ -298,7 +614,7 @@ patch_e820:
/****************************************************************************
* Split E820 memory map entry if necessary
*
* Parameters:
* Parameters:
* As for INT 15,e820
* Returns:
* As for INT 15,e820
Expand All @@ -318,7 +634,16 @@ split_e820:
jnz 1f
movl %ebx, %cs:real_ebx
1: movl %cs:real_ebx, %ebx
lcall *%cs:int15_vector

// 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
Expand Down
1 change: 1 addition & 0 deletions src/arch/i386/scripts/i386.lds
Expand Up @@ -82,6 +82,7 @@ SECTIONS {
__data16 = .;
*(.data16)
*(.data16.*)
*(SORT(.tbl.data16.*)) /* Various tables. See include/tables.h */
_edata16_progbits = .;
}
.bss16 : AT ( _data16_load_offset + __bss16 ) {
Expand Down

0 comments on commit 9737095

Please sign in to comment.