Rework of the previously sent patch, with the review comments
addressed: ceiling division for the kernel superpage count, a 1GiB
gigapage direct map over phys_mem_start + phys_mem_size with every PA
mapped at DIRECT_MAP_VA_BASE + PA (phystokv contract) and RW
permissions, a temporary identity map so execution survives enabling
satp, and kernel_virtual_start placed after the kernel image mapping.

While testing this locally I found an issue that prevents the kernel
from reaching this code at all with the current toolchain defaults.
If this looks good, I will send the next patch about that finding.

* riscv64/riscv64/pmap.c: Include <string.h> and <riscv64/proc_reg.h>.
(pmap_bootstrap): Implement Sv39 bootstrap.
* riscv64/riscv64/pmap.h (RISCV_L1_SPAN, RISCV_L2_SPAN, round_l1,
trunc_l1, round_l2, trunc_l2, RISCV_PTE_LEAF_RWX, RISCV_PTE_LEAF_RW):
New macros.

Tested by building with riscv64-linux-gnu-gcc 13 and booting under
QEMU virt with OpenSBI v1.3 (with the issue above worked around
locally): no page faults or access faults during or after enabling
Sv39.
---
 riscv64/riscv64/pmap.c | 97 +++++++++++++++++++++++++++++++++++++++++-
 riscv64/riscv64/pmap.h | 23 ++++++++++
 2 files changed, 119 insertions(+), 1 deletion(-)

diff --git a/riscv64/riscv64/pmap.c b/riscv64/riscv64/pmap.c
index dca90a94..3beef139 100644
--- a/riscv64/riscv64/pmap.c
+++ b/riscv64/riscv64/pmap.c
@@ -16,9 +16,11 @@
  * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
  */
 
+#include <string.h>
 #include <device/dtb.h>
 #include <kern/debug.h>
 #include <mach/vm_prot.h>
+#include <riscv64/proc_reg.h>
 #include <vm/pmap.h>
 #include <vm/vm_page.h>
 
@@ -124,8 +126,101 @@ pmap_discover_physical_memory(struct dtb_node *node)
 void
 pmap_bootstrap(void)
 {
+       pt_entry_t *root, *l1_kernel;
+       vm_offset_t kernel_phys_start, kernel_phys_end;
+       vm_offset_t phys_mem_end, direct_map_start, va;
+       phys_addr_t pa;
+       unsigned int i, num_kernel_pages, num_giga_pages;
+
        kernel_pmap = &kernel_pmap_store;
-       panic("riscv64: pmap_bootstrap not implemented");
+
+       /*
+        * Paging is still disabled, so PC-relative symbol references resolve
+        * to the kernel's physical load addresses, not its linked VMAs.
+        */
+       kernel_phys_start = (vm_offset_t) &__text_start;
+       kernel_phys_end = (vm_offset_t) &_end;
+       if (trunc_l1(kernel_phys_start) != kernel_phys_start)
+               panic("riscv64: kernel image is not superpage aligned");
+
+       /* Cover the kernel image with a whole number of superpages.  */
+       num_kernel_pages = (unsigned int)
+               ((kernel_phys_end - kernel_phys_start + RISCV_L1_SPAN - 1)
+                >> RISCV_VPN1_SHIFT);
+
+       /*
+        *      Allocate the Sv39 root table and the kernel L1 table from
+        *      the bootstrap heap.  These are physical addresses; paging
+        *      is still disabled.  TODO: once the higher-half trampoline
+        *      lands, consumers must reach them through phystokv().
+        */
+       root = (pt_entry_t *) pmap_grab_page();
+       memset(root, 0, PAGE_SIZE);
+       l1_kernel = (pt_entry_t *) pmap_grab_page();
+       memset(l1_kernel, 0, PAGE_SIZE);
+
+       /*
+        *      Map the kernel image at KERNEL_MAP_BASE with 2MiB
+        *      superpages: image offset O (physical
+        *      kernel_phys_start + O) maps to virtual
+        *      KERNEL_MAP_BASE + O.
+        */
+       for (i = 0; i < num_kernel_pages; i++) {
+               va = KERNEL_MAP_BASE + (vm_offset_t) i * RISCV_L1_SPAN;
+               pa = (phys_addr_t) (kernel_phys_start
+                                   + (vm_offset_t) i * RISCV_L1_SPAN);
+               l1_kernel[lin2vpn1(va)] = pa_to_pte(pa) | RISCV_PTE_LEAF_RWX;
+       }
+       root[lin2vpn2(KERNEL_MAP_BASE)] =
+               pa_to_pte((phys_addr_t) l1_kernel) | RISCV_PTE_V;
+
+       /*
+        *      Direct-map physical RAM at DIRECT_MAP_VA_BASE so that
+        *      phystokv()/kvtophys() hold: phystokv(pa) = pa +
+        *      DIRECT_MAP_VA_BASE.  Use 1GiB gigapages (one root entry
+        *      per gigabyte, e.g. root[258], root[259], ...).  The
+        *      physical range ends at phys_mem_start + phys_mem_size,
+        *      not at the bootstrap heap boundary.
+        */
+       phys_mem_end = phys_mem_start + phys_mem_size;
+       direct_map_start = trunc_l2(phys_mem_start);
+       num_giga_pages = (unsigned int)
+               ((phys_mem_end - direct_map_start + RISCV_L2_SPAN - 1)
+                >> RISCV_VPN2_SHIFT);
+       for (i = 0; i < num_giga_pages; i++) {
+               pa = (phys_addr_t) (direct_map_start
+                                   + (vm_offset_t) i * RISCV_L2_SPAN);
+               va = DIRECT_MAP_VA_BASE + (vm_offset_t) pa;
+               root[lin2vpn2(va)] = pa_to_pte(pa) | RISCV_PTE_LEAF_RW;
+       }
+
+       /*
+        *      Temporary identity maps for the pre-paging window: the
+        *      running text, bootstrap stack, DTB and early console still
+        *      use physical addresses right after satp is enabled.  The
+        *      kernel image is covered by its containing 1GiB region; the
+        *      UART MMIO range used by the console is in the 0..1GiB
+        *      region.  TODO: take the UART range from the device tree.
+        */
+       root[lin2vpn2(kernel_phys_start)] =
+               pa_to_pte((phys_addr_t) trunc_l2(kernel_phys_start))
+               | RISCV_PTE_LEAF_RWX;
+       root[lin2vpn2(0x10000000UL)] =
+               pa_to_pte((phys_addr_t) 0) | RISCV_PTE_LEAF_RWX;
+
+       kernel_pmap->root_table = root;
+
+       /* Activate Sv39.  */
+       satp_write(satp_sv39((phys_addr_t) root));
+       sfence_vma();
+
+       /*
+        *      The kernel map starts right after the virtual range
+        *      covered by the kernel image mapping.
+        */
+       kernel_virtual_start = KERNEL_MAP_BASE
+                              + (vm_offset_t) num_kernel_pages * RISCV_L1_SPAN;
+       kernel_virtual_end = VM_MAX_KERNEL_ADDRESS;
 }
 
 void
diff --git a/riscv64/riscv64/pmap.h b/riscv64/riscv64/pmap.h
index fbb163ba..fc3fb88f 100644
--- a/riscv64/riscv64/pmap.h
+++ b/riscv64/riscv64/pmap.h
@@ -41,6 +41,19 @@ typedef phys_addr_t pt_entry_t;
 #define RISCV_VPN0_SHIFT       12
 #define RISCV_VPN_MASK         0x1ff
 
+/* Span of a level-1 superpage mapping: 2MiB.  */
+#define RISCV_L1_SPAN          ((vm_size_t) 1 << RISCV_VPN1_SHIFT)
+
+/* Span of a level-2 gigapage mapping: 1GiB.  */
+#define RISCV_L2_SPAN          ((vm_size_t) 1 << RISCV_VPN2_SHIFT)
+
+#define round_l1(x)    (((vm_offset_t)(x) + RISCV_L1_SPAN - 1) \
+                        & ~(RISCV_L1_SPAN - 1))
+#define trunc_l1(x)    ((vm_offset_t)(x) & ~(RISCV_L1_SPAN - 1))
+#define round_l2(x)    (((vm_offset_t)(x) + RISCV_L2_SPAN - 1) \
+                        & ~(RISCV_L2_SPAN - 1))
+#define trunc_l2(x)    ((vm_offset_t)(x) & ~(RISCV_L2_SPAN - 1))
+
 #define lin2vpn2(a)            (((a) >> RISCV_VPN2_SHIFT) & RISCV_VPN_MASK)
 #define lin2vpn1(a)            (((a) >> RISCV_VPN1_SHIFT) & RISCV_VPN_MASK)
 #define lin2vpn0(a)            (((a) >> RISCV_VPN0_SHIFT) & RISCV_VPN_MASK)
@@ -65,6 +78,16 @@ typedef phys_addr_t pt_entry_t;
 #define RISCV_PTE_IS_LEAF(pte) \
        ((pte) & (RISCV_PTE_R | RISCV_PTE_W | RISCV_PTE_X))
 
+/* Common leaf flags for a read/write/execute mapping.  */
+#define RISCV_PTE_LEAF_RWX \
+       (RISCV_PTE_V | RISCV_PTE_R | RISCV_PTE_W | RISCV_PTE_X \
+        | RISCV_PTE_A | RISCV_PTE_D)
+
+/* Leaf flags for a read/write data mapping (e.g. the direct map).  */
+#define RISCV_PTE_LEAF_RW \
+       (RISCV_PTE_V | RISCV_PTE_R | RISCV_PTE_W \
+        | RISCV_PTE_A | RISCV_PTE_D)
+
 struct pmap {
        pt_entry_t              *root_table;
        int                     ref_count;
-- 
2.43.0


Reply via email to