diff --git a/arch/Kconfig b/arch/Kconfig index 73061ece402..74fe13e3c36 100644 --- a/arch/Kconfig +++ b/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 diff --git a/arch/risc-v/src/common/riscv_addrenv.c b/arch/risc-v/src/common/riscv_addrenv.c index f26a5d11c28..a6524abe47d 100644 --- a/arch/risc-v/src/common/riscv_addrenv.c +++ b/arch/risc-v/src/common/riscv_addrenv.c @@ -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) diff --git a/arch/risc-v/src/common/riscv_exception.c b/arch/risc-v/src/common/riscv_exception.c index f560a60b0a0..cb94c153a81 100644 --- a/arch/risc-v/src/common/riscv_exception.c +++ b/arch/risc-v/src/common/riscv_exception.c @@ -30,6 +30,14 @@ #include #include +#ifdef CONFIG_PAGING +# include +#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); diff --git a/arch/risc-v/src/common/riscv_internal.h b/arch/risc-v/src/common/riscv_internal.h index ac16c0bd87f..100bcb79d69 100644 --- a/arch/risc-v/src/common/riscv_internal.h +++ b/arch/risc-v/src/common/riscv_internal.h @@ -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 ********************************************************************/ diff --git a/boards/risc-v/qemu-rv/rv-virt/configs/knsh32_paging/defconfig b/boards/risc-v/qemu-rv/rv-virt/configs/knsh32_paging/defconfig new file mode 100644 index 00000000000..3c809f0c05c --- /dev/null +++ b/boards/risc-v/qemu-rv/rv-virt/configs/knsh32_paging/defconfig @@ -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