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:
parent
ae9ef972c0
commit
c67502d9b4
5 changed files with 242 additions and 2 deletions
10
arch/Kconfig
10
arch/Kconfig
|
@ -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
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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 ********************************************************************/
|
||||
|
|
|
@ -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
|
Loading…
Reference in a new issue