riscv: Implement page-fault exception and on-demand paging

When an application is being loaded `up_addrenv_create ` calls
`create_region` to create the address environment. Only the first
entry is mapped when the region is created. Virtual memory that is
not mapped will trigger an exception when accessed. Other memory
pages are allocated and mapped on-demand. This enables setting
larger heap and stack for the process without compromising the
overall system memory.
This commit is contained in:
Tiago Medicci Serrano 2024-02-23 10:33:24 -03:00 committed by Xiang Xiao
parent ae9ef972c0
commit c67502d9b4
5 changed files with 242 additions and 2 deletions

View file

@ -903,6 +903,16 @@ config ARCH_PGPOOL_SIZE
endif # ARCH_PGPOOL_MAPPING
endif # ARCH_ADDRENV && ARCH_NEED_ADDRENV_MAPPING
config PAGING
bool "On-demand paging"
default n
depends on BUILD_KERNEL && ARCH_USE_MMU && !ARCH_ROMPGTABLE && !LEGACY_PAGING
---help---
If set =y in your configation file, this setting will enable on-demand
paging, which relies on a MMU to enable larger virtual memory spaces
and map it to physical memory on-demand (usually during a page-fault
exception).
menuconfig LEGACY_PAGING
bool "Legacy On-demand paging"
default n

View file

@ -282,7 +282,13 @@ static int create_region(arch_addrenv_t *addrenv, uintptr_t vaddr,
/* Then allocate memory for the region data */
for (j = 0; j < ENTRIES_PER_PGT && nmapped < size; j++)
for (j = 0;
#ifdef CONFIG_PAGING
j < 1;
#else
j < ENTRIES_PER_PGT && nmapped < size;
#endif
j++)
{
paddr = mm_pgalloc(1);
if (!paddr)

View file

@ -30,6 +30,14 @@
#include <nuttx/irq.h>
#include <nuttx/arch.h>
#ifdef CONFIG_PAGING
# include <nuttx/pgalloc.h>
#endif
#ifdef CONFIG_PAGING
# include "pgalloc.h"
# include "riscv_mmu.h"
#endif
#include "riscv_internal.h"
#include "chip.h"
@ -87,6 +95,117 @@ int riscv_exception(int mcause, void *regs, void *args)
return 0;
}
/****************************************************************************
* Name: riscv_fillpage
*
* Description:
* This function is an exception handler for page faults in a RISC-V.
* It is invoked when a page fault exception occurs, which is typically
* when a process tries to access a page that is not currently in memory.
*
* The function takes as arguments the machine cause (mcause) which
* indicates the cause of the exception, a pointer to the register state
* at the time of the exception (regs), and a pointer to any additional
* arguments (args).
*
* The function should handle the exception appropriately, typically by
* loading the required page into memory and updating the page table.
*
* Input Parameters:
* mcause - The machine cause of the exception.
* regs - A pointer to the register state at the time of the exception.
* args - A pointer to any additional arguments.
*
* Returned Value:
* Zero (OK) on success; a negated errno value on failure.
*
****************************************************************************/
#ifdef CONFIG_PAGING
int riscv_fillpage(int mcause, void *regs, void *args)
{
uintptr_t cause = mcause & RISCV_IRQ_MASK;
uintptr_t ptlast;
uintptr_t ptprev;
uintptr_t paddr;
uintptr_t vaddr;
uint32_t ptlevel;
uintptr_t satp;
uint32_t mmuflags;
_info("EXCEPTION: %s. MCAUSE: %" PRIxREG ", EPC: %" PRIxREG
", MTVAL: %" PRIxREG "\n",
mcause > RISCV_MAX_EXCEPTION ? "Unknown" : g_reasons_str[cause],
cause, READ_CSR(CSR_EPC), READ_CSR(CSR_TVAL));
vaddr = MM_PGALIGNDOWN(READ_CSR(CSR_TVAL));
if (vaddr >= CONFIG_ARCH_TEXT_VBASE && vaddr <= ARCH_TEXT_VEND)
{
mmuflags = MMU_UTEXT_FLAGS;
/* Write access to .text region needs to be set according to
* https://github.com/apache/nuttx/pull/6193.
*/
mmuflags |= PTE_W;
}
else if (vaddr >= CONFIG_ARCH_DATA_VBASE && vaddr <= ARCH_DATA_VEND)
{
mmuflags = MMU_UDATA_FLAGS;
}
else if (vaddr >= CONFIG_ARCH_HEAP_VBASE && vaddr <= ARCH_HEAP_VEND)
{
mmuflags = MMU_UDATA_FLAGS;
}
else
{
_alert("PANIC!!! virtual address not mappable: %" PRIxPTR "\n", vaddr);
up_irq_save();
CURRENT_REGS = regs;
PANIC_WITH_REGS("panic", regs);
}
satp = READ_CSR(CSR_SATP);
ptprev = riscv_pgvaddr(mmu_satp_to_paddr(satp));
ptlevel = ARCH_SPGTS;
paddr = mmu_pte_to_paddr(mmu_ln_getentry(ptlevel, ptprev, vaddr));
if (!paddr)
{
/* Nothing yet, allocate one page for final level page table */
paddr = mm_pgalloc(1);
if (!paddr)
{
return -ENOMEM;
}
/* Map the page table to the prior level */
mmu_ln_setentry(ptlevel, ptprev, paddr, vaddr, MMU_UPGT_FLAGS);
/* This is then used to map the final level */
riscv_pgwipe(paddr);
}
ptlast = riscv_pgvaddr(paddr);
paddr = mm_pgalloc(1);
if (!paddr)
{
return -ENOMEM;
}
/* Wipe the physical page memory */
riscv_pgwipe(paddr);
/* Then map the virtual address to the physical address */
mmu_ln_setentry(ptlevel + 1, ptlast, paddr, vaddr, mmuflags);
return 0;
}
#endif /* CONFIG_PAGING */
/****************************************************************************
* Name: riscv_exception_attach
*
@ -130,9 +249,16 @@ void riscv_exception_attach(void)
#endif
irq_attach(RISCV_IRQ_INSTRUCTIONPF, riscv_exception, NULL);
#ifdef CONFIG_PAGING
irq_attach(RISCV_IRQ_LOADPF, riscv_fillpage, NULL);
irq_attach(RISCV_IRQ_STOREPF, riscv_fillpage, NULL);
#else
irq_attach(RISCV_IRQ_LOADPF, riscv_exception, NULL);
irq_attach(RISCV_IRQ_RESERVED, riscv_exception, NULL);
irq_attach(RISCV_IRQ_STOREPF, riscv_exception, NULL);
#endif
irq_attach(RISCV_IRQ_RESERVED, riscv_exception, NULL);
#ifdef CONFIG_SMP
irq_attach(RISCV_IRQ_SOFT, riscv_pause_handler, NULL);

View file

@ -312,6 +312,7 @@ void riscv_netinitialize(void);
uintptr_t *riscv_doirq(int irq, uintptr_t *regs);
int riscv_exception(int mcause, void *regs, void *args);
int riscv_fillpage(int mcause, void *regs, void *args);
int riscv_misaligned(int irq, void *context, void *arg);
/* Debug ********************************************************************/

View file

@ -0,0 +1,97 @@
#
# This file is autogenerated: PLEASE DO NOT EDIT IT.
#
# You can use "make menuconfig" to make any modifications to the installed .config file.
# You can then do "make savedefconfig" to generate a new defconfig file that includes your
# modifications.
#
# CONFIG_ASSERTIONS_FILENAME is not set
# CONFIG_DISABLE_OS_API is not set
# CONFIG_NDEBUG is not set
# CONFIG_NSH_DISABLE_LOSMART is not set
CONFIG_16550_ADDRWIDTH=0
CONFIG_16550_UART0=y
CONFIG_16550_UART0_BASE=0x10000000
CONFIG_16550_UART0_CLOCK=3686400
CONFIG_16550_UART0_IRQ=35
CONFIG_16550_UART0_SERIAL_CONSOLE=y
CONFIG_16550_UART=y
CONFIG_ARCH="risc-v"
CONFIG_ARCH_ADDRENV=y
CONFIG_ARCH_BOARD="rv-virt"
CONFIG_ARCH_BOARD_QEMU_RV_VIRT=y
CONFIG_ARCH_CHIP="qemu-rv"
CONFIG_ARCH_CHIP_QEMU_RV32=y
CONFIG_ARCH_CHIP_QEMU_RV=y
CONFIG_ARCH_CHIP_QEMU_RV_ISA_A=y
CONFIG_ARCH_CHIP_QEMU_RV_ISA_C=y
CONFIG_ARCH_CHIP_QEMU_RV_ISA_M=y
CONFIG_ARCH_DATA_NPAGES=128
CONFIG_ARCH_DATA_VBASE=0xC0100000
CONFIG_ARCH_HEAP_NPAGES=2048
CONFIG_ARCH_HEAP_VBASE=0xC0800000
CONFIG_ARCH_INTERRUPTSTACK=2048
CONFIG_ARCH_KERNEL_STACKSIZE=3072
CONFIG_ARCH_PGPOOL_MAPPING=y
CONFIG_ARCH_PGPOOL_PBASE=0x80800000
CONFIG_ARCH_PGPOOL_SIZE=4194304
CONFIG_ARCH_PGPOOL_VBASE=0x80800000
CONFIG_ARCH_RISCV=y
CONFIG_ARCH_STACKDUMP=y
CONFIG_ARCH_TEXT_NPAGES=128
CONFIG_ARCH_TEXT_VBASE=0xC0000000
CONFIG_ARCH_USE_MMU=y
CONFIG_ARCH_USE_MPU=y
CONFIG_ARCH_USE_S_MODE=y
CONFIG_BINFMT_ELF_EXECUTABLE=y
CONFIG_BOARD_LATE_INITIALIZE=y
CONFIG_BOARD_LOOPSPERMSEC=6366
CONFIG_BUILD_KERNEL=y
CONFIG_DEBUG_FEATURES=y
CONFIG_DEBUG_SYMBOLS=y
CONFIG_DEV_ZERO=y
CONFIG_ELF=y
CONFIG_EXAMPLES_HELLO=m
CONFIG_EXAMPLES_HELLO_STACKSIZE=8192
CONFIG_FS_PROCFS=y
CONFIG_FS_ROMFS=y
CONFIG_GRAN_INTR=y
CONFIG_IDLETHREAD_STACKSIZE=3072
CONFIG_INIT_FILEPATH="/system/bin/init"
CONFIG_INIT_MOUNT=y
CONFIG_INIT_MOUNT_FLAGS=0x1
CONFIG_INIT_MOUNT_TARGET="/system/bin"
CONFIG_INIT_STACKSIZE=3072
CONFIG_INTELHEX_BINARY=y
CONFIG_LIBC_ENVPATH=y
CONFIG_LIBC_EXECFUNCS=y
CONFIG_LIBC_PERROR_STDOUT=y
CONFIG_LIBC_STRERROR=y
CONFIG_LIBM=y
CONFIG_MM_PGALLOC=y
CONFIG_NFILE_DESCRIPTORS_PER_BLOCK=6
CONFIG_NSH_ARCHINIT=y
CONFIG_NSH_FILEIOSIZE=512
CONFIG_NSH_FILE_APPS=y
CONFIG_NSH_READLINE=y
CONFIG_PAGING=y
CONFIG_PATH_INITIAL="/system/bin"
CONFIG_POSIX_SPAWN_DEFAULT_STACKSIZE=1048576
CONFIG_RAM_SIZE=4194304
CONFIG_RAM_START=0x80400000
CONFIG_READLINE_CMD_HISTORY=y
CONFIG_RR_INTERVAL=200
CONFIG_SCHED_LPWORK=y
CONFIG_SCHED_WAITPID=y
CONFIG_SERIAL_UART_ARCH_MMIO=y
CONFIG_SIG_DEFAULT=y
CONFIG_START_MONTH=12
CONFIG_START_YEAR=2021
CONFIG_SYMTAB_ORDEREDBYNAME=y
CONFIG_SYSLOG_TIMESTAMP=y
CONFIG_SYSTEM_NSH=y
CONFIG_SYSTEM_NSH_PROGNAME="init"
CONFIG_TESTING_GETPRIME=y
CONFIG_TESTING_OSTEST=y
CONFIG_TLS_LOG2_MAXSTACK=20
CONFIG_USEC_PER_TICK=1000