[pcbios] Prepare for multiple splits of hidden e820 memory regions
authorMichael Brown <mcb30@etherboot.org>
Mon, 18 Aug 2008 00:01:45 +0000 (01:01 +0100)
committerMichael Brown <mcb30@etherboot.org>
Mon, 18 Aug 2008 00:01:45 +0000 (01:01 +0100)
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.

src/arch/i386/firmware/pcbios/e820mangler.S
src/arch/i386/scripts/i386.lds

index e932804..0ab857b 100644 (file)
@@ -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
  *
@@ -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
@@ -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
index a5a0105..0422344 100644 (file)
@@ -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 ) {