131921Sbrian/*-
231921Sbrian * Copyright (c) 1998 Michael Smith <msmith@freebsd.org>
331921Sbrian * All rights reserved.
431921Sbrian *
531921Sbrian * Redistribution and use in source and binary forms, with or without
631921Sbrian * modification, are permitted provided that the following conditions
731921Sbrian * are met:
831921Sbrian * 1. Redistributions of source code must retain the above copyright
931921Sbrian *    notice, this list of conditions and the following disclaimer.
1031921Sbrian * 2. Redistributions in binary form must reproduce the above copyright
1131921Sbrian *    notice, this list of conditions and the following disclaimer in the
1231921Sbrian *    documentation and/or other materials provided with the distribution.
1331921Sbrian *
1431921Sbrian * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
1531921Sbrian * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
1631921Sbrian * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
1731921Sbrian * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
1831921Sbrian * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
1931921Sbrian * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
2031921Sbrian * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
2131921Sbrian * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
2231921Sbrian * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
2331921Sbrian * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
2431921Sbrian * SUCH DAMAGE.
2531921Sbrian */
2646686Sbrian
2730715Sbrian#include <sys/cdefs.h>
2830715Sbrian__FBSDID("$FreeBSD$");
2931196Sbrian
3044279Sbrian/*
3144279Sbrian * Obtain memory configuration information from the BIOS
3244279Sbrian */
3344279Sbrian#include <stand.h>
3444279Sbrian#include <machine/pc/bios.h>
3544279Sbrian#include "libi386.h"
3644279Sbrian#include "btxv86.h"
3746085Sbrian
3830715Sbrianvm_offset_t	memtop, memtop_copyin, high_heap_base;
3931121Sbrianuint32_t	bios_basemem, bios_extmem, high_heap_size;
4046686Sbrian
4137192Sbrianstatic struct bios_smap smap;
4234539Sbrian
4337192Sbrian/*
4431196Sbrian * The minimum amount of memory to reserve in bios_extmem for the heap.
4530715Sbrian */
4630715Sbrian#define	HEAP_MIN	(3 * 1024 * 1024)
4730715Sbrian
4846686Sbrianvoid
4946686Sbrianbios_getmem(void)
5030715Sbrian{
5130715Sbrian    uint64_t size;
5230715Sbrian
5336453Sbrian    /* Parse system memory map */
5437010Sbrian    v86.ebx = 0;
5530715Sbrian    do {
5630715Sbrian	v86.ctl = V86_FLAGS;
5730715Sbrian	v86.addr = 0x15;		/* int 0x15 function 0xe820*/
5830715Sbrian	v86.eax = 0xe820;
5930715Sbrian	v86.ecx = sizeof(struct bios_smap);
6031343Sbrian	v86.edx = SMAP_SIG;
6136285Sbrian	v86.es = VTOPSEG(&smap);
6231343Sbrian	v86.edi = VTOPOFF(&smap);
6330715Sbrian	v86int();
6431196Sbrian	if ((V86_CY(v86.efl)) || (v86.eax != SMAP_SIG))
6536285Sbrian	    break;
6636285Sbrian	/* look for a low-memory segment that's large enough */
6736285Sbrian	if ((smap.type == SMAP_TYPE_MEMORY) && (smap.base == 0) &&
6836285Sbrian	    (smap.length >= (512 * 1024)))
6931196Sbrian	    bios_basemem = smap.length;
7036285Sbrian	/* look for the first segment in 'extended' memory */
7136285Sbrian	if ((smap.type == SMAP_TYPE_MEMORY) && (smap.base == 0x100000)) {
7236285Sbrian	    bios_extmem = smap.length;
7336285Sbrian	}
7436285Sbrian
7536285Sbrian	/*
7636285Sbrian	 * Look for the largest segment in 'extended' memory beyond
7736285Sbrian	 * 1MB but below 4GB.
7836285Sbrian	 */
7936285Sbrian	if ((smap.type == SMAP_TYPE_MEMORY) && (smap.base > 0x100000) &&
8036285Sbrian	    (smap.base < 0x100000000ull)) {
8136285Sbrian	    size = smap.length;
8236285Sbrian
8336285Sbrian	    /*
8436285Sbrian	     * If this segment crosses the 4GB boundary, truncate it.
8536285Sbrian	     */
8636285Sbrian	    if (smap.base + size > 0x100000000ull)
8736465Sbrian		size = 0x100000000ull - smap.base;
8836465Sbrian
8936285Sbrian	    if (size > high_heap_size) {
9036285Sbrian		high_heap_size = size;
9136465Sbrian		high_heap_base = smap.base;
9236465Sbrian	    }
9336285Sbrian	}
9436285Sbrian    } while (v86.ebx != 0);
9536285Sbrian
9636285Sbrian    /* Fall back to the old compatibility function for base memory */
9736285Sbrian    if (bios_basemem == 0) {
9836285Sbrian	v86.ctl = 0;
9931196Sbrian	v86.addr = 0x12;		/* int 0x12 */
10036285Sbrian	v86int();
10131196Sbrian
10236285Sbrian	bios_basemem = (v86.eax & 0xffff) * 1024;
10336285Sbrian    }
10436285Sbrian
10531196Sbrian    /* Fall back through several compatibility functions for extended memory */
10636285Sbrian    if (bios_extmem == 0) {
10731196Sbrian	v86.ctl = V86_FLAGS;
10831203Sbrian	v86.addr = 0x15;		/* int 0x15 function 0xe801*/
10936285Sbrian	v86.eax = 0xe801;
11036285Sbrian	v86int();
11131203Sbrian	if (!(V86_CY(v86.efl))) {
11236285Sbrian	    bios_extmem = ((v86.ecx & 0xffff) + ((v86.edx & 0xffff) * 64)) * 1024;
11331203Sbrian	}
11436285Sbrian    }
11536285Sbrian    if (bios_extmem == 0) {
11636285Sbrian	v86.ctl = 0;
11736285Sbrian	v86.addr = 0x15;		/* int 0x15 function 0x88*/
11836285Sbrian	v86.eax = 0x8800;
11936285Sbrian	v86int();
12036285Sbrian	bios_extmem = (v86.eax & 0xffff) * 1024;
12136285Sbrian    }
12236285Sbrian
12336285Sbrian    /* Set memtop to actual top of memory */
12436285Sbrian    memtop = memtop_copyin = 0x100000 + bios_extmem;
12536285Sbrian
12631203Sbrian    /*
12744279Sbrian     * If we have extended memory and did not find a suitable heap
12844279Sbrian     * region in the SMAP, use the last 3MB of 'extended' memory as a
12944279Sbrian     * high heap candidate.
13044279Sbrian     */
13144279Sbrian    if (bios_extmem >= HEAP_MIN && high_heap_size < HEAP_MIN) {
13244279Sbrian	high_heap_size = HEAP_MIN;
13344279Sbrian	high_heap_base = memtop - HEAP_MIN;
13444279Sbrian    }
13544279Sbrian}
13644279Sbrian