Call for testers, change BIOS memory detection
Michael Gmelin
freebsd at grem.de
Sat Feb 7 18:03:22 UTC 2015
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)
@@ -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 (quirks[i].quirk);
+
+ 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);
--
Michael Gmelin
More information about the freebsd-current
mailing list