Rewrite the i386 memory probe:

- Check for SMAP data from the loader first.  If it exists, don't bother
  doing any VM86 calls at all.  This will be more friendly for non-BIOS
  boot environments such as EFI, etc.
- Move the base memory setup into a new basemem_setup() routine instead
  of duplicating it.
- Simplify the XEN case by removing all of the VM86/SMAP parsing code rather
  than just jumping over it.
- Adjust some comments to better explain the code flow.

MFC after:	2 weeks
This commit is contained in:
jhb 2010-09-27 19:36:15 +00:00
parent f0a3248def
commit 1d47059ff9

View File

@ -1950,6 +1950,7 @@ sdtossd(sd, ssd)
ssd->ssd_gran = sd->sd_gran;
}
#ifndef XEN
static int
add_smap_entry(struct bios_smap *smap, vm_paddr_t *physmap, int *physmap_idxp)
{
@ -2029,78 +2030,13 @@ add_smap_entry(struct bios_smap *smap, vm_paddr_t *physmap, int *physmap_idxp)
return (1);
}
/*
* Populate the (physmap) array with base/bound pairs describing the
* available physical memory in the system, then test this memory and
* build the phys_avail array describing the actually-available memory.
*
* If we cannot accurately determine the physical memory map, then use
* value from the 0xE801 call, and failing that, the RTC.
*
* Total memory size may be set by the kernel environment variable
* hw.physmem or the compile-time define MAXMEM.
*
* XXX first should be vm_paddr_t.
*/
static void
getmemsize(int first)
basemem_setup(void)
{
int i, off, physmap_idx, pa_indx, da_indx;
int hasbrokenint12, has_smap;
u_long physmem_tunable;
u_int extmem;
struct vm86frame vmf;
struct vm86context vmc;
vm_paddr_t pa, physmap[PHYSMAP_SIZE];
vm_paddr_t pa;
pt_entry_t *pte;
struct bios_smap *smap, *smapbase, *smapend;
u_int32_t smapsize;
quad_t dcons_addr, dcons_size;
caddr_t kmdp;
int i;
has_smap = 0;
#ifdef XBOX
if (arch_i386_is_xbox) {
/*
* We queried the memory size before, so chop off 4MB for
* the framebuffer and inform the OS of this.
*/
physmap[0] = 0;
physmap[1] = (arch_i386_xbox_memsize * 1024 * 1024) - XBOX_FB_SIZE;
physmap_idx = 0;
goto physmap_done;
}
#endif
#if defined(XEN)
has_smap = 0;
Maxmem = xen_start_info->nr_pages - init_first;
physmem = Maxmem;
basemem = 0;
physmap[0] = init_first << PAGE_SHIFT;
physmap[1] = ptoa(Maxmem) - round_page(MSGBUF_SIZE);
physmap_idx = 0;
goto physmap_done;
#endif
hasbrokenint12 = 0;
TUNABLE_INT_FETCH("hw.hasbrokenint12", &hasbrokenint12);
bzero(&vmf, sizeof(vmf));
bzero(physmap, sizeof(physmap));
basemem = 0;
/*
* Some newer BIOSes has broken INT 12H implementation which cause
* kernel panic immediately. In this case, we need to scan SMAP
* with INT 15:E820 first, then determine base memory size.
*/
if (hasbrokenint12) {
goto int15e820;
}
/*
* Perform "base memory" related probes & setup
*/
vm86_intcall(0x12, &vmf);
basemem = vmf.vmf_ax;
if (basemem > 640) {
printf("Preposterous BIOS basemem of %uK, truncating to 640K\n",
basemem);
@ -2140,12 +2076,69 @@ getmemsize(int first)
pte = (pt_entry_t *)vm86paddr;
for (i = basemem / 4; i < 160; i++)
pte[i] = (i << PAGE_SHIFT) | PG_V | PG_RW | PG_U;
}
#endif
/*
* Populate the (physmap) array with base/bound pairs describing the
* available physical memory in the system, then test this memory and
* build the phys_avail array describing the actually-available memory.
*
* If we cannot accurately determine the physical memory map, then use
* value from the 0xE801 call, and failing that, the RTC.
*
* Total memory size may be set by the kernel environment variable
* hw.physmem or the compile-time define MAXMEM.
*
* XXX first should be vm_paddr_t.
*/
static void
getmemsize(int first)
{
int has_smap, off, physmap_idx, pa_indx, da_indx;
u_long physmem_tunable;
vm_paddr_t physmap[PHYSMAP_SIZE];
pt_entry_t *pte;
quad_t dcons_addr, dcons_size;
#ifndef XEN
int hasbrokenint12, i;
u_int extmem;
struct vm86frame vmf;
struct vm86context vmc;
vm_paddr_t pa;
struct bios_smap *smap, *smapbase, *smapend;
u_int32_t smapsize;
caddr_t kmdp;
#endif
has_smap = 0;
#if defined(XEN)
Maxmem = xen_start_info->nr_pages - init_first;
physmem = Maxmem;
basemem = 0;
physmap[0] = init_first << PAGE_SHIFT;
physmap[1] = ptoa(Maxmem) - round_page(MSGBUF_SIZE);
physmap_idx = 0;
#else
#ifdef XBOX
if (arch_i386_is_xbox) {
/*
* We queried the memory size before, so chop off 4MB for
* the framebuffer and inform the OS of this.
*/
physmap[0] = 0;
physmap[1] = (arch_i386_xbox_memsize * 1024 * 1024) - XBOX_FB_SIZE;
physmap_idx = 0;
goto physmap_done;
}
#endif
bzero(&vmf, sizeof(vmf));
bzero(physmap, sizeof(physmap));
basemem = 0;
int15e820:
/*
* Fetch the memory map with INT 15:E820. First, check to see
* if the loader supplied it and use that if so. Otherwise,
* use vm86 to invoke the BIOS call directly.
* Check if the loader supplied an SMAP memory map. If so,
* use that and do not make any VM86 calls.
*/
physmap_idx = 0;
smapbase = NULL;
@ -2156,9 +2149,10 @@ int15e820:
smapbase = (struct bios_smap *)preload_search_info(kmdp,
MODINFO_METADATA | MODINFOMD_SMAP);
if (smapbase != NULL) {
/* subr_module.c says:
/*
* subr_module.c says:
* "Consumer may safely assume that size value precedes data."
* ie: an int32_t immediately precedes smap.
* ie: an int32_t immediately precedes SMAP.
*/
smapsize = *((u_int32_t *)smapbase - 1);
smapend = (struct bios_smap *)((uintptr_t)smapbase + smapsize);
@ -2167,33 +2161,50 @@ int15e820:
for (smap = smapbase; smap < smapend; smap++)
if (!add_smap_entry(smap, physmap, &physmap_idx))
break;
} else {
/*
* map page 1 R/W into the kernel page table so we can use it
* as a buffer. The kernel will unmap this page later.
*/
pmap_kenter(KERNBASE + (1 << PAGE_SHIFT), 1 << PAGE_SHIFT);
vmc.npages = 0;
smap = (void *)vm86_addpage(&vmc, 1, KERNBASE +
(1 << PAGE_SHIFT));
vm86_getptr(&vmc, (vm_offset_t)smap, &vmf.vmf_es, &vmf.vmf_di);
vmf.vmf_ebx = 0;
do {
vmf.vmf_eax = 0xE820;
vmf.vmf_edx = SMAP_SIG;
vmf.vmf_ecx = sizeof(struct bios_smap);
i = vm86_datacall(0x15, &vmf, &vmc);
if (i || vmf.vmf_eax != SMAP_SIG)
break;
has_smap = 1;
if (!add_smap_entry(smap, physmap, &physmap_idx))
break;
} while (vmf.vmf_ebx != 0);
goto have_smap;
}
/*
* Perform "base memory" related probes & setup based on SMAP
* Some newer BIOSes have a broken INT 12H implementation
* which causes a kernel panic immediately. In this case, we
* need use the SMAP to determine the base memory size.
*/
hasbrokenint12 = 0;
TUNABLE_INT_FETCH("hw.hasbrokenint12", &hasbrokenint12);
if (hasbrokenint12 == 0) {
/* Use INT12 to determine base memory size. */
vm86_intcall(0x12, &vmf);
basemem = vmf.vmf_ax;
basemem_setup();
}
/*
* Fetch the memory map with INT 15:E820. Map page 1 R/W into
* the kernel page table so we can use it as a buffer. The
* kernel will unmap this page later.
*/
pmap_kenter(KERNBASE + (1 << PAGE_SHIFT), 1 << PAGE_SHIFT);
vmc.npages = 0;
smap = (void *)vm86_addpage(&vmc, 1, KERNBASE + (1 << PAGE_SHIFT));
vm86_getptr(&vmc, (vm_offset_t)smap, &vmf.vmf_es, &vmf.vmf_di);
vmf.vmf_ebx = 0;
do {
vmf.vmf_eax = 0xE820;
vmf.vmf_edx = SMAP_SIG;
vmf.vmf_ecx = sizeof(struct bios_smap);
i = vm86_datacall(0x15, &vmf, &vmc);
if (i || vmf.vmf_eax != SMAP_SIG)
break;
has_smap = 1;
if (!add_smap_entry(smap, physmap, &physmap_idx))
break;
} while (vmf.vmf_ebx != 0);
have_smap:
/*
* If we didn't fetch the "base memory" size from INT12,
* figure it out from the SMAP (or just guess).
*/
if (basemem == 0) {
for (i = 0; i <= physmap_idx; i += 2) {
@ -2203,49 +2214,39 @@ int15e820:
}
}
/*
* XXX this function is horribly organized and has to the same
* things that it does above here.
*/
/* XXX: If we couldn't find basemem from SMAP, just guess. */
if (basemem == 0)
basemem = 640;
if (basemem > 640) {
printf(
"Preposterous BIOS basemem of %uK, truncating to 640K\n",
basemem);
basemem = 640;
}
/*
* Let vm86 scribble on pages between basemem and
* ISA_HOLE_START, as above.
*/
for (pa = trunc_page(basemem * 1024);
pa < ISA_HOLE_START; pa += PAGE_SIZE)
pmap_kenter(KERNBASE + pa, pa);
pte = (pt_entry_t *)vm86paddr;
for (i = basemem / 4; i < 160; i++)
pte[i] = (i << PAGE_SHIFT) | PG_V | PG_RW | PG_U;
basemem_setup();
}
if (physmap[1] != 0)
goto physmap_done;
/*
* If we failed above, try memory map with INT 15:E801
* If we failed to find an SMAP, figure out the extended
* memory size. We will then build a simple memory map with
* two segments, one for "base memory" and the second for
* "extended memory". Note that "extended memory" starts at a
* physical address of 1MB and that both basemem and extmem
* are in units of 1KB.
*
* First, try to fetch the extended memory size via INT 15:E801.
*/
vmf.vmf_ax = 0xE801;
if (vm86_intcall(0x15, &vmf) == 0) {
extmem = vmf.vmf_cx + vmf.vmf_dx * 64;
} else {
/*
* If INT15:E801 fails, this is our last ditch effort
* to determine the extended memory size. Currently
* we prefer the RTC value over INT15:88.
*/
#if 0
vmf.vmf_ah = 0x88;
vm86_intcall(0x15, &vmf);
extmem = vmf.vmf_ax;
#elif !defined(XEN)
/*
* Prefer the RTC value for extended memory.
*/
#else
extmem = rtcin(RTC_EXTLO) + (rtcin(RTC_EXTHI) << 8);
#endif
}
@ -2270,6 +2271,7 @@ int15e820:
physmap[physmap_idx + 1] = physmap[physmap_idx] + extmem * 1024;
physmap_done:
#endif
/*
* Now, physmap contains a map of physical memory.
*/