freebsd-dev/stand/i386/libi386/biosmem.c

Ignoring revisions in .git-blame-ignore-revs. Click here to bypass and see the normal blame view.

260 lines
7.2 KiB
C
Raw Normal View History

/*-
* Copyright (c) 1998 Michael Smith <msmith@freebsd.org>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*/
#include <sys/cdefs.h>
__FBSDID("$FreeBSD$");
/*
* Obtain memory configuration information from the BIOS
*/
#include <stand.h>
#include <machine/pc/bios.h>
#include "bootstrap.h"
#include "libi386.h"
#include "btxv86.h"
#include "smbios.h"
vm_offset_t memtop, memtop_copyin, high_heap_base;
uint32_t bios_basemem, bios_extmem, high_heap_size;
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 (64 * 1024 * 1024)
/*
* Products in this list need quirks to detect
* memory correctly. You need both maker and product as
* reported by smbios.
*/
/* e820 might not return useful extended memory */
#define BQ_DISTRUST_E820_EXTMEM 0x1
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},
{"coreboot", "Dell", "Wolf", 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)
{
uint64_t size;
/* Parse system memory map */
v86.ebx = 0;
do {
v86.ctl = V86_FLAGS;
v86.addr = 0x15; /* int 0x15 function 0xe820 */
v86.eax = 0xe820;
v86.ecx = sizeof(struct bios_smap_xattr);
v86.edx = SMAP_SIG;
v86.es = VTOPSEG(&smap);
v86.edi = VTOPOFF(&smap);
v86int();
if ((V86_CY(v86.efl)) || (v86.eax != SMAP_SIG))
break;
/* look for a low-memory segment that's large enough */
if ((smap.type == SMAP_TYPE_MEMORY) && (smap.base == 0) &&
(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) &&
!(bios_getquirks() & BQ_DISTRUST_E820_EXTMEM)) {
bios_extmem = smap.length;
b_bios_probed |= B_EXTMEM_E820;
}
/*
loader: biosmem allocate heap just below 4GB The current biosmem code is walking bios smap entries and looking for smap entry just below 4GB line, if there is such entry, its base and size is set for heap base and size. Instead of entry base, we should use last HEAP_MIN (currently 64MB) bytes just below 4GB, to make maximum space for kernel and modules. The problem was revealed on ASUS B350M-A system board, an AMD Ryzen 3 1200 CPU memory map: SMAP type=01 base=0000000000000000 len=000000000009d400 attr=01 SMAP type=02 base=000000000009d400 len=0000000000002c00 attr=01 SMAP type=02 base=00000000000e0000 len=0000000000020000 attr=01 SMAP type=01 base=0000000000100000 len=0000000009c00000 attr=01 SMAP type=02 base=0000000009d00000 len=0000000000300000 attr=01 SMAP type=01 base=000000000a000000 len=00000000be69b000 attr=01 SMAP type=03 base=00000000c869b000 len=0000000000016000 attr=01 SMAP type=01 base=00000000c86b1000 len=00000000124e7000 attr=01 SMAP type=02 base=00000000dab98000 len=0000000000138000 attr=01 SMAP type=03 base=00000000dacd0000 len=0000000000008000 attr=01 SMAP type=01 base=00000000dacd8000 len=0000000000100000 attr=01 SMAP type=04 base=00000000dadd8000 len=00000000003b3000 attr=01 SMAP type=02 base=00000000db18b000 len=0000000000d42000 attr=01 SMAP type=01 base=00000000dbecd000 len=0000000002133000 attr=01 SMAP type=01 base=0000000100000000 len=000000011f380000 attr=01 SMAP type=02 base=00000000de000000 len=0000000002000000 attr=01 SMAP type=02 base=00000000f8000000 len=0000000004000000 attr=01 SMAP type=02 base=00000000fdf00000 len=0000000000100000 attr=01 SMAP type=02 base=00000000fea00000 len=0000000000010000 attr=01 SMAP type=02 base=00000000feb80000 len=0000000000082000 attr=01 SMAP type=02 base=00000000fec10000 len=0000000000001000 attr=01 SMAP type=02 base=00000000fec30000 len=0000000000001000 attr=01 SMAP type=02 base=00000000fed00000 len=0000000000001000 attr=01 SMAP type=02 base=00000000fed40000 len=0000000000005000 attr=01 SMAP type=02 base=00000000fed80000 len=0000000000010000 attr=01 SMAP type=02 base=00000000fedc2000 len=000000000000e000 attr=01 SMAP type=02 base=00000000fedd4000 len=0000000000002000 attr=01 SMAP type=02 base=00000000fee00000 len=0000000000100000 attr=01 SMAP type=02 base=00000000ff000000 len=0000000001000000 attr=01 Reviewed by: imp Differential Revision: https://reviews.freebsd.org/D12368
2017-09-18 15:17:01 +00:00
* Look for the highest segment in 'extended' memory beyond
* 1MB but below 4GB.
*/
if ((smap.type == SMAP_TYPE_MEMORY) &&
(smap.base > 0x100000) &&
(smap.base < 0x100000000ull)) {
size = smap.length;
/*
* If this segment crosses the 4GB boundary,
* truncate it.
*/
if (smap.base + size > 0x100000000ull)
size = 0x100000000ull - smap.base;
loader: biosmem allocate heap just below 4GB The current biosmem code is walking bios smap entries and looking for smap entry just below 4GB line, if there is such entry, its base and size is set for heap base and size. Instead of entry base, we should use last HEAP_MIN (currently 64MB) bytes just below 4GB, to make maximum space for kernel and modules. The problem was revealed on ASUS B350M-A system board, an AMD Ryzen 3 1200 CPU memory map: SMAP type=01 base=0000000000000000 len=000000000009d400 attr=01 SMAP type=02 base=000000000009d400 len=0000000000002c00 attr=01 SMAP type=02 base=00000000000e0000 len=0000000000020000 attr=01 SMAP type=01 base=0000000000100000 len=0000000009c00000 attr=01 SMAP type=02 base=0000000009d00000 len=0000000000300000 attr=01 SMAP type=01 base=000000000a000000 len=00000000be69b000 attr=01 SMAP type=03 base=00000000c869b000 len=0000000000016000 attr=01 SMAP type=01 base=00000000c86b1000 len=00000000124e7000 attr=01 SMAP type=02 base=00000000dab98000 len=0000000000138000 attr=01 SMAP type=03 base=00000000dacd0000 len=0000000000008000 attr=01 SMAP type=01 base=00000000dacd8000 len=0000000000100000 attr=01 SMAP type=04 base=00000000dadd8000 len=00000000003b3000 attr=01 SMAP type=02 base=00000000db18b000 len=0000000000d42000 attr=01 SMAP type=01 base=00000000dbecd000 len=0000000002133000 attr=01 SMAP type=01 base=0000000100000000 len=000000011f380000 attr=01 SMAP type=02 base=00000000de000000 len=0000000002000000 attr=01 SMAP type=02 base=00000000f8000000 len=0000000004000000 attr=01 SMAP type=02 base=00000000fdf00000 len=0000000000100000 attr=01 SMAP type=02 base=00000000fea00000 len=0000000000010000 attr=01 SMAP type=02 base=00000000feb80000 len=0000000000082000 attr=01 SMAP type=02 base=00000000fec10000 len=0000000000001000 attr=01 SMAP type=02 base=00000000fec30000 len=0000000000001000 attr=01 SMAP type=02 base=00000000fed00000 len=0000000000001000 attr=01 SMAP type=02 base=00000000fed40000 len=0000000000005000 attr=01 SMAP type=02 base=00000000fed80000 len=0000000000010000 attr=01 SMAP type=02 base=00000000fedc2000 len=000000000000e000 attr=01 SMAP type=02 base=00000000fedd4000 len=0000000000002000 attr=01 SMAP type=02 base=00000000fee00000 len=0000000000100000 attr=01 SMAP type=02 base=00000000ff000000 len=0000000001000000 attr=01 Reviewed by: imp Differential Revision: https://reviews.freebsd.org/D12368
2017-09-18 15:17:01 +00:00
/*
* To make maximum space for the kernel and the modules,
* set heap to use highest HEAP_MIN bytes below 4GB.
*/
if (high_heap_base < smap.base && size >= HEAP_MIN) {
high_heap_base = smap.base + size - HEAP_MIN;
high_heap_size = HEAP_MIN;
}
}
} while (v86.ebx != 0);
/* Fall back to the old compatibility function for base memory */
if (bios_basemem == 0) {
v86.ctl = 0;
v86.addr = 0x12; /* int 0x12 */
v86int();
bios_basemem = (v86.eax & 0xffff) * 1024;
b_bios_probed |= B_BASEMEM_12;
}
/*
* Fall back through several compatibility functions for extended
* memory.
*/
if (bios_extmem == 0) {
v86.ctl = V86_FLAGS;
v86.addr = 0x15; /* int 0x15 function 0xe801 */
v86.eax = 0xe801;
v86int();
if (!(V86_CY(v86.efl))) {
/*
* 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) {
v86.ctl = 0;
v86.addr = 0x15; /* int 0x15 function 0x88 */
v86.eax = 0x8800;
v86int();
bios_extmem = (v86.eax & 0xffff) * 1024;
b_bios_probed |= B_EXTMEM_8800;
}
/* Set memtop to actual top of memory */
loader: biosmem allocate heap just below 4GB The current biosmem code is walking bios smap entries and looking for smap entry just below 4GB line, if there is such entry, its base and size is set for heap base and size. Instead of entry base, we should use last HEAP_MIN (currently 64MB) bytes just below 4GB, to make maximum space for kernel and modules. The problem was revealed on ASUS B350M-A system board, an AMD Ryzen 3 1200 CPU memory map: SMAP type=01 base=0000000000000000 len=000000000009d400 attr=01 SMAP type=02 base=000000000009d400 len=0000000000002c00 attr=01 SMAP type=02 base=00000000000e0000 len=0000000000020000 attr=01 SMAP type=01 base=0000000000100000 len=0000000009c00000 attr=01 SMAP type=02 base=0000000009d00000 len=0000000000300000 attr=01 SMAP type=01 base=000000000a000000 len=00000000be69b000 attr=01 SMAP type=03 base=00000000c869b000 len=0000000000016000 attr=01 SMAP type=01 base=00000000c86b1000 len=00000000124e7000 attr=01 SMAP type=02 base=00000000dab98000 len=0000000000138000 attr=01 SMAP type=03 base=00000000dacd0000 len=0000000000008000 attr=01 SMAP type=01 base=00000000dacd8000 len=0000000000100000 attr=01 SMAP type=04 base=00000000dadd8000 len=00000000003b3000 attr=01 SMAP type=02 base=00000000db18b000 len=0000000000d42000 attr=01 SMAP type=01 base=00000000dbecd000 len=0000000002133000 attr=01 SMAP type=01 base=0000000100000000 len=000000011f380000 attr=01 SMAP type=02 base=00000000de000000 len=0000000002000000 attr=01 SMAP type=02 base=00000000f8000000 len=0000000004000000 attr=01 SMAP type=02 base=00000000fdf00000 len=0000000000100000 attr=01 SMAP type=02 base=00000000fea00000 len=0000000000010000 attr=01 SMAP type=02 base=00000000feb80000 len=0000000000082000 attr=01 SMAP type=02 base=00000000fec10000 len=0000000000001000 attr=01 SMAP type=02 base=00000000fec30000 len=0000000000001000 attr=01 SMAP type=02 base=00000000fed00000 len=0000000000001000 attr=01 SMAP type=02 base=00000000fed40000 len=0000000000005000 attr=01 SMAP type=02 base=00000000fed80000 len=0000000000010000 attr=01 SMAP type=02 base=00000000fedc2000 len=000000000000e000 attr=01 SMAP type=02 base=00000000fedd4000 len=0000000000002000 attr=01 SMAP type=02 base=00000000fee00000 len=0000000000100000 attr=01 SMAP type=02 base=00000000ff000000 len=0000000001000000 attr=01 Reviewed by: imp Differential Revision: https://reviews.freebsd.org/D12368
2017-09-18 15:17:01 +00:00
if (high_heap_size != 0) {
memtop = memtop_copyin = high_heap_base;
} else {
memtop = memtop_copyin = 0x100000 + bios_extmem;
}
/*
* If we have extended memory and did not find a suitable heap
* region in the SMAP, use the last HEAP_MIN of 'extended' memory as a
* high heap candidate.
*/
if (bios_extmem >= HEAP_MIN && high_heap_size < HEAP_MIN) {
high_heap_size = HEAP_MIN;
high_heap_base = memtop - HEAP_MIN;
loader: biosmem allocate heap just below 4GB The current biosmem code is walking bios smap entries and looking for smap entry just below 4GB line, if there is such entry, its base and size is set for heap base and size. Instead of entry base, we should use last HEAP_MIN (currently 64MB) bytes just below 4GB, to make maximum space for kernel and modules. The problem was revealed on ASUS B350M-A system board, an AMD Ryzen 3 1200 CPU memory map: SMAP type=01 base=0000000000000000 len=000000000009d400 attr=01 SMAP type=02 base=000000000009d400 len=0000000000002c00 attr=01 SMAP type=02 base=00000000000e0000 len=0000000000020000 attr=01 SMAP type=01 base=0000000000100000 len=0000000009c00000 attr=01 SMAP type=02 base=0000000009d00000 len=0000000000300000 attr=01 SMAP type=01 base=000000000a000000 len=00000000be69b000 attr=01 SMAP type=03 base=00000000c869b000 len=0000000000016000 attr=01 SMAP type=01 base=00000000c86b1000 len=00000000124e7000 attr=01 SMAP type=02 base=00000000dab98000 len=0000000000138000 attr=01 SMAP type=03 base=00000000dacd0000 len=0000000000008000 attr=01 SMAP type=01 base=00000000dacd8000 len=0000000000100000 attr=01 SMAP type=04 base=00000000dadd8000 len=00000000003b3000 attr=01 SMAP type=02 base=00000000db18b000 len=0000000000d42000 attr=01 SMAP type=01 base=00000000dbecd000 len=0000000002133000 attr=01 SMAP type=01 base=0000000100000000 len=000000011f380000 attr=01 SMAP type=02 base=00000000de000000 len=0000000002000000 attr=01 SMAP type=02 base=00000000f8000000 len=0000000004000000 attr=01 SMAP type=02 base=00000000fdf00000 len=0000000000100000 attr=01 SMAP type=02 base=00000000fea00000 len=0000000000010000 attr=01 SMAP type=02 base=00000000feb80000 len=0000000000082000 attr=01 SMAP type=02 base=00000000fec10000 len=0000000000001000 attr=01 SMAP type=02 base=00000000fec30000 len=0000000000001000 attr=01 SMAP type=02 base=00000000fed00000 len=0000000000001000 attr=01 SMAP type=02 base=00000000fed40000 len=0000000000005000 attr=01 SMAP type=02 base=00000000fed80000 len=0000000000010000 attr=01 SMAP type=02 base=00000000fedc2000 len=000000000000e000 attr=01 SMAP type=02 base=00000000fedd4000 len=0000000000002000 attr=01 SMAP type=02 base=00000000fee00000 len=0000000000100000 attr=01 SMAP type=02 base=00000000ff000000 len=0000000001000000 attr=01 Reviewed by: imp Differential Revision: https://reviews.freebsd.org/D12368
2017-09-18 15:17:01 +00:00
memtop = memtop_copyin = high_heap_base;
}
}
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(biosmem, "biosmem", "show BIOS memory setup", command_biosmem);