From owner-p4-projects@FreeBSD.ORG Thu Sep 23 21:37:14 2010 Return-Path: Delivered-To: p4-projects@freebsd.org Received: by hub.freebsd.org (Postfix, from userid 32767) id D59321065672; Thu, 23 Sep 2010 21:37:13 +0000 (UTC) Delivered-To: perforce@freebsd.org Received: from mx1.freebsd.org (mx1.freebsd.org [IPv6:2001:4f8:fff6::34]) by hub.freebsd.org (Postfix) with ESMTP id 98360106564A for ; Thu, 23 Sep 2010 21:37:13 +0000 (UTC) (envelope-from jhb@freebsd.org) Received: from skunkworks.freebsd.org (skunkworks.freebsd.org [IPv6:2001:4f8:fff6::2d]) by mx1.freebsd.org (Postfix) with ESMTP id 845528FC18 for ; Thu, 23 Sep 2010 21:37:13 +0000 (UTC) Received: from skunkworks.freebsd.org (localhost [127.0.0.1]) by skunkworks.freebsd.org (8.14.4/8.14.4) with ESMTP id o8NLbDPd034068 for ; Thu, 23 Sep 2010 21:37:13 GMT (envelope-from jhb@freebsd.org) Received: (from perforce@localhost) by skunkworks.freebsd.org (8.14.4/8.14.4/Submit) id o8NLbDZU034065 for perforce@freebsd.org; Thu, 23 Sep 2010 21:37:13 GMT (envelope-from jhb@freebsd.org) Date: Thu, 23 Sep 2010 21:37:13 GMT Message-Id: <201009232137.o8NLbDZU034065@skunkworks.freebsd.org> X-Authentication-Warning: skunkworks.freebsd.org: perforce set sender to jhb@freebsd.org using -f From: John Baldwin To: Perforce Change Reviews Precedence: bulk Cc: Subject: PERFORCE change 184128 for review X-BeenThere: p4-projects@freebsd.org X-Mailman-Version: 2.1.5 List-Id: p4 projects tree changes List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , X-List-Received-Date: Thu, 23 Sep 2010 21:37:14 -0000 http://p4web.freebsd.org/@@184128?ac=10 Change 184128 by jhb@jhb_jhbbsd on 2010/09/23 21:36:15 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 twice, and only call it once during boot. - Adjust some comments to better explain the code flow. Affected files ... .. //depot/projects/smpng/sys/i386/i386/machdep.c#157 edit Differences ... ==== //depot/projects/smpng/sys/i386/i386/machdep.c#157 (text+ko) ==== @@ -2029,6 +2029,52 @@ return (1); } +static void +basemem_setup(void) +{ + pt_entry_t *pte; + + if (basemem > 640) { + printf("Preposterous BIOS basemem of %uK, truncating to 640K\n", + basemem); + basemem = 640; + } + + /* + * XXX if biosbasemem is now < 640, there is a `hole' + * between the end of base memory and the start of + * ISA memory. The hole may be empty or it may + * contain BIOS code or data. Map it read/write so + * that the BIOS can write to it. (Memory from 0 to + * the physical end of the kernel is mapped read-only + * to begin with and then parts of it are remapped. + * The parts that aren't remapped form holes that + * remain read-only and are unused by the kernel. + * The base memory area is below the physical end of + * the kernel and right now forms a read-only hole. + * The part of it from PAGE_SIZE to + * (trunc_page(biosbasemem * 1024) - 1) will be + * remapped and used by the kernel later.) + * + * This code is similar to the code used in + * pmap_mapdev, but since no memory needs to be + * allocated we simply change the mapping. + */ + for (pa = trunc_page(basemem * 1024); + pa < ISA_HOLE_START; pa += PAGE_SIZE) + pmap_kenter(KERNBASE + pa, pa); + + /* + * Map pages between basemem and ISA_HOLE_START, if any, r/w into + * the vm86 page table so that vm86 can scribble on them using + * the vm86 map too. XXX: why 2 ways for this and only 1 way for + * page 0, at least as initialized here? + */ + pte = (pt_entry_t *)vm86paddr; + for (i = basemem / 4; i < 160; i++) + pte[i] = (i << PAGE_SHIFT) | PG_V | PG_RW | PG_U; +} + /* * Populate the (physmap) array with base/bound pairs describing the * available physical memory in the system, then test this memory and @@ -2052,13 +2098,20 @@ struct vm86frame vmf; struct vm86context vmc; vm_paddr_t pa, physmap[PHYSMAP_SIZE]; - pt_entry_t *pte; struct bios_smap *smap, *smapbase, *smapend; u_int32_t smapsize; quad_t dcons_addr, dcons_size; caddr_t kmdp; 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) { /* @@ -2071,83 +2124,14 @@ 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); - basemem = 640; - } - - /* - * XXX if biosbasemem is now < 640, there is a `hole' - * between the end of base memory and the start of - * ISA memory. The hole may be empty or it may - * contain BIOS code or data. Map it read/write so - * that the BIOS can write to it. (Memory from 0 to - * the physical end of the kernel is mapped read-only - * to begin with and then parts of it are remapped. - * The parts that aren't remapped form holes that - * remain read-only and are unused by the kernel. - * The base memory area is below the physical end of - * the kernel and right now forms a read-only hole. - * The part of it from PAGE_SIZE to - * (trunc_page(biosbasemem * 1024) - 1) will be - * remapped and used by the kernel later.) - * - * This code is similar to the code used in - * pmap_mapdev, but since no memory needs to be - * allocated we simply change the mapping. - */ - for (pa = trunc_page(basemem * 1024); - pa < ISA_HOLE_START; pa += PAGE_SIZE) - pmap_kenter(KERNBASE + pa, pa); - - /* - * Map pages between basemem and ISA_HOLE_START, if any, r/w into - * the vm86 page table so that vm86 can scribble on them using - * the vm86 map too. XXX: why 2 ways for this and only 1 way for - * page 0, at least as initialized here? - */ - pte = (pt_entry_t *)vm86paddr; - for (i = basemem / 4; i < 160; i++) - pte[i] = (i << PAGE_SHIFT) | PG_V | PG_RW | PG_U; - -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; kmdp = preload_search_by_type("elf kernel"); if (kmdp == NULL) @@ -2156,9 +2140,10 @@ 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 +2152,51 @@ 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); + goto have_smap; + } - 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); + /* + * 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(); } /* - * Perform "base memory" related probes & setup based on SMAP + * 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. + */ + physmap_idx = 0; + 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 +2206,39 @@ } } - /* - * 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 +2263,7 @@ physmap[physmap_idx + 1] = physmap[physmap_idx] + extmem * 1024; physmap_done: +#endif /* * Now, physmap contains a map of physical memory. */