svn commit: r338356 - head/sys/amd64/amd64

Konstantin Belousov kib at FreeBSD.org
Tue Aug 28 18:47:03 UTC 2018


Author: kib
Date: Tue Aug 28 18:47:02 2018
New Revision: 338356
URL: https://svnweb.freebsd.org/changeset/base/338356

Log:
  Several bug fixes and robustness improvements for the AP boot page
  table allocation.
  
  At the time that mp_bootaddress() is called, phys_avail[] array does
  not reflect some memory reservations already done, like kernel
  placement.  Recent changes to DMAP protection which make kernel text
  read-only in DMAP revealed this, where on some machines AP boot page
  tables selection appears to intersect with the kernel itself.
  
  Fix this by checking the addresses selected using the same algorithm
  as bootaddr_rwx().  Also, try to chomp pages for the page table not
  only at the start of the contiguous range, but also at the end.  This
  should improve robustness when the only suitable range is already
  consumed by the kernel.
  
  Reported and tested by: Michael Gmelin <freebsd at grem.de>
  Reviewed by:    jhb
  MFC after:      1 week
  Sponsored by:   The FreeBSD Foundation
  Approved by:    re (gjb)
  Differential revision:  https://reviews.freebsd.org/D16907

Modified:
  head/sys/amd64/amd64/mp_machdep.c

Modified: head/sys/amd64/amd64/mp_machdep.c
==============================================================================
--- head/sys/amd64/amd64/mp_machdep.c	Tue Aug 28 18:16:02 2018	(r338355)
+++ head/sys/amd64/amd64/mp_machdep.c	Tue Aug 28 18:47:02 2018	(r338356)
@@ -86,6 +86,8 @@ __FBSDID("$FreeBSD$");
 
 #define GiB(v)			(v ## ULL << 30)
 
+#define	AP_BOOTPT_SZ		(PAGE_SIZE * 3)
+
 extern	struct pcpu __pcpu[];
 
 /* Temporary variables for init_secondary()  */
@@ -100,45 +102,79 @@ char *dbg_stack;
 
 static int	start_ap(int apic_id);
 
+static bool
+is_kernel_paddr(vm_paddr_t pa)
+{
+
+	return (pa >= trunc_2mpage(btext - KERNBASE) &&
+	   pa < round_page(_end - KERNBASE));
+}
+
+static bool
+is_mpboot_good(vm_paddr_t start, vm_paddr_t end)
+{
+
+	return (start + AP_BOOTPT_SZ <= GiB(4) && atop(end) < Maxmem);
+}
+
 /*
  * Calculate usable address in base memory for AP trampoline code.
  */
 void
 mp_bootaddress(vm_paddr_t *physmap, unsigned int *physmap_idx)
 {
+	vm_paddr_t start, end;
 	unsigned int i;
 	bool allocated;
 
 	alloc_ap_trampoline(physmap, physmap_idx);
 
+	/*
+	 * Find a memory region big enough below the 4GB boundary to
+	 * store the initial page tables.  Region must be mapped by
+	 * the direct map.
+	 *
+	 * Note that it needs to be aligned to a page boundary.
+	 */
 	allocated = false;
 	for (i = *physmap_idx; i <= *physmap_idx; i -= 2) {
 		/*
-		 * Find a memory region big enough below the 4GB
-		 * boundary to store the initial page tables.  Region
-		 * must be mapped by the direct map.
-		 *
-		 * Note that it needs to be aligned to a page
-		 * boundary.
+		 * First, try to chomp at the start of the physmap region.
+		 * Kernel binary might claim it already.
 		 */
-		if (physmap[i] >= GiB(4) || physmap[i + 1] -
-		    round_page(physmap[i]) < PAGE_SIZE * 3 ||
-		    atop(physmap[i + 1]) > Maxmem)
-			continue;
+		start = round_page(physmap[i]);
+		end = start + AP_BOOTPT_SZ;
+		if (start < end && end <= physmap[i + 1] &&
+		    is_mpboot_good(start, end) &&
+		    !is_kernel_paddr(start) && !is_kernel_paddr(end - 1)) {
+			allocated = true;
+			physmap[i] = end;
+			break;
+		}
 
-		allocated = true;
-		mptramp_pagetables = round_page(physmap[i]);
-		physmap[i] = round_page(physmap[i]) + (PAGE_SIZE * 3);
+		/*
+		 * Second, try to chomp at the end.  Again, check
+		 * against kernel.
+		 */
+		end = trunc_page(physmap[i + 1]);
+		start = end - AP_BOOTPT_SZ;
+		if (start < end && start >= physmap[i] &&
+		    is_mpboot_good(start, end) &&
+		    !is_kernel_paddr(start) && !is_kernel_paddr(end - 1)) {
+			allocated = true;
+			physmap[i + 1] = start;
+			break;
+		}
+	}
+	if (allocated) {
+		mptramp_pagetables = start;
 		if (physmap[i] == physmap[i + 1] && *physmap_idx != 0) {
 			memmove(&physmap[i], &physmap[i + 2],
 			    sizeof(*physmap) * (*physmap_idx - i + 2));
 			*physmap_idx -= 2;
 		}
-		break;
-	}
-
-	if (!allocated) {
-		mptramp_pagetables = trunc_page(boot_address) - (PAGE_SIZE * 3);
+	} else {
+		mptramp_pagetables = trunc_page(boot_address) - AP_BOOTPT_SZ;
 		if (bootverbose)
 			printf(
 "Cannot find enough space for the initial AP page tables, placing them at %#x",


More information about the svn-src-all mailing list