Index: libi386/biosmem.c
===================================================================
--- libi386/biosmem.c	(revision 277404)
+++ libi386/biosmem.c	(working copy)
@@ -32,6 +32,7 @@
  */
 #include <stand.h>
 #include <machine/pc/bios.h>
+#include "bootstrap.h"
 #include "libi386.h"
 #include "btxv86.h"
 
@@ -38,13 +39,56 @@
 vm_offset_t	memtop, memtop_copyin, high_heap_base;
 uint32_t	bios_basemem, bios_extmem, high_heap_size;
 
-static struct bios_smap smap;
+static struct bios_smap_xattr smap;
 
 /*
+ * Used to track which method was used to set BIOS memory
+ * regions.
+ */
+static uint8_t b_bios_probed;
+#define	B_BASEMEM_E820	0x1
+#define	B_BASEMEM_12	0x2
+#define	B_EXTMEM_E820	0x4
+#define	B_EXTMEM_E801	0x8
+#define	B_EXTMEM_8800	0x10
+
+/*
  * The minimum amount of memory to reserve in bios_extmem for the heap.
  */
 #define	HEAP_MIN	(3 * 1024 * 1024)
 
+/*
+ * Products in this list need quirks to detect
+ * memory correctly. You need both maker and product as
+ * reported by smbios.
+ */
+#define BQ_DISTRUST_E820_EXTMEM	0x1	/* e820 might not return useful
+					   extended memory */
+struct bios_getmem_quirks {
+    const char* bios_vendor;
+    const char*	maker;
+    const char*	product;
+    int		quirk;
+};
+
+static struct bios_getmem_quirks quirks[] = {
+    {"coreboot", "Acer", "Peppy", BQ_DISTRUST_E820_EXTMEM},
+    {NULL, NULL, NULL, 0}
+};
+
+static int
+bios_getquirks(void)
+{
+    int i;
+
+    for (i=0; quirks[i].quirk != 0; ++i)
+	if (smbios_match(quirks[i].bios_vendor, quirks[i].maker,
+	    quirks[i].product))
+	    return (1);
+
+    return (0);
+}
+
 void
 bios_getmem(void)
 {
@@ -56,7 +100,7 @@
 	v86.ctl = V86_FLAGS;
 	v86.addr = 0x15;		/* int 0x15 function 0xe820*/
 	v86.eax = 0xe820;
-	v86.ecx = sizeof(struct bios_smap);
+	v86.ecx = sizeof(struct bios_smap_xattr);
 	v86.edx = SMAP_SIG;
 	v86.es = VTOPSEG(&smap);
 	v86.edi = VTOPOFF(&smap);
@@ -65,11 +109,17 @@
 	    break;
 	/* look for a low-memory segment that's large enough */
 	if ((smap.type == SMAP_TYPE_MEMORY) && (smap.base == 0) &&
-	    (smap.length >= (512 * 1024)))
+	    (smap.length >= (512 * 1024))) {
 	    bios_basemem = smap.length;
+	    b_bios_probed |= B_BASEMEM_E820;
+	}
 	/* look for the first segment in 'extended' memory */
-	if ((smap.type == SMAP_TYPE_MEMORY) && (smap.base == 0x100000)) {
+	/* we need it to be at least 32MiB or -HEAD won't load */
+	if ((smap.type == SMAP_TYPE_MEMORY) && (smap.base == 0x100000) &&
+	    (smap.length >= (32 * 1024 * 1024) ||
+	     !(bios_getquirks() & BQ_DISTRUST_E820_EXTMEM))) {
 	    bios_extmem = smap.length;
+	    b_bios_probed |= B_EXTMEM_E820;
 	}
 
 	/*
@@ -100,6 +150,7 @@
 	v86int();
 	
 	bios_basemem = (v86.eax & 0xffff) * 1024;
+	b_bios_probed |= B_BASEMEM_12;
     }
 
     /* Fall back through several compatibility functions for extended memory */
@@ -109,7 +160,29 @@
 	v86.eax = 0xe801;
 	v86int();
 	if (!(V86_CY(v86.efl))) {
-	    bios_extmem = ((v86.ecx & 0xffff) + ((v86.edx & 0xffff) * 64)) * 1024;
+	    /*
+	     * Clear high_heap; it may end up overlapping
+	     * with the segment we're determining here.
+	     * Let the default "steal stuff from top of
+	     * bios_extmem" code below pick up on it.
+	     */
+	    high_heap_size = 0;
+	    high_heap_base = 0;
+	    /*
+	     * cx is the number of 1KiB blocks between 1..16MiB.
+	     * It can only be up to 0x3c00; if it's smaller then
+	     * there's a PC AT memory hole so we can't treat
+	     * it as contiguous.
+	     */
+	    bios_extmem = (v86.ecx & 0xffff) * 1024;
+	    if (bios_extmem == (1024 * 0x3c00))
+		bios_extmem += (v86.edx & 0xffff) * 64 * 1024;
+
+	    /* truncate bios_extmem */
+	    if (bios_extmem > 0x3ff00000)
+		bios_extmem = 0x3ff00000;
+
+	    b_bios_probed |= B_EXTMEM_E801;
 	}
     }
     if (bios_extmem == 0) {
@@ -118,6 +191,7 @@
 	v86.eax = 0x8800;
 	v86int();
 	bios_extmem = (v86.eax & 0xffff) * 1024;
+	b_bios_probed |= B_EXTMEM_8800;
     }
 
     /* Set memtop to actual top of memory */
@@ -132,4 +206,36 @@
 	high_heap_size = HEAP_MIN;
 	high_heap_base = memtop - HEAP_MIN;
     }
-}    
+}
+
+static int
+command_biosmem(int argc, char *argv[])
+{
+	int bq = bios_getquirks();
+
+	printf("bios_basemem: 0x%llx\n", (unsigned long long) bios_basemem);
+	printf("bios_extmem: 0x%llx\n", (unsigned long long) bios_extmem);
+	printf("memtop: 0x%llx\n", (unsigned long long) memtop);
+	printf("high_heap_base: 0x%llx\n", (unsigned long long) high_heap_base);
+	printf("high_heap_size: 0x%llx\n", (unsigned long long) high_heap_size);
+	printf("bios_quirks: 0x%02x", bq);
+	if (bq & BQ_DISTRUST_E820_EXTMEM)
+		printf(" BQ_DISTRUST_E820_EXTMEM");
+	printf("\n");
+	printf("b_bios_probed: 0x%02x", (int) b_bios_probed);
+	if (b_bios_probed & B_BASEMEM_E820)
+		printf(" B_BASEMEM_E820");
+	if (b_bios_probed & B_BASEMEM_12)
+		printf(" B_BASEMEM_12");
+	if (b_bios_probed & B_EXTMEM_E820)
+		printf(" B_EXTMEM_E820");
+	if (b_bios_probed & B_EXTMEM_E801)
+		printf(" B_EXTMEM_E801");
+	if (b_bios_probed & B_EXTMEM_8800)
+		printf(" B_EXTMEM_8800");
+	printf("\n");
+
+	return (CMD_OK);
+}
+
+COMMAND_SET(smap, "biosmem", "show BIOS memory setup", command_biosmem);
Index: libi386/libi386.h
===================================================================
--- libi386/libi386.h	(revision 277404)
+++ libi386/libi386.h	(working copy)
@@ -114,6 +114,9 @@
 void	biosacpi_detect(void);
 
 void	smbios_detect(void);
+void	smbios_setup(void);
+int	smbios_match(const char* bios_vendor, const char* maker,
+	    const char* product);
 
 int	i386_autoload(void);
 
Index: libi386/smbios.c
===================================================================
--- libi386/smbios.c	(revision 277404)
+++ libi386/smbios.c	(working copy)
@@ -64,11 +64,24 @@
 #define	SMBIOS_GETLEN(base)	SMBIOS_GET8(base, 0x01)
 #define	SMBIOS_GETSTR(base)	((base) + SMBIOS_GETLEN(base))
 
-static uint32_t	smbios_enabled_memory = 0;
-static uint32_t	smbios_old_enabled_memory = 0;
-static uint8_t	smbios_enabled_sockets = 0;
-static uint8_t	smbios_populated_sockets = 0;
+struct smbios_attr {
+	caddr_t 	addr;
+	size_t		length;
+	size_t		count;
+	int		major;
+	int		minor;
+	int		ver;
+	const char*	bios_vendor;
+	const char*	maker;
+	const char*	product;
+	uint32_t	enabled_memory;
+	uint32_t	old_enabled_memory;
+	uint8_t		enabled_sockets;
+	uint8_t		populated_sockets;
+};
 
+static struct smbios_attr smbios;
+
 static uint8_t
 smbios_checksum(const caddr_t addr, const uint8_t len)
 {
@@ -95,8 +108,8 @@
 	return (NULL);
 }
 
-static void
-smbios_setenv(const char *name, caddr_t addr, const int offset)
+static const char*
+smbios_getstring(caddr_t addr, const int offset)
 {
 	caddr_t		cp;
 	int		i, idx;
@@ -106,10 +119,21 @@
 		cp = SMBIOS_GETSTR(addr);
 		for (i = 1; i < idx; i++)
 			cp += strlen(cp) + 1;
-		setenv(name, cp, 1);
+		return cp;
 	}
+	return (NULL);
 }
 
+static void
+smbios_setenv(const char *name, caddr_t addr, const int offset)
+{
+	const char*	val;
+
+	val = smbios_getstring(addr, offset);
+	if (val != NULL)
+		setenv(name, val, 1);
+}
+
 #ifdef SMBIOS_SERIAL_NUMBERS
 
 #define	UUID_SIZE		16
@@ -187,7 +211,7 @@
 #endif
 
 static caddr_t
-smbios_parse_table(const caddr_t addr, const int ver)
+smbios_parse_table(const caddr_t addr)
 {
 	caddr_t		cp;
 	int		proc, size, osize, type;
@@ -206,7 +230,7 @@
 		smbios_setenv("smbios.system.version", addr, 0x06);
 #ifdef SMBIOS_SERIAL_NUMBERS
 		smbios_setenv("smbios.system.serial", addr, 0x07);
-		smbios_setuuid("smbios.system.uuid", addr + 0x08, ver);
+		smbios_setuuid("smbios.system.uuid", addr + 0x08, smbios.ver);
 #endif
 		break;
 
@@ -248,9 +272,9 @@
 		 */
 		proc = SMBIOS_GET8(addr, 0x18);
 		if ((proc & 0x07) == 1)
-			smbios_enabled_sockets++;
+			smbios.enabled_sockets++;
 		if ((proc & 0x40) != 0)
-			smbios_populated_sockets++;
+			smbios.populated_sockets++;
 		break;
 
 	case 6:		/* 3.3.7 Memory Module Information (Type 6, Obsolete) */
@@ -268,7 +292,7 @@
 		 */
 		osize = SMBIOS_GET8(addr, 0x0a) & 0x7f;
 		if (osize > 0 && osize < 22)
-			smbios_old_enabled_memory += 1 << (osize + 10);
+			smbios.old_enabled_memory += 1 << (osize + 10);
 		break;
 
 	case 17:	/* 3.3.18 Memory Device (Type 17) */
@@ -282,7 +306,7 @@
 		 */
 		size = SMBIOS_GET16(addr, 0x0c);
 		if (size != 0 && size != 0xffff)
-			smbios_enabled_memory += (size & 0x8000) != 0 ?
+			smbios.enabled_memory += (size & 0x8000) != 0 ?
 			    (size & 0x7fff) : (size << 10);
 		break;
 
@@ -298,54 +322,112 @@
 	return (cp + 2);
 }
 
+static caddr_t
+smbios_find_struct(int type)
+{
+	caddr_t		dmi;
+	int		i;
+
+	if (smbios.addr == NULL)
+		return (NULL);
+
+	for (dmi = smbios.addr, i = 0;
+	     dmi < smbios.addr + smbios.length && i < smbios.count; i++) {
+		if (SMBIOS_GET8(dmi, 0) == type)
+			return dmi;
+		/* Find structure terminator. */
+		dmi = SMBIOS_GETSTR(dmi);
+		while (SMBIOS_GET16(dmi, 0) != 0)
+			dmi++;
+		dmi += 2;
+	}
+
+	return (NULL);
+}
+
 void
 smbios_detect(void)
 {
-	char		buf[16];
-	caddr_t		addr, dmi, smbios;
-	size_t		count, length;
-	uint32_t	paddr;
-	int		i, major, minor, ver;
+	caddr_t		saddr, info;
+	u_int32_t	paddr;
 
 	/* Search signatures and validate checksums. */
-	smbios = smbios_sigsearch(PTOV(SMBIOS_START), SMBIOS_LENGTH);
-	if (smbios == NULL)
+	saddr = smbios_sigsearch(PTOV(SMBIOS_START), SMBIOS_LENGTH);
+	if (saddr == NULL)
 		return;
 
-	length = SMBIOS_GET16(smbios, 0x16);	/* Structure Table Length */
-	paddr = SMBIOS_GET32(smbios, 0x18);	/* Structure Table Address */
-	count = SMBIOS_GET16(smbios, 0x1c);	/* No of SMBIOS Structures */
-	ver = SMBIOS_GET8(smbios, 0x1e);	/* SMBIOS BCD Revision */
+	smbios.length = SMBIOS_GET16(saddr, 0x16);	/* Structure Table Length */
+	paddr = SMBIOS_GET32(saddr, 0x18);		/* Structure Table Address */
+	smbios.count = SMBIOS_GET16(saddr, 0x1c);	/* No of SMBIOS Structures */
+	smbios.ver = SMBIOS_GET8(saddr, 0x1e);		/* SMBIOS BCD Revision */
 
-	if (ver != 0) {
-		major = ver >> 4;
-		minor = ver & 0x0f;
-		if (major > 9 || minor > 9)
-			ver = 0;
+	if (smbios.ver != 0) {
+		smbios.major = smbios.ver >> 4;
+		smbios.minor = smbios.ver & 0x0f;
+		if (smbios.major > 9 || smbios.minor > 9)
+			smbios.ver = 0;
 	}
-	if (ver == 0) {
-		major = SMBIOS_GET8(smbios, 0x06); /* SMBIOS Major Version */
-		minor = SMBIOS_GET8(smbios, 0x07); /* SMBIOS Minor Version */
+	if (smbios.ver == 0) {
+		smbios.major = SMBIOS_GET8(saddr, 0x06);/* SMBIOS Major Version */
+		smbios.minor = SMBIOS_GET8(saddr, 0x07);/* SMBIOS Minor Version */
 	}
-	ver = (major << 8) | minor;
+	smbios.ver = (smbios.major << 8) | smbios.minor;
+	smbios.addr = PTOV(paddr);
 
-	addr = PTOV(paddr);
-	for (dmi = addr, i = 0; dmi < addr + length && i < count; i++)
-		dmi = smbios_parse_table(dmi, ver);
+	/* Get system information from SMBIOS */
+	info = smbios_find_struct(0x00);
+	if (info != NULL) {
+		smbios.bios_vendor = smbios_getstring(info, 0x04);
+	}
+	info = smbios_find_struct(0x01);
+	if (info != NULL) {
+		smbios.maker = smbios_getstring(info, 0x04);
+		smbios.product = smbios_getstring(info, 0x05);
+	}
+}
 
-	sprintf(buf, "%d.%d", major, minor);
+void
+smbios_setup(void)
+{
+	char		buf[16];
+	caddr_t		dmi;
+	int		i;
+
+	if (smbios.addr == NULL)
+		return;
+
+	for (dmi = smbios.addr, i = 0;
+	     dmi < smbios.addr + smbios.length && i < smbios.count; i++)
+		dmi = smbios_parse_table(dmi);
+
+	sprintf(buf, "%d.%d", smbios.major, smbios.minor);
 	setenv("smbios.version", buf, 1);
-	if (smbios_enabled_memory > 0 || smbios_old_enabled_memory > 0) {
-		sprintf(buf, "%u", smbios_enabled_memory > 0 ?
-		    smbios_enabled_memory : smbios_old_enabled_memory);
+	if (smbios.enabled_memory > 0 || smbios.old_enabled_memory > 0) {
+		sprintf(buf, "%u", smbios.enabled_memory > 0 ?
+		    smbios.enabled_memory : smbios.old_enabled_memory);
 		setenv("smbios.memory.enabled", buf, 1);
 	}
-	if (smbios_enabled_sockets > 0) {
-		sprintf(buf, "%u", smbios_enabled_sockets);
+	if (smbios.enabled_sockets > 0) {
+		sprintf(buf, "%u", smbios.enabled_sockets);
 		setenv("smbios.socket.enabled", buf, 1);
 	}
-	if (smbios_populated_sockets > 0) {
-		sprintf(buf, "%u", smbios_populated_sockets);
+	if (smbios.populated_sockets > 0) {
+		sprintf(buf, "%u", smbios.populated_sockets);
 		setenv("smbios.socket.populated", buf, 1);
 	}
 }
+
+static int
+smbios_match_str(const char* s1, const char* s2)
+{
+	return (s1 == NULL || (s2 != NULL && !strcmp(s1, s2)));
+}
+
+int
+smbios_match(const char* bios_vendor, const char* maker,
+    const char* product)
+{
+	return (smbios_match_str(bios_vendor, smbios.bios_vendor) &&
+	    smbios_match_str(maker, smbios.maker) &&
+	    smbios_match_str(product, smbios.product));
+}
Index: loader/main.c
===================================================================
--- loader/main.c	(revision 277404)
+++ loader/main.c	(working copy)
@@ -95,6 +95,9 @@
     bzero(&v86, sizeof(v86));
     v86.efl = PSL_RESERVED_DEFAULT | PSL_I;
 
+    /* early SMBIOS detection, won't populate kenv */
+    smbios_detect();
+
     /* 
      * Initialise the heap as early as possible.  Once this is done, malloc() is usable.
      */
@@ -180,8 +183,8 @@
     /* detect ACPI for future reference */
     biosacpi_detect();
 
-    /* detect SMBIOS for future reference */
-    smbios_detect();
+    /* populate SMBIOS kenv */
+    smbios_setup();
 
     /* detect PCI BIOS for future reference */
     biospci_detect();
