git: fc5ab0227bba - stable/13 - Handle non-page aligned/sized memory in physmem

From: Andrew Turner <andrew_at_FreeBSD.org>
Date: Tue, 03 May 2022 14:04:41 UTC
The branch stable/13 has been updated by andrew:

URL: https://cgit.FreeBSD.org/src/commit/?id=fc5ab0227bbaa265aa8e4e0247cf816040ac4b44

commit fc5ab0227bbaa265aa8e4e0247cf816040ac4b44
Author:     Andrew Turner <andrew@FreeBSD.org>
AuthorDate: 2022-03-28 11:37:09 +0000
Commit:     Andrew Turner <andrew@FreeBSD.org>
CommitDate: 2022-05-03 14:04:04 +0000

    Handle non-page aligned/sized memory in physmem
    
    In some configurations the firmware may pass memory regions that are
    not page sized or aligned, e.g. when using 16k pages on arm64. If this
    is the case we will calculate many small regions because the alignment
    is applied before being inserted. As we round the start up and end down
    this will leave a 1 page hole between what should have been a single
    region.
    
    Fix by keeping the original alignment until we are just about to insert
    the region into the avail array.
    
    Sponsored by:   The FreeBSD Foundation
    Differential Revision: https://reviews.freebsd.org/D34694
    
    (cherry picked from commit d8bff5b67c9b6a85f409a7408e45e7e87d39c5a2)
---
 sys/kern/subr_physmem.c            | 17 ++++-------------
 tests/sys/kern/subr_physmem_test.c | 22 ++++++++++++++++++++--
 2 files changed, 24 insertions(+), 15 deletions(-)

diff --git a/sys/kern/subr_physmem.c b/sys/kern/subr_physmem.c
index 106200dd814e..99b4c2141612 100644
--- a/sys/kern/subr_physmem.c
+++ b/sys/kern/subr_physmem.c
@@ -180,7 +180,7 @@ regions_to_avail(vm_paddr_t *avail, uint32_t exflags, size_t maxavail,
     uint64_t maxphyssz, long *pavail, long *prealmem)
 {
 	size_t acnt, exi, hwi;
-	uint64_t end, start, xend, xstart;
+	uint64_t adj, end, start, xend, xstart;
 	long availmem, totalmem;
 	const struct region *exp, *hwp;
 	uint64_t availsz;
@@ -190,8 +190,9 @@ regions_to_avail(vm_paddr_t *avail, uint32_t exflags, size_t maxavail,
 	availsz = 0;
 	acnt = 0;
 	for (hwi = 0, hwp = hwregions; hwi < hwcnt; ++hwi, ++hwp) {
-		start = hwp->addr;
-		end   = hwp->size + start;
+		adj   = round_page(hwp->addr) - hwp->addr;
+		start = round_page(hwp->addr);
+		end   = trunc_page(hwp->size + adj) + start;
 		totalmem += atop((vm_offset_t)(end - start));
 		for (exi = 0, exp = exregions; exi < excnt; ++exi, ++exp) {
 			/*
@@ -337,8 +338,6 @@ insert_region(struct region *regions, size_t rcnt, vm_paddr_t addr,
 void
 physmem_hardware_region(uint64_t pa, uint64_t sz)
 {
-	vm_offset_t adj;
-
 	/*
 	 * Filter out the page at PA 0x00000000.  The VM can't handle it, as
 	 * pmap_extract() == 0 means failure.
@@ -371,14 +370,6 @@ physmem_hardware_region(uint64_t pa, uint64_t sz)
 		sz -= 1024 * 1024;
 	}
 
-	/*
-	 * Round the starting address up to a page boundary, and truncate the
-	 * ending page down to a page boundary.
-	 */
-	adj = round_page(pa) - pa;
-	pa  = round_page(pa);
-	sz  = trunc_page(sz - adj);
-
 	if (sz > 0 && hwcnt < nitems(hwregions))
 		hwcnt = insert_region(hwregions, hwcnt, pa, sz, 0);
 }
diff --git a/tests/sys/kern/subr_physmem_test.c b/tests/sys/kern/subr_physmem_test.c
index a562bacec65c..10824f7fe4ff 100644
--- a/tests/sys/kern/subr_physmem_test.c
+++ b/tests/sys/kern/subr_physmem_test.c
@@ -76,14 +76,12 @@ ATF_TC_BODY(hwregion, tc)
 	ATF_CHECK_EQ(avail[0], 2 * PAGE_SIZE);
 	ATF_CHECK_EQ(avail[1], 6 * PAGE_SIZE);
 
-#ifdef notyet /* This doesn't currently work */
 	/* Add the remaining part of the page */
 	physmem_hardware_region(6 * PAGE_SIZE + PAGE_SIZE / 2, PAGE_SIZE / 2);
 	len = physmem_avail(avail, 4);
 	ATF_CHECK_EQ(len, 2);
 	ATF_CHECK_EQ(avail[0], 2 * PAGE_SIZE);
 	ATF_CHECK_EQ(avail[1], 7 * PAGE_SIZE);
-#endif
 }
 
 ATF_TC_WITHOUT_HEAD(hwregion_exclude);
@@ -113,10 +111,30 @@ ATF_TC_BODY(hwregion_exclude, tc)
 	ATF_CHECK_EQ(avail[3], 7 * PAGE_SIZE);
 }
 
+ATF_TC_WITHOUT_HEAD(hwregion_unordered);
+ATF_TC_BODY(hwregion_unordered, tc)
+{
+	vm_paddr_t avail[4];
+	size_t len;
+
+	/* Add a partial page */
+	physmem_hardware_region(PAGE_SIZE, PAGE_SIZE / 2);
+	/* Add a full page not touching the previous */
+	physmem_hardware_region( 2 * PAGE_SIZE, PAGE_SIZE);
+	/* Add the remainder of the first page */
+	physmem_hardware_region(PAGE_SIZE + PAGE_SIZE / 2, PAGE_SIZE / 2);
+
+	len = physmem_avail(avail, 4);
+	ATF_CHECK_EQ(len, 2);
+	ATF_CHECK_EQ(avail[0], PAGE_SIZE);
+	ATF_CHECK_EQ(avail[1], 3 * PAGE_SIZE);
+}
+
 ATF_TP_ADD_TCS(tp)
 {
 
 	ATF_TP_ADD_TC(tp, hwregion);
 	ATF_TP_ADD_TC(tp, hwregion_exclude);
+	ATF_TP_ADD_TC(tp, hwregion_unordered);
 	return (atf_no_error());
 }