Call for testers, change BIOS memory detection

From: Michael Gmelin <freebsd_at_grem.de>
Date: Sat, 7 Feb 2015 18:56:31 +0100
The patch to CURRENT below is an extended version of a patch originally
done by Adrian and accomplishes the following:

 * Add a quirks table based on BIOS vendor / maker /
   product (NULL matches any), making use of the new
   smbios_match function
 * pass in an smap+acpi entry size for e820; like what
   biossmap.c does;
 * don't blindly believe e820 if it doesn't give us
   enough memory to successfully load things, but ONLY
   if the machine is listed as BQ_DISTRUST_E820_EXTMEM
   in the quirks table (in this patch this is only the
   Acer C720 aka Peppy, since it was the reason to
   write this patch). There are probably other models
   out there with these issues, but without data it's
   probably best to add them one at a time.
 * don't blindly concat cx/dx with e801 - ensure that
   cx indicates there's no hole and there's a full 15mb
   of ram before concat'ing;
 * truncate how much RAM we probe for via e801 (which
   adrian doesn't entirely think is needed)
 * if we use e801, don't use the heap as-is; use the
   end of bios_extmem instead as if we didn't get
   anything from E820;
 * add a boot loader command "biosmem" which give
   details about how memory was detected

The code has already been reviewed and more details can be found here:
https://reviews.freebsd.org/D1741

It would be great, if a few more people could take a look at this and
test it before it's committed to HEAD.

Thanks,
Michael

Index: sys/boot/i386/libi386/biosmem.c
===================================================================
diff --git a/head/sys/boot/i386/libi386/biosmem.c b/head/sys/boot/i386/libi386/biosmem.c
--- a/head/sys/boot/i386/libi386/biosmem.c	(revision 277957)
+++ b/head/sys/boot/i386/libi386/biosmem.c	(working copy)
_at__at_ -32,6 +32,7 _at__at_
  */
 #include <stand.h>
 #include <machine/pc/bios.h>
+#include "bootstrap.h"
 #include "libi386.h"
 #include "btxv86.h"
 
_at__at_ -38,13 +39,56 _at__at_
 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 (quirks[i].quirk);
+
+    return (0);
+}
+
 void
 bios_getmem(void)
 {
_at__at_ -56,7 +100,7 _at__at_
 	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);
_at__at_ -65,11 +109,17 _at__at_
 	    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;
 	}
 
 	/*
_at__at_ -100,6 +150,7 _at__at_
 	v86int();
 	
 	bios_basemem = (v86.eax & 0xffff) * 1024;
+	b_bios_probed |= B_BASEMEM_12;
     }
 
     /* Fall back through several compatibility functions for extended memory */
_at__at_ -109,7 +160,29 _at__at_
 	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) {
_at__at_ -118,6 +191,7 _at__at_
 	v86.eax = 0x8800;
 	v86int();
 	bios_extmem = (v86.eax & 0xffff) * 1024;
+	b_bios_probed |= B_EXTMEM_8800;
     }
 
     /* Set memtop to actual top of memory */
_at__at_ -132,4 +206,36 _at__at_
 	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);



-- 
Michael Gmelin
Received on Sat Feb 07 2015 - 17:03:22 UTC

This archive was generated by hypermail 2.4.0 : Wed May 19 2021 - 11:40:55 UTC