summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMichael Brown2008-09-29 06:11:51 +0200
committerMichael Brown2008-09-29 06:11:51 +0200
commit1dda75c9cdca7105733bfc21e8b4b6d46df19113 (patch)
tree0533439bb5433ba90f4c709bad3a0465d3b9b55c
parent[pcbios] Print INT 15,E820 extended attributes, if present (diff)
downloadipxe-1dda75c9cdca7105733bfc21e8b4b6d46df19113.tar.gz
ipxe-1dda75c9cdca7105733bfc21e8b4b6d46df19113.tar.xz
ipxe-1dda75c9cdca7105733bfc21e8b4b6d46df19113.zip
[pcbios] Allow for larger-than-20-byte buffers in e820mangler.S
Although the E820 API allows for a caller to provide only a 20-byte buffer, there exists at least one combination (HP BIOS, 32-bit WinPE) that relies on information found only in the "extended attributes" field, which requires a 24-byte buffer. Allow for up to a 64-byte E820 buffer, in the hope of coping with future idiocies like this one.
-rw-r--r--src/arch/i386/firmware/pcbios/e820mangler.S59
1 files changed, 45 insertions, 14 deletions
diff --git a/src/arch/i386/firmware/pcbios/e820mangler.S b/src/arch/i386/firmware/pcbios/e820mangler.S
index 437efa77..4fbd6563 100644
--- a/src/arch/i386/firmware/pcbios/e820mangler.S
+++ b/src/arch/i386/firmware/pcbios/e820mangler.S
@@ -25,6 +25,26 @@
#define SMAP 0x534d4150
+/* Most documentation refers to the E820 buffer as being 20 bytes, and
+ * the API makes it perfectly legitimate to pass only a 20-byte buffer
+ * and expect to get valid data. However, some morons at ACPI decided
+ * to extend the data structure by adding an extra "extended
+ * attributes" field and by including critical information within this
+ * field, such as whether or not the region is enabled. A caller who
+ * passes in only a 20-byte buffer therefore risks getting very, very
+ * misleading information.
+ *
+ * I have personally witnessed an HP BIOS that returns a value of
+ * 0x0009 in the extended attributes field. If we don't pass this
+ * value through to the caller, 32-bit WinPE will die, usually with a
+ * PAGE_FAULT_IN_NONPAGED_AREA blue screen of death.
+ *
+ * Allow a ridiculously large maximum value (64 bytes) for the E820
+ * buffer as a guard against insufficiently creative idiots in the
+ * future.
+ */
+#define E820MAXSIZE 64
+
/****************************************************************************
*
* Allowed memory windows
@@ -204,19 +224,22 @@ get_underlying_e820:
/* If the requested region is in the cache, return it */
cmpw %bx, underlying_e820_index
- jne 1f
+ jne 2f
pushw %di
pushw %si
movw $underlying_e820_cache, %si
- movw $20, %cx
+ cmpl underlying_e820_cache_size, %ecx
+ jbe 1f
+ movl underlying_e820_cache_size, %ecx
+1: pushl %ecx
rep movsb
+ popl %ecx
popw %si
popw %di
- movw $20, %cx
incw %bx
movl %edx, %eax
ret
-1:
+2:
/* If the requested region is earlier than the cached region,
* invalidate the cache.
*/
@@ -250,23 +273,26 @@ get_underlying_e820:
pushw %ds
popw %es
movw $underlying_e820_cache, %di
- movl $20, %ecx
- movl underlying_e820_ebx, %ebx
+ cmpl $E820MAXSIZE, %ecx
+ jbe 1f
+ movl $E820MAXSIZE, %ecx
+1: movl underlying_e820_ebx, %ebx
stc
pushfw
lcall *%cs:int15_vector
popw %di
popw %es
/* Check for error return from underlying e820 call */
- jc 1f /* CF set: error */
+ jc 2f /* CF set: error */
cmpl $SMAP, %eax
- je 2f /* 'SMAP' missing: error */
-1: /* An error occurred: return values returned by underlying e820 call */
+ je 3f /* 'SMAP' missing: error */
+2: /* An error occurred: return values returned by underlying e820 call */
stc /* Force CF set if SMAP was missing */
addr32 leal 16(%esp), %esp /* avoid changing other flags */
ret
-2: /* No error occurred */
+3: /* No error occurred */
movl %ebx, underlying_e820_ebx
+ movl %ecx, underlying_e820_cache_size
popl %edx
popl %ecx
popl %ebx
@@ -290,9 +316,14 @@ underlying_e820_ebx:
.section ".bss16"
underlying_e820_cache:
- .space 20
+ .space E820MAXSIZE
.size underlying_e820_cache, . - underlying_e820_cache
+ .section ".bss16"
+underlying_e820_cache_size:
+ .long 0
+ .size underlying_e820_cache_size, . - underlying_e820_cache_size
+
/****************************************************************************
* Get windowed e820 region, without empty region stripping
*
@@ -437,15 +468,15 @@ get_mangled_e820:
/* Peek ahead to see if there are any further nonempty regions */
pushal
pushw %es
- subw $20, %sp
+ movw %sp, %bp
+ subw %cx, %sp
movl $0xe820, %eax
movl $SMAP, %edx
- movl $20, %ecx
pushw %ss
popw %es
movw %sp, %di
call get_nonempty_e820
- addr32 leal 20(%esp), %esp /* avoid changing flags */
+ movw %bp, %sp
popw %es
popal
jnc 99f /* There are further nonempty regions */