/*-1* Copyright (c) 1998 Michael Smith <[email protected]>2* All rights reserved.3*4* Redistribution and use in source and binary forms, with or without5* modification, are permitted provided that the following conditions6* are met:7* 1. Redistributions of source code must retain the above copyright8* notice, this list of conditions and the following disclaimer.9* 2. Redistributions in binary form must reproduce the above copyright10* notice, this list of conditions and the following disclaimer in the11* documentation and/or other materials provided with the distribution.12*13* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND14* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE15* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE16* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE17* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL18* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS19* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)20* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT21* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY22* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF23* SUCH DAMAGE.24*/2526/*27* Obtain memory configuration information from the BIOS28*/29#include <stand.h>30#include <machine/pc/bios.h>31#include "bootstrap.h"32#include "libi386.h"33#include "btxv86.h"34#include "smbios.h"3536vm_offset_t memtop, memtop_copyin, high_heap_base;37uint32_t bios_basemem, bios_extmem, high_heap_size;3839static struct bios_smap_xattr smap;4041/*42* Used to track which method was used to set BIOS memory43* regions.44*/45static uint8_t b_bios_probed;46#define B_BASEMEM_E820 0x147#define B_BASEMEM_12 0x248#define B_EXTMEM_E820 0x449#define B_EXTMEM_E801 0x850#define B_EXTMEM_8800 0x105152/*53* The minimum amount of memory to reserve in bios_extmem for the heap.54*/55#define HEAP_MIN (64 * 1024 * 1024)5657/*58* Products in this list need quirks to detect59* memory correctly. You need both maker and product as60* reported by smbios.61*/62/* e820 might not return useful extended memory */63#define BQ_DISTRUST_E820_EXTMEM 0x164struct bios_getmem_quirks {65const char *bios_vendor;66const char *maker;67const char *product;68int quirk;69};7071static struct bios_getmem_quirks quirks[] = {72{"coreboot", "Acer", "Peppy", BQ_DISTRUST_E820_EXTMEM},73{"coreboot", "Dell", "Wolf", BQ_DISTRUST_E820_EXTMEM},74{NULL, NULL, NULL, 0}75};7677static int78bios_getquirks(void)79{80int i;8182for (i = 0; quirks[i].quirk != 0; ++i) {83if (smbios_match(quirks[i].bios_vendor, quirks[i].maker,84quirks[i].product))85return (quirks[i].quirk);86}8788return (0);89}9091void92bios_getmem(void)93{94uint64_t size;9596/* Parse system memory map */97v86.ebx = 0;98do {99v86.ctl = V86_FLAGS;100v86.addr = 0x15; /* int 0x15 function 0xe820 */101v86.eax = 0xe820;102v86.ecx = sizeof(struct bios_smap_xattr);103v86.edx = SMAP_SIG;104v86.es = VTOPSEG(&smap);105v86.edi = VTOPOFF(&smap);106v86int();107if ((V86_CY(v86.efl)) || (v86.eax != SMAP_SIG))108break;109/* look for a low-memory segment that's large enough */110if ((smap.type == SMAP_TYPE_MEMORY) && (smap.base == 0) &&111(smap.length >= (512 * 1024))) {112bios_basemem = smap.length;113b_bios_probed |= B_BASEMEM_E820;114}115116/* look for the first segment in 'extended' memory */117if ((smap.type == SMAP_TYPE_MEMORY) &&118(smap.base == 0x100000) &&119!(bios_getquirks() & BQ_DISTRUST_E820_EXTMEM)) {120bios_extmem = smap.length;121b_bios_probed |= B_EXTMEM_E820;122}123124/*125* Look for the highest segment in 'extended' memory beyond126* 1MB but below 4GB.127*/128if ((smap.type == SMAP_TYPE_MEMORY) &&129(smap.base > 0x100000) &&130(smap.base < 0x100000000ull)) {131size = smap.length;132133/*134* If this segment crosses the 4GB boundary,135* truncate it.136*/137if (smap.base + size > 0x100000000ull)138size = 0x100000000ull - smap.base;139140/*141* To make maximum space for the kernel and the modules,142* set heap to use highest HEAP_MIN bytes below 4GB.143*/144if (high_heap_base < smap.base && size >= HEAP_MIN) {145high_heap_base = smap.base + size - HEAP_MIN;146high_heap_size = HEAP_MIN;147}148}149} while (v86.ebx != 0);150151/* Fall back to the old compatibility function for base memory */152if (bios_basemem == 0) {153v86.ctl = 0;154v86.addr = 0x12; /* int 0x12 */155v86int();156157bios_basemem = (v86.eax & 0xffff) * 1024;158b_bios_probed |= B_BASEMEM_12;159}160161/*162* Fall back through several compatibility functions for extended163* memory.164*/165if (bios_extmem == 0) {166v86.ctl = V86_FLAGS;167v86.addr = 0x15; /* int 0x15 function 0xe801 */168v86.eax = 0xe801;169v86int();170if (!(V86_CY(v86.efl))) {171/*172* Clear high_heap; it may end up overlapping173* with the segment we're determining here.174* Let the default "steal stuff from top of175* bios_extmem" code below pick up on it.176*/177high_heap_size = 0;178high_heap_base = 0;179180/*181* %cx is the number of 1KiB blocks between 1..16MiB.182* It can only be up to 0x3c00; if it's smaller then183* there's a PC AT memory hole so we can't treat184* it as contiguous.185*/186bios_extmem = (v86.ecx & 0xffff) * 1024;187if (bios_extmem == (1024 * 0x3c00))188bios_extmem += (v86.edx & 0xffff) * 64 * 1024;189190/* truncate bios_extmem */191if (bios_extmem > 0x3ff00000)192bios_extmem = 0x3ff00000;193194b_bios_probed |= B_EXTMEM_E801;195}196}197if (bios_extmem == 0) {198v86.ctl = 0;199v86.addr = 0x15; /* int 0x15 function 0x88 */200v86.eax = 0x8800;201v86int();202bios_extmem = (v86.eax & 0xffff) * 1024;203b_bios_probed |= B_EXTMEM_8800;204}205206/* Set memtop to actual top of memory */207if (high_heap_size != 0) {208memtop = memtop_copyin = high_heap_base;209} else {210memtop = memtop_copyin = 0x100000 + bios_extmem;211}212213/*214* If we have extended memory and did not find a suitable heap215* region in the SMAP, use the last HEAP_MIN of 'extended' memory as a216* high heap candidate.217*/218if (bios_extmem >= HEAP_MIN && high_heap_size < HEAP_MIN) {219high_heap_size = HEAP_MIN;220high_heap_base = memtop - HEAP_MIN;221memtop = memtop_copyin = high_heap_base;222}223}224225static int226command_biosmem(int argc, char *argv[])227{228int bq = bios_getquirks();229230printf("bios_basemem: 0x%llx\n", (unsigned long long)bios_basemem);231printf("bios_extmem: 0x%llx\n", (unsigned long long)bios_extmem);232printf("memtop: 0x%llx\n", (unsigned long long)memtop);233printf("high_heap_base: 0x%llx\n", (unsigned long long)high_heap_base);234printf("high_heap_size: 0x%llx\n", (unsigned long long)high_heap_size);235printf("bios_quirks: 0x%02x", bq);236if (bq & BQ_DISTRUST_E820_EXTMEM)237printf(" BQ_DISTRUST_E820_EXTMEM");238printf("\n");239printf("b_bios_probed: 0x%02x", (int)b_bios_probed);240if (b_bios_probed & B_BASEMEM_E820)241printf(" B_BASEMEM_E820");242if (b_bios_probed & B_BASEMEM_12)243printf(" B_BASEMEM_12");244if (b_bios_probed & B_EXTMEM_E820)245printf(" B_EXTMEM_E820");246if (b_bios_probed & B_EXTMEM_E801)247printf(" B_EXTMEM_E801");248if (b_bios_probed & B_EXTMEM_8800)249printf(" B_EXTMEM_8800");250printf("\n");251252return (CMD_OK);253}254255COMMAND_SET(biosmem, "biosmem", "show BIOS memory setup", command_biosmem);256257258