You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@nuttx.apache.org by xi...@apache.org on 2022/05/12 20:14:06 UTC

[incubator-nuttx] 01/02: arch: risc-v: Add MMU support for qemu-rv

This is an automated email from the ASF dual-hosted git repository.

xiaoxiang pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/incubator-nuttx.git

commit 3193aa3c97ed26e5cb9c5638c070282643531ad2
Author: Masayuki Ishikawa <ma...@gmail.com>
AuthorDate: Mon May 9 11:00:56 2022 +0900

    arch: risc-v: Add MMU support for qemu-rv
    
    Summary:
    - This commit adds MMU support for qemu-rv
    - Please note that mtimer is disabled for S-mode because
      the mtimer needs to be accessed in M-mode
    
    Impact:
    - qemu-rv only
    
    Testing:
    - Tested with rv-virt:knsh64 (will be pushed later)
    
    Signed-off-by: Masayuki Ishikawa <Ma...@jp.sony.com>
---
 arch/risc-v/Kconfig                                |   6 +
 arch/risc-v/src/qemu-rv/Make.defs                  |  36 ++++-
 arch/risc-v/src/qemu-rv/chip.h                     |   8 +
 arch/risc-v/src/qemu-rv/hardware/qemu_rv_plic.h    |  22 ++-
 arch/risc-v/src/qemu-rv/qemu_rv_allocateheap.c     | 147 ++++++++++++++++++
 arch/risc-v/src/qemu-rv/qemu_rv_head.S             |   4 +
 arch/risc-v/src/qemu-rv/qemu_rv_irq.c              |  43 +++---
 arch/risc-v/src/qemu-rv/qemu_rv_irq_dispatch.c     |  10 +-
 arch/risc-v/src/qemu-rv/qemu_rv_mm_init.c          | 171 +++++++++++++++++++++
 .../{qemu_rv_timerisr.c => qemu_rv_mm_init.h}      |  51 +++---
 .../{qemu_rv_timerisr.c => qemu_rv_pgalloc.c}      |  44 +++---
 arch/risc-v/src/qemu-rv/qemu_rv_start.c            |  53 +++++++
 arch/risc-v/src/qemu-rv/qemu_rv_timerisr.c         |   2 +
 13 files changed, 508 insertions(+), 89 deletions(-)

diff --git a/arch/risc-v/Kconfig b/arch/risc-v/Kconfig
index 82a4b649b0..025e186d97 100644
--- a/arch/risc-v/Kconfig
+++ b/arch/risc-v/Kconfig
@@ -136,6 +136,12 @@ config ARCH_CHIP_QEMU_RV
 	select ARCH_HAVE_FPU if EXPERIMENTAL
 	select ARCH_HAVE_DPFPU if EXPERIMENTAL
 	select ARCH_HAVE_MULTICPU
+	select ARCH_HAVE_MPU
+	select ARCH_HAVE_MMU
+	select ARCH_MMU_TYPE_SV39 if ARCH_CHIP_QEMU_RV64
+	select ARCH_HAVE_ADDRENV
+	select ARCH_NEED_ADDRENV_MAPPING
+	select ARCH_HAVE_S_MODE
 	select ONESHOT
 	select ALARM_ARCH
 	---help---
diff --git a/arch/risc-v/src/qemu-rv/Make.defs b/arch/risc-v/src/qemu-rv/Make.defs
index 74fc60a903..e48c85fcc8 100644
--- a/arch/risc-v/src/qemu-rv/Make.defs
+++ b/arch/risc-v/src/qemu-rv/Make.defs
@@ -22,6 +22,10 @@
 # the first object file, so it will appear at address 0
 HEAD_ASRC = qemu_rv_head.S
 
+ifeq ($(CONFIG_BUILD_KERNEL),y)
+STARTUP_OBJS = crt0$(OBJEXT)
+endif
+
 # Specify our general Assembly files
 CMN_ASRCS += riscv_vectors.S riscv_exception_common.S
 
@@ -75,4 +79,34 @@ endif
 
 # Specify our C code within this directory to be included
 CHIP_CSRCS  = qemu_rv_start.c qemu_rv_irq_dispatch.c qemu_rv_irq.c
-CHIP_CSRCS += qemu_rv_timerisr.c
+CHIP_CSRCS += qemu_rv_timerisr.c qemu_rv_allocateheap.c
+
+ifeq ($(CONFIG_BUILD_KERNEL),y)
+CHIP_CSRCS += qemu_rv_mm_init.c
+endif
+
+ifneq ($(CONFIG_BUILD_FLAT),y)
+CMN_CSRCS += riscv_task_start.c
+CMN_CSRCS += riscv_pthread_start.c
+CMN_CSRCS += riscv_signal_dispatch.c
+endif
+
+ifeq ($(CONFIG_ARCH_USE_MPU),y)
+CMN_CSRCS += riscv_pmp.c
+endif
+
+ifeq ($(CONFIG_ARCH_USE_MMU),y)
+CMN_CSRCS += riscv_mmu.c
+endif
+
+ifeq ($(CONFIG_ARCH_KERNEL_STACK),y)
+CMN_CSRCS += riscv_addrenv_kstack.c
+endif
+
+ifeq ($(CONFIG_ARCH_ADDRENV),y)
+CMN_CSRCS += riscv_addrenv.c riscv_pgalloc.c riscv_addrenv_perms.c
+endif
+
+ifeq ($(CONFIG_MM_PGALLOC),y)
+CHIP_CSRCS += qemu_rv_pgalloc.c
+endif
diff --git a/arch/risc-v/src/qemu-rv/chip.h b/arch/risc-v/src/qemu-rv/chip.h
index 3d8d05d75f..f8f78fbe51 100644
--- a/arch/risc-v/src/qemu-rv/chip.h
+++ b/arch/risc-v/src/qemu-rv/chip.h
@@ -47,6 +47,7 @@ extern void up_serialinit(void);
 #include "hardware/qemu_rv_plic.h"
 
 #include "riscv_internal.h"
+#include "riscv_percpu.h"
 
 /****************************************************************************
  * Macro Definitions
@@ -73,5 +74,12 @@ extern void up_serialinit(void);
 .endm
 #endif /* CONFIG_SMP && CONFIG_ARCH_INTERRUPTSTACK > 15 */
 
+#if defined(CONFIG_ARCH_USE_S_MODE) && CONFIG_ARCH_INTERRUPTSTACK > 15
+.macro  setintstack tmp0, tmp1
+  csrr    \tmp0, CSR_SCRATCH
+  REGLOAD sp, RISCV_PERCPU_IRQSTACK(\tmp0)
+.endm
+#endif /* CONFIG_ARCH_USE_S_MODE && CONFIG_ARCH_INTERRUPTSTACK > 15 */
+
 #endif /* __ASSEMBLY__  */
 #endif /* __ARCH_RISCV_SRC_QEMU_RV_CHIP_H */
diff --git a/arch/risc-v/src/qemu-rv/hardware/qemu_rv_plic.h b/arch/risc-v/src/qemu-rv/hardware/qemu_rv_plic.h
index 7c010dd90b..eac61f8fd1 100644
--- a/arch/risc-v/src/qemu-rv/hardware/qemu_rv_plic.h
+++ b/arch/risc-v/src/qemu-rv/hardware/qemu_rv_plic.h
@@ -21,15 +21,29 @@
 #ifndef __ARCH_RISCV_SRC_QEMU_RV_HARDWARE_QEMU_RV_PLIC_H
 #define __ARCH_RISCV_SRC_QEMU_RV_HARDWARE_QEMU_RV_PLIC_H
 
+/****************************************************************************
+ * Included Files
+ ****************************************************************************/
+
+#include <nuttx/config.h>
+
 /****************************************************************************
  * Pre-processor Definitions
  ****************************************************************************/
 
 #define QEMU_RV_PLIC_PRIORITY    (QEMU_RV_PLIC_BASE + 0x000000)
 #define QEMU_RV_PLIC_PENDING1    (QEMU_RV_PLIC_BASE + 0x001000)
-#define QEMU_RV_PLIC_ENABLE1     (QEMU_RV_PLIC_BASE + 0x002000)
-#define QEMU_RV_PLIC_ENABLE2     (QEMU_RV_PLIC_BASE + 0x002004)
-#define QEMU_RV_PLIC_THRESHOLD   (QEMU_RV_PLIC_BASE + 0x200000)
-#define QEMU_RV_PLIC_CLAIM       (QEMU_RV_PLIC_BASE + 0x200004)
+
+#ifdef CONFIG_ARCH_USE_S_MODE
+#  define QEMU_RV_PLIC_ENABLE1   (QEMU_RV_PLIC_BASE + 0x002080)
+#  define QEMU_RV_PLIC_ENABLE2   (QEMU_RV_PLIC_BASE + 0x002084)
+#  define QEMU_RV_PLIC_THRESHOLD (QEMU_RV_PLIC_BASE + 0x201000)
+#  define QEMU_RV_PLIC_CLAIM     (QEMU_RV_PLIC_BASE + 0x201004)
+#else
+#  define QEMU_RV_PLIC_ENABLE1   (QEMU_RV_PLIC_BASE + 0x002000)
+#  define QEMU_RV_PLIC_ENABLE2   (QEMU_RV_PLIC_BASE + 0x002004)
+#  define QEMU_RV_PLIC_THRESHOLD (QEMU_RV_PLIC_BASE + 0x200000)
+#  define QEMU_RV_PLIC_CLAIM     (QEMU_RV_PLIC_BASE + 0x200004)
+#endif
 
 #endif /* __ARCH_RISCV_SRC_QEMU_RV_HARDWARE_QEMU_RV_PLIC_H */
diff --git a/arch/risc-v/src/qemu-rv/qemu_rv_allocateheap.c b/arch/risc-v/src/qemu-rv/qemu_rv_allocateheap.c
new file mode 100755
index 0000000000..bf61de105b
--- /dev/null
+++ b/arch/risc-v/src/qemu-rv/qemu_rv_allocateheap.c
@@ -0,0 +1,147 @@
+/****************************************************************************
+ * arch/risc-v/src/qemu-rv/qemu_rv_allocateheap.c
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.  The
+ * ASF licenses this file to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the
+ * License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ *
+ ****************************************************************************/
+
+/****************************************************************************
+ * Included Files
+ ****************************************************************************/
+
+#include <nuttx/config.h>
+
+#include <nuttx/kmalloc.h>
+#include <nuttx/userspace.h>
+
+#include <nuttx/arch.h>
+
+#ifdef CONFIG_MM_KERNEL_HEAP
+#include <arch/board/board_memorymap.h>
+#endif
+
+#include "riscv_internal.h"
+
+/****************************************************************************
+ * Pre-processor Definitions
+ ****************************************************************************/
+
+#ifdef CONFIG_MM_KERNEL_HEAP
+#define KRAM_END    KSRAM_END
+#else
+#define KRAM_END    CONFIG_RAM_END
+#endif
+
+/****************************************************************************
+ * Public Data
+ ****************************************************************************/
+
+/****************************************************************************
+ * Public Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: up_allocate_heap
+ *
+ * Description:
+ *   This function will be called to dynamically set aside the heap region.
+ *
+ *   For the kernel build (CONFIG_BUILD_PROTECTED=y) with both kernel- and
+ *   user-space heaps (CONFIG_MM_KERNEL_HEAP=y), this function provides the
+ *   size of the unprotected, user-space heap.
+ *
+ *   If a protected kernel-space heap is provided, the kernel heap must be
+ *   allocated (and protected) by an analogous up_allocate_kheap().
+ *
+ *   The following memory map is assumed for the flat build:
+ *
+ *   .data region.  Size determined at link time.
+ *   .bss  region  Size determined at link time.
+ *   IDLE thread stack.  Size determined by CONFIG_IDLETHREAD_STACKSIZE.
+ *   Heap.  Extends to the end of User SRAM.
+ *
+ *   The following memory map is assumed for the protect build.
+ *   The kernel and user space have it's own dedicated heap space.
+ *
+ *   User .data region         Size determined at link time
+ *   User .bss region          Size determined at link time
+ *   User heap                 Extends to the end of User SRAM
+ *   Kernel .data region       Size determined at link time
+ *   Kernel .bss  region       Size determined at link time
+ *   Kernel IDLE thread stack  Size determined by CONFIG_IDLETHREAD_STACKSIZE
+ *   Kernel heap               Size determined by CONFIG_MM_KERNEL_HEAPSIZE
+ *
+ ****************************************************************************/
+
+#ifdef CONFIG_BUILD_KERNEL
+void up_allocate_kheap(void **heap_start, size_t *heap_size)
+#else
+void up_allocate_heap(void **heap_start, size_t *heap_size)
+#endif /* CONFIG_BUILD_KERNEL */
+{
+#if defined(CONFIG_BUILD_PROTECTED) && defined(CONFIG_MM_KERNEL_HEAP)
+  /* Get the size and position of the user-space heap.
+   * This heap begins after the user-space .bss section.
+   */
+
+  uintptr_t ubase = (uintptr_t)USERSPACE->us_bssend;
+  size_t    usize = (uintptr_t)USERSPACE->us_heapend - ubase;
+
+  /* Return the user-space heap settings */
+
+  *heap_start = (void *)ubase;
+  *heap_size  = usize;
+
+  /* Allow user-mode access to the user heap memory in PMP
+   * is already done in qemu_rv_userspace().
+   */
+
+#else
+  /* Return the heap settings */
+
+  *heap_start = (void *)g_idle_topstack;
+  *heap_size = KRAM_END - g_idle_topstack;
+#endif /* CONFIG_BUILD_PROTECTED && CONFIG_MM_KERNEL_HEAP */
+}
+
+/****************************************************************************
+ * Name: up_allocate_kheap
+ *
+ * Description:
+ *   For the kernel build (CONFIG_BUILD_PROTECTED=y) with both kernel- and
+ *   user-space heaps (CONFIG_MM_KERNEL_HEAP=y), this function allocates
+ *   (and protects) the kernel-space heap.
+ *
+ ****************************************************************************/
+
+#if defined(CONFIG_BUILD_PROTECTED) && defined(CONFIG_MM_KERNEL_HEAP) && \
+    defined(__KERNEL__)
+void up_allocate_kheap(void **heap_start, size_t *heap_size)
+{
+  /* Return the kernel heap settings. */
+
+  *heap_start = (void *)g_idle_topstack;
+  *heap_size = KRAM_END - g_idle_topstack;
+}
+#endif /* CONFIG_BUILD_PROTECTED && CONFIG_MM_KERNEL_HEAP */
+
+/****************************************************************************
+ * Name: up_addregion
+ ****************************************************************************/
+
+void up_addregion(void)
+{
+}
diff --git a/arch/risc-v/src/qemu-rv/qemu_rv_head.S b/arch/risc-v/src/qemu-rv/qemu_rv_head.S
index 7c2bba94ca..ebfe5d1c68 100644
--- a/arch/risc-v/src/qemu-rv/qemu_rv_head.S
+++ b/arch/risc-v/src/qemu-rv/qemu_rv_head.S
@@ -97,7 +97,11 @@ __start:
 
   /* Jump to qemu_rv_start */
 
+#ifdef CONFIG_ARCH_USE_S_MODE
+  jal  x1, qemu_rv_start_s
+#else
   jal  x1, qemu_rv_start
+#endif
 
   /* We shouldn't return from _start */
 
diff --git a/arch/risc-v/src/qemu-rv/qemu_rv_irq.c b/arch/risc-v/src/qemu-rv/qemu_rv_irq.c
index 4951ab6eab..c9ea8a94bc 100644
--- a/arch/risc-v/src/qemu-rv/qemu_rv_irq.c
+++ b/arch/risc-v/src/qemu-rv/qemu_rv_irq.c
@@ -119,21 +119,21 @@ void up_disable_irq(int irq)
 {
   int extirq;
 
-  if (irq == RISCV_IRQ_MSOFT)
+  if (irq == RISCV_IRQ_SOFT)
     {
-      /* Read mstatus & clear machine software interrupt enable in mie */
+      /* Read m/sstatus & clear machine software interrupt enable in m/sie */
 
-      CLEAR_CSR(mie, MIE_MSIE);
+      CLEAR_CSR(CSR_IE, IE_SIE);
     }
-  else if (irq == RISCV_IRQ_MTIMER)
+  else if (irq == RISCV_IRQ_TIMER)
     {
-      /* Read mstatus & clear machine timer interrupt enable in mie */
+      /* Read m/sstatus & clear timer interrupt enable in m/sie */
 
-      CLEAR_CSR(mie, MIE_MTIE);
+      CLEAR_CSR(CSR_IE, IE_TIE);
     }
-  else if (irq > RISCV_IRQ_MEXT)
+  else if (irq > RISCV_IRQ_EXT)
     {
-      extirq = irq - RISCV_IRQ_MEXT;
+      extirq = irq - RISCV_IRQ_EXT;
 
       /* Clear enable bit for the irq */
 
@@ -161,21 +161,21 @@ void up_enable_irq(int irq)
 {
   int extirq;
 
-  if (irq == RISCV_IRQ_MSOFT)
+  if (irq == RISCV_IRQ_SOFT)
     {
-      /* Read mstatus & set machine software interrupt enable in mie */
+      /* Read m/sstatus & set machine software interrupt enable in m/sie */
 
-      SET_CSR(mie, MIE_MSIE);
+      SET_CSR(CSR_IE, IE_SIE);
     }
-  else if (irq == RISCV_IRQ_MTIMER)
+  else if (irq == RISCV_IRQ_TIMER)
     {
-      /* Read mstatus & set machine timer interrupt enable in mie */
+      /* Read m/sstatus & set timer interrupt enable in m/sie */
 
-      SET_CSR(mie, MIE_MTIE);
+      SET_CSR(CSR_IE, IE_TIE);
     }
-  else if (irq > RISCV_IRQ_MEXT)
+  else if (irq > RISCV_IRQ_EXT)
     {
-      extirq = irq - RISCV_IRQ_MEXT;
+      extirq = irq - RISCV_IRQ_EXT;
 
       /* Set enable bit for the irq */
 
@@ -195,16 +195,13 @@ irqstate_t up_irq_enable(void)
 {
   irqstate_t oldstat;
 
-#if 1
-  /* Enable MEIE (machine external interrupt enable) */
+  /* Enable external interrupts (mie/sie) */
 
-  /* TODO: should move to up_enable_irq() */
+  SET_CSR(CSR_IE, IE_EIE);
 
-  SET_CSR(mie, MIE_MEIE);
-#endif
+  /* Read and enable global interrupts (M/SIE) in m/sstatus */
 
-  /* Read mstatus & set machine interrupt enable (MIE) in mstatus */
+  oldstat = READ_AND_SET_CSR(CSR_STATUS, STATUS_IE);
 
-  oldstat = READ_AND_SET_CSR(mstatus, MSTATUS_MIE);
   return oldstat;
 }
diff --git a/arch/risc-v/src/qemu-rv/qemu_rv_irq_dispatch.c b/arch/risc-v/src/qemu-rv/qemu_rv_irq_dispatch.c
index 37037fd06c..f73f672169 100644
--- a/arch/risc-v/src/qemu-rv/qemu_rv_irq_dispatch.c
+++ b/arch/risc-v/src/qemu-rv/qemu_rv_irq_dispatch.c
@@ -59,7 +59,7 @@ void *riscv_dispatch_irq(uintptr_t vector, uintptr_t *regs)
 
   /* Firstly, check if the irq is machine external interrupt */
 
-  if (RISCV_IRQ_MEXT == irq)
+  if (RISCV_IRQ_EXT == irq)
     {
       uintptr_t val = getreg32(QEMU_RV_PLIC_CLAIM);
 
@@ -68,20 +68,20 @@ void *riscv_dispatch_irq(uintptr_t vector, uintptr_t *regs)
       irq += val;
     }
 
-  /* MEXT means no interrupt */
+  /* EXT means no interrupt */
 
-  if (RISCV_IRQ_MEXT != irq)
+  if (RISCV_IRQ_EXT != irq)
     {
       /* Deliver the IRQ */
 
       regs = riscv_doirq(irq, regs);
     }
 
-  if (RISCV_IRQ_MEXT <= irq)
+  if (RISCV_IRQ_EXT <= irq)
     {
       /* Then write PLIC_CLAIM to clear pending in PLIC */
 
-      putreg32(irq - RISCV_IRQ_MEXT, QEMU_RV_PLIC_CLAIM);
+      putreg32(irq - RISCV_IRQ_EXT, QEMU_RV_PLIC_CLAIM);
     }
 
   return regs;
diff --git a/arch/risc-v/src/qemu-rv/qemu_rv_mm_init.c b/arch/risc-v/src/qemu-rv/qemu_rv_mm_init.c
new file mode 100644
index 0000000000..0065f9539b
--- /dev/null
+++ b/arch/risc-v/src/qemu-rv/qemu_rv_mm_init.c
@@ -0,0 +1,171 @@
+/****************************************************************************
+ * arch/risc-v/src/qemu-rv/qemu_rv_mm_init.c
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.  The
+ * ASF licenses this file to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the
+ * License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ *
+ ****************************************************************************/
+
+/****************************************************************************
+ * Included Files
+ ****************************************************************************/
+
+#include <nuttx/config.h>
+#include <nuttx/arch.h>
+
+#include <stdint.h>
+#include <assert.h>
+#include <debug.h>
+
+#include <arch/board/board_memorymap.h>
+
+#include "qemu_rv_memorymap.h"
+
+#include "riscv_internal.h"
+#include "riscv_mmu.h"
+
+/****************************************************************************
+ * Pre-processor Definitions
+ ****************************************************************************/
+
+/* Map the whole I/O memory with vaddr = paddr mappings */
+
+#define MMU_IO_BASE     (0x00000000)
+#define MMU_IO_SIZE     (0x80000000)
+
+/* Physical and virtual addresses to page tables (vaddr = paddr mapping) */
+
+#define PGT_L1_PBASE    (uintptr_t)&m_l1_pgtable
+#define PGT_L2_PBASE    (uintptr_t)&m_l2_pgtable
+#define PGT_L3_PBASE    (uintptr_t)&m_l3_pgtable
+#define PGT_L1_VBASE    PGT_L1_PBASE
+#define PGT_L2_VBASE    PGT_L2_PBASE
+#define PGT_L3_VBASE    PGT_L3_PBASE
+
+#define PGT_L1_SIZE     (512)  /* Enough to map 512 GiB */
+#define PGT_L2_SIZE     (512)  /* Enough to map 1 GiB */
+#define PGT_L3_SIZE     (1024) /* Enough to map 4 MiB (2MiB x 2) */
+
+/****************************************************************************
+ * Private Data
+ ****************************************************************************/
+
+/* Kernel mappings simply here, mapping is vaddr=paddr */
+
+static uint64_t         m_l1_pgtable[PGT_L1_SIZE] locate_data(".pgtables");
+static uint64_t         m_l2_pgtable[PGT_L2_SIZE] locate_data(".pgtables");
+static uint64_t         m_l3_pgtable[PGT_L3_SIZE] locate_data(".pgtables");
+
+/* Kernel mappings (L1 base) */
+
+uintptr_t               g_kernel_mappings  = PGT_L1_VBASE;
+uintptr_t               g_kernel_pgt_pbase = PGT_L1_PBASE;
+
+/****************************************************************************
+ * Private Functions
+ ****************************************************************************/
+
+static void map_region(uintptr_t paddr, uintptr_t vaddr, size_t size,
+                       uint32_t mmuflags)
+{
+  uintptr_t l3base;
+  uintptr_t end_vaddr;
+
+  /* Start index for the L3 table, kernel flash is always first */
+
+  l3base = PGT_L3_PBASE + ((paddr - KFLASH_START) / RV_MMU_PAGE_ENTRIES);
+
+  /* Map the region to the L3 table as a whole */
+
+  mmu_ln_map_region(3, l3base, paddr, vaddr, size, mmuflags);
+
+  /* Connect to L2 table */
+
+  end_vaddr = vaddr + size;
+  while (vaddr < end_vaddr)
+    {
+      mmu_ln_setentry(2, PGT_L2_VBASE, l3base, vaddr, PTE_G);
+      l3base += RV_MMU_L3_PAGE_SIZE;
+      vaddr += RV_MMU_L2_PAGE_SIZE;
+    }
+}
+
+/****************************************************************************
+ * Public Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: qemu_rv_kernel_mappings
+ *
+ * Description:
+ *  Setup kernel mappings when usinc CONFIG_BUILD_KERNEL. Sets up the kernel
+ *  MMU mappings.
+ *
+ ****************************************************************************/
+
+void qemu_rv_kernel_mappings(void)
+{
+  /* Begin mapping memory to MMU; note that at this point the MMU is not yet
+   * active, so the page table virtual addresses are actually physical
+   * addresses and so forth. M-mode does not perform translations anyhow, so
+   * this mapping is quite simple to do
+   */
+
+  /* Map I/O region, use 2 L1 entries (i.e. 2 * 1GB address space) */
+
+  binfo("map I/O regions\n");
+  mmu_ln_map_region(1, PGT_L1_VBASE, MMU_IO_BASE, MMU_IO_BASE,
+                    MMU_IO_SIZE, MMU_IO_FLAGS);
+
+  /* Map the kernel text and data for L2/L3 */
+
+  binfo("map kernel text\n");
+  map_region(KFLASH_START, KFLASH_START, KFLASH_SIZE, MMU_KTEXT_FLAGS);
+
+  binfo("map kernel data\n");
+  map_region(KSRAM_START, KSRAM_START, KSRAM_SIZE, MMU_KDATA_FLAGS);
+
+  /* Connect the L1 and L2 page tables for the kernel text and data */
+
+  binfo("connect the L1 and L2 page tables\n");
+  mmu_ln_setentry(1, PGT_L1_VBASE, PGT_L2_PBASE, KFLASH_START, PTE_G);
+
+  /* Map the page pool */
+
+  binfo("map the page pool\n");
+  mmu_ln_map_region(2, PGT_L2_VBASE, PGPOOL_START, PGPOOL_START, PGPOOL_SIZE,
+                    MMU_KDATA_FLAGS);
+}
+
+/****************************************************************************
+ * Name: qemu_rv_mm_init
+ *
+ * Description:
+ *  Setup kernel mappings when using CONFIG_BUILD_KERNEL. Sets up kernel MMU
+ *  mappings. Function also sets the first address environment (satp value).
+ *
+ ****************************************************************************/
+
+void qemu_rv_mm_init(void)
+{
+  /* Setup the kernel mappings */
+
+  qemu_rv_kernel_mappings();
+
+  /* Enable MMU (note: system is still in M-mode) */
+
+  binfo("mmu_enable: satp=%lx\n", g_kernel_pgt_pbase);
+  mmu_enable(g_kernel_pgt_pbase, 0);
+}
diff --git a/arch/risc-v/src/qemu-rv/qemu_rv_timerisr.c b/arch/risc-v/src/qemu-rv/qemu_rv_mm_init.h
similarity index 66%
copy from arch/risc-v/src/qemu-rv/qemu_rv_timerisr.c
copy to arch/risc-v/src/qemu-rv/qemu_rv_mm_init.h
index ef1d622980..3c3c7378b2 100644
--- a/arch/risc-v/src/qemu-rv/qemu_rv_timerisr.c
+++ b/arch/risc-v/src/qemu-rv/qemu_rv_mm_init.h
@@ -1,5 +1,5 @@
 /****************************************************************************
- * arch/risc-v/src/qemu-rv/qemu_rv_timerisr.c
+ * arch/risc-v/src/qemu-rv/qemu_rv_mm_init.h
  *
  * Licensed to the Apache Software Foundation (ASF) under one or more
  * contributor license agreements.  See the NOTICE file distributed with
@@ -18,54 +18,41 @@
  *
  ****************************************************************************/
 
+#ifndef __ARCH_RISC_V_SRC_QEMURV_QEMURV_MM_INIT_H
+#define __ARCH_RISC_V_SRC_QEMURV_QEMURV_MM_INIT_H
+
 /****************************************************************************
  * Included Files
  ****************************************************************************/
 
 #include <nuttx/config.h>
 
-#include <assert.h>
-#include <stdint.h>
-#include <time.h>
-#include <debug.h>
-
-#include <nuttx/arch.h>
-#include <nuttx/clock.h>
-#include <nuttx/spinlock.h>
-#include <nuttx/timers/arch_alarm.h>
-#include <arch/board/board.h>
-
-#include "riscv_internal.h"
-#include "riscv_mtimer.h"
-#include "hardware/qemu_rv_memorymap.h"
-#include "hardware/qemu_rv_clint.h"
+#include "riscv_mmu.h"
 
 /****************************************************************************
- * Pre-processor Definitions
+ * Public Functions Prototypes
  ****************************************************************************/
 
-#define MTIMER_FREQ 10000000
-
 /****************************************************************************
- * Public Functions
+ * Name: qemu_rv_kernel_mappings
+ *
+ * Description:
+ *  Setup kernel mappings when using CONFIG_BUILD_KERNEL. Sets up the kernel
+ *  MMU mappings.
+ *
  ****************************************************************************/
 
+void qemu_rv_kernel_mappings(void);
+
 /****************************************************************************
- * Name: up_timer_initialize
+ * Name: qemu_rv_mm_init
  *
  * Description:
- *   This function is called during start-up to initialize
- *   the timer interrupt.
+ *  Setup kernel mappings when using CONFIG_BUILD_KERNEL. Sets up kernel MMU
+ *  mappings. Function also sets the first address environment (satp value).
  *
  ****************************************************************************/
 
-void up_timer_initialize(void)
-{
-  struct oneshot_lowerhalf_s *lower = riscv_mtimer_initialize(
-    QEMU_RV_CLINT_MTIME, QEMU_RV_CLINT_MTIMECMP,
-    RISCV_IRQ_MTIMER, MTIMER_FREQ);
-
-  DEBUGASSERT(lower);
+void qemu_rv_mm_init(void);
 
-  up_alarm_set_lowerhalf(lower);
-}
+#endif /* __ARCH_RISC_V_SRC_QEMURV_QEMURV_MM_INIT_H */
diff --git a/arch/risc-v/src/qemu-rv/qemu_rv_timerisr.c b/arch/risc-v/src/qemu-rv/qemu_rv_pgalloc.c
similarity index 64%
copy from arch/risc-v/src/qemu-rv/qemu_rv_timerisr.c
copy to arch/risc-v/src/qemu-rv/qemu_rv_pgalloc.c
index ef1d622980..8b04758ac5 100644
--- a/arch/risc-v/src/qemu-rv/qemu_rv_timerisr.c
+++ b/arch/risc-v/src/qemu-rv/qemu_rv_pgalloc.c
@@ -1,5 +1,5 @@
 /****************************************************************************
- * arch/risc-v/src/qemu-rv/qemu_rv_timerisr.c
+ * arch/risc-v/src/qemu-rv/qemu_rv_pgalloc.c
  *
  * Licensed to the Apache Software Foundation (ASF) under one or more
  * contributor license agreements.  See the NOTICE file distributed with
@@ -22,50 +22,46 @@
  * Included Files
  ****************************************************************************/
 
+#include <nuttx/arch.h>
 #include <nuttx/config.h>
+#include <nuttx/pgalloc.h>
 
 #include <assert.h>
-#include <stdint.h>
-#include <time.h>
 #include <debug.h>
 
-#include <nuttx/arch.h>
-#include <nuttx/clock.h>
-#include <nuttx/spinlock.h>
-#include <nuttx/timers/arch_alarm.h>
-#include <arch/board/board.h>
-
-#include "riscv_internal.h"
-#include "riscv_mtimer.h"
-#include "hardware/qemu_rv_memorymap.h"
-#include "hardware/qemu_rv_clint.h"
+#include <arch/board/board_memorymap.h>
 
 /****************************************************************************
  * Pre-processor Definitions
  ****************************************************************************/
 
-#define MTIMER_FREQ 10000000
+/****************************************************************************
+ * Private Data
+ ****************************************************************************/
+
+/****************************************************************************
+ * Public Data
+ ****************************************************************************/
 
 /****************************************************************************
  * Public Functions
  ****************************************************************************/
 
 /****************************************************************************
- * Name: up_timer_initialize
+ * Name: up_allocate_pgheap
  *
  * Description:
- *   This function is called during start-up to initialize
- *   the timer interrupt.
+ *   If there is a page allocator in the configuration, then this function
+ *   must be provided by the platform-specific code.  The OS initialization
+ *   logic will call this function early in the initialization sequence to
+ *   get the page heap information needed to configure the page allocator.
  *
  ****************************************************************************/
 
-void up_timer_initialize(void)
+void up_allocate_pgheap(void **heap_start, size_t *heap_size)
 {
-  struct oneshot_lowerhalf_s *lower = riscv_mtimer_initialize(
-    QEMU_RV_CLINT_MTIME, QEMU_RV_CLINT_MTIMECMP,
-    RISCV_IRQ_MTIMER, MTIMER_FREQ);
-
-  DEBUGASSERT(lower);
+  DEBUGASSERT(heap_start && heap_size);
 
-  up_alarm_set_lowerhalf(lower);
+  *heap_start = (void *)PGPOOL_START;
+  *heap_size  = (size_t)PGPOOL_SIZE;
 }
diff --git a/arch/risc-v/src/qemu-rv/qemu_rv_start.c b/arch/risc-v/src/qemu-rv/qemu_rv_start.c
index f1a9f4e73d..57d014a612 100644
--- a/arch/risc-v/src/qemu-rv/qemu_rv_start.c
+++ b/arch/risc-v/src/qemu-rv/qemu_rv_start.c
@@ -31,6 +31,10 @@
 #include "riscv_internal.h"
 #include "chip.h"
 
+#ifdef CONFIG_BUILD_KERNEL
+#  include "qemu_rv_mm_init.h"
+#endif
+
 /****************************************************************************
  * Pre-processor Definitions
  ****************************************************************************/
@@ -41,6 +45,10 @@
 #define showprogress(c)
 #endif
 
+#if defined (CONFIG_BUILD_KERNEL) && !defined (CONFIG_ARCH_USE_S_MODE)
+#  error "Target requires kernel in S-mode, enable CONFIG_ARCH_USE_S_MODE"
+#endif
+
 /****************************************************************************
  * Public Data
  ****************************************************************************/
@@ -91,8 +99,18 @@ void qemu_rv_start(int mhartid)
 
   /* Do board initialization */
 
+#ifdef CONFIG_ARCH_USE_S_MODE
+  /* Initialize the per CPU areas */
+
+  riscv_percpu_add_hart(mhartid);
+#endif
+
   showprogress('C');
 
+#ifdef CONFIG_BUILD_KERNEL
+  qemu_rv_mm_init();
+#endif
+
   /* Call nx_start() */
 
   nx_start();
@@ -109,6 +127,41 @@ cpux:
     }
 }
 
+#ifdef CONFIG_ARCH_USE_S_MODE
+void qemu_rv_start_s(int mhartid)
+{
+  /* Disable MMU and enable PMP */
+
+  SET_CSR(satp, 0x0);
+  SET_CSR(pmpaddr0, 0x3fffffffffffffull);
+  SET_CSR(pmpcfg0, 0xf);
+
+  /* Set exception and interrupt delegation for S-mode */
+
+  SET_CSR(medeleg, 0xffff);
+  SET_CSR(mideleg, 0xffff);
+
+  /* Allow to write satp from S-mode */
+
+  CLEAR_CSR(mstatus, MSTATUS_TVM);
+
+  /* Set mstatus to S-mode and enable SUM */
+
+  CLEAR_CSR(mstatus, ~MSTATUS_MPP_MASK);
+  SET_CSR(mstatus, MSTATUS_MPPS | SSTATUS_SUM);
+
+  /* Set the trap vector for S-mode */
+
+  extern void __trap_vec(void);
+  SET_CSR(stvec, (uintptr_t)__trap_vec);
+
+  /* Set mepc to the entry */
+
+  SET_CSR(mepc, (uintptr_t)qemu_rv_start);
+  asm volatile("mret");
+}
+#endif
+
 void riscv_serialinit(void)
 {
   up_serialinit();
diff --git a/arch/risc-v/src/qemu-rv/qemu_rv_timerisr.c b/arch/risc-v/src/qemu-rv/qemu_rv_timerisr.c
index ef1d622980..fd594f4ca6 100644
--- a/arch/risc-v/src/qemu-rv/qemu_rv_timerisr.c
+++ b/arch/risc-v/src/qemu-rv/qemu_rv_timerisr.c
@@ -61,6 +61,7 @@
 
 void up_timer_initialize(void)
 {
+#ifndef CONFIG_BUILD_KERNEL
   struct oneshot_lowerhalf_s *lower = riscv_mtimer_initialize(
     QEMU_RV_CLINT_MTIME, QEMU_RV_CLINT_MTIMECMP,
     RISCV_IRQ_MTIMER, MTIMER_FREQ);
@@ -68,4 +69,5 @@ void up_timer_initialize(void)
   DEBUGASSERT(lower);
 
   up_alarm_set_lowerhalf(lower);
+#endif
 }