On Thursday, 17 September 2026 at 22:06, Diego Meretta <[email protected]> 
wrote:

> * riscv64/riscv64/pmap.h (RISCV_L1_SPAN): Define 2MB superpage size.
>   (RISCV_PTE_LEAF_RWX): Define common leaf PTE flags.
> * riscv64/riscv64/pmap.c (pmap_bootstrap): Implement Sv39 bootstrap.
>   Allocate root table and two L1 tables from the bootstrap heap.
>   Map the kernel image at KERNEL_MAP_BASE using 2MB superpages.
>   Direct-map physical memory at DIRECT_MAP_VA_BASE for phystokv.
>   Activate Sv39 via satp and flush the TLB.
> ---
>  riscv64/riscv64/pmap.c | 65 +++++++++++++++++++++++++++++++++++++++++-
>  riscv64/riscv64/pmap.h |  6 ++++
>  2 files changed, 70 insertions(+), 1 deletion(-)
> 

Hi Diego,

Thanks for your patch. It does not work as is on QEMU, and has several 
bugs. It can not be merged as is.

> diff --git a/riscv64/riscv64/pmap.c b/riscv64/riscv64/pmap.c
> index dca90a94..d5835554 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,69 @@ pmap_discover_physical_memory(struct dtb_node *node)
>  void
>  pmap_bootstrap(void)
>  {
> +     pt_entry_t *root, *l1_kernel, *l1_direct;
> +     vm_offset_t kernel_phys_end, va;
> +     unsigned int idx, num_kernel_pages, num_direct_pages;
> +
>       kernel_pmap = &kernel_pmap_store;
> -     panic("riscv64: pmap_bootstrap not implemented");
> +
> +     /* Allocate Sv39 root table and two L1 tables from the bootstrap heap.  
> */
> +     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);
> +     l1_direct = (pt_entry_t *) pmap_grab_page();
> +     memset(l1_direct, 0, PAGE_SIZE);
> +
> +     /*
> +      * Map the kernel image using 2MB superpages.
> +      * Physical range: [__text_start, round_page(_end))
> +      * Virtual range:  KERNEL_MAP_BASE + offset
> +      */
> +     kernel_phys_end = round_page((vm_offset_t) &_end);
> +     num_kernel_pages = (kernel_phys_end - (vm_offset_t) &__text_start)
> +                        >> (RISCV_VPN1_SHIFT);

This doesn't round to the next megapage, and thus does not cover the 
whole kernel range. Our kernel is smaller than 2MB, and num_kernel_pages 
ends up as 0. Ceiling division should be done instead.

> +
> +     for (idx = 0; idx < num_kernel_pages; idx++) {
> +             phys_addr_t pa = (vm_offset_t) &__text_start
> +                              + (vm_offset_t) idx * (RISCV_L1_SPAN);
> +     l1_kernel[idx] = pa_to_pte(pa) | RISCV_PTE_LEAF_RWX;
> +     }
> +
> +     /*
> +      * Direct-map physical memory so phystokv/_kvtophys work.
> +      * Map the bootstrap heap range [phys_mem_start, heap_end)
> +      * at DIRECT_MAP_VA_BASE.
> +      */
> +     num_direct_pages = (heap_end - round_page(phys_mem_start))
> +                        >> (RISCV_VPN1_SHIFT);

Same as above. Ceiling division should be used.

Also, we should use L2 gigapages instead of L1 megapages for the direct 
mapping of physical RAM. This can only cover up to 1 GiB.

Lastly, heap_end is the boundary of the bootstrap allocator, not the end 
of phys RAM. Use phys_mem_start + phys_mem_size instead of heap_end.

> +     if (num_direct_pages == 0)
> +             num_direct_pages = 1;

Apply ceiling division. This is unnecessary.

> +
> +     for (idx = 0; idx < num_direct_pages; idx++) {
> +             phys_addr_t pa = round_page(phys_mem_start)
> +                              + (vm_offset_t) idx * (RISCV_L1_SPAN);
> +     l1_direct[idx] = pa_to_pte(pa) | RISCV_PTE_LEAF_RWX;

This overflows if idx >= 512, which happens when physical RAM is bigger 
than 1 GiB. Using gigapages solves this.

For direct mapping, RW is more desirable over RWX.

> +     }
> +
> +     /*
> +      * Install L1 tables in the root:
> +      *   root[510] -> l1_kernel  (VA 0xffffffff80000000)
> +      *   root[508] -> l1_direct  (VA 0xffffffc000000000)

This comment is wrong. Root PTE index 508 does not correspond to VA 
0xffffffc000000000. It should have been root[256].

> +      */
> +     root[lin2vpn2(KERNEL_MAP_BASE)] =
> +             pa_to_pte((phys_addr_t) l1_kernel) | RISCV_PTE_V;
> +     root[lin2vpn2(DIRECT_MAP_VA_BASE)] =
> +             pa_to_pte((phys_addr_t) l1_direct) | RISCV_PTE_V;

DIRECT_MAP_VA_BASE + phys_mem_start must be used. This does not uphold 
the phystokv contract.

Since we'll be using gigapages, this part needs changing and needs to be 
in a loop later. I.e. we'll map 258 (depending on phys ram beginning), 
259, 260 etc.

> +
> +     kernel_pmap->root_table = root;
> +
> +     /* Activate Sv39.  */
> +     satp_write(satp_sv39((phys_addr_t) root));
> +     sfence_vma();

We enabled paging without updating SP or setting up temporary identity 
mapping for the kernel. As expected, this faults under QEMU.

> +
> +     kernel_virtual_start = KERNEL_MAP_BASE;

we can not use the statically defined virtual base directly. This needs 
to begin after the virtual range occupied by the kernel mapping, at 
least after the 2MiB leaf used for the image.

> +     kernel_virtual_end = VM_MAX_KERNEL_ADDRESS;
>  }
> 
>  void
> diff --git a/riscv64/riscv64/pmap.h b/riscv64/riscv64/pmap.h
> index fbb163ba..2df267f8 100644
> --- a/riscv64/riscv64/pmap.h
> +++ b/riscv64/riscv64/pmap.h
> @@ -35,6 +35,7 @@ typedef phys_addr_t pt_entry_t;
>  #define RISCV_VPN_BITS               9
>  #define RISCV_PTE_SIZE               8
>  #define RISCV_PT_ENTRIES     512
> +#define RISCV_L1_SPAN                (2 * 1024 * 1024)       /* 2MB 
> superpage */
> 
>  #define RISCV_VPN2_SHIFT     30
>  #define RISCV_VPN1_SHIFT     21
> @@ -65,6 +66,11 @@ 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)
> +
>  struct pmap {
>       pt_entry_t              *root_table;
>       int                     ref_count;
> --
> 2.43.0
> 

Please ensure patches are tested locally before sending. This will make 
the patch review process faster and easier.

Hakan


Reply via email to