svn commit: r213226 - head/sys/i386/i386

John Baldwin jhb at FreeBSD.org
Mon Sep 27 19:36:16 UTC 2010


Author: jhb
Date: Mon Sep 27 19:36:15 2010
New Revision: 213226
URL: http://svn.freebsd.org/changeset/base/213226

Log:
  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

Modified:
  head/sys/i386/i386/machdep.c

Modified: head/sys/i386/i386/machdep.c
==============================================================================
--- head/sys/i386/i386/machdep.c	Mon Sep 27 19:26:56 2010	(r213225)
+++ head/sys/i386/i386/machdep.c	Mon Sep 27 19:36:15 2010	(r213226)
@@ -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, v
 	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;
-
-	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;
-	}
+	int i;
 
-	/*
-	 * 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.
 	 */


More information about the svn-src-head mailing list