From 926a19217ef207ffe07da4b91f381ace92b7bdf5 Mon Sep 17 00:00:00 2001 From: Ville Juven Date: Wed, 19 Jan 2022 10:14:28 +0200 Subject: [PATCH] Add simple MMU driver for RISC-V (Sv39) Sv39 is the only mode supported for now. However, it should be trivial to extend the driver to support the other modes (including Sv32) as well. The driver is tested with mpfs only, but it should work with any riscv implementation. --- arch/risc-v/Kconfig | 6 + arch/risc-v/src/common/riscv_mmu.c | 107 +++++++++++++++ arch/risc-v/src/common/riscv_mmu.h | 206 +++++++++++++++++++++++++++++ arch/risc-v/src/mpfs/Make.defs | 14 +- arch/risc-v/src/mpfs/mpfs_head.S | 5 + 5 files changed, 336 insertions(+), 2 deletions(-) create mode 100644 arch/risc-v/src/common/riscv_mmu.c create mode 100644 arch/risc-v/src/common/riscv_mmu.h diff --git a/arch/risc-v/Kconfig b/arch/risc-v/Kconfig index 0d417895c6..72bc2b2f78 100644 --- a/arch/risc-v/Kconfig +++ b/arch/risc-v/Kconfig @@ -96,6 +96,8 @@ config ARCH_CHIP_MPFS select ARCH_HAVE_FPU select ARCH_HAVE_DPFPU select ARCH_HAVE_MPU + select ARCH_HAVE_MMU + select ARCH_MMU_TYPE_SV39 select ARCH_HAVE_RESET select ARCH_HAVE_SPI_CS_CONTROL select ARCH_HAVE_PWM_MULTICHAN @@ -182,6 +184,10 @@ config ARCH_RISCV_INTXCPT_EXTREGS endif +config ARCH_MMU_TYPE_SV39 + bool + default n + source "arch/risc-v/src/opensbi/Kconfig" source "arch/risc-v/src/common/Kconfig" diff --git a/arch/risc-v/src/common/riscv_mmu.c b/arch/risc-v/src/common/riscv_mmu.c new file mode 100644 index 0000000000..d2d88ed18e --- /dev/null +++ b/arch/risc-v/src/common/riscv_mmu.c @@ -0,0 +1,107 @@ +/**************************************************************************** + * arch/risc-v/src/common/riscv_mmu.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 + +#include +#include +#include + +#include "riscv_internal.h" +#include "riscv_mmu.h" + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +/**************************************************************************** + * Private Functions + ****************************************************************************/ + +/**************************************************************************** + * Private Data + ****************************************************************************/ + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +void mmu_ln_setentry(uint32_t ptlevel, uintptr_t lnvaddr, uintptr_t paddr, + uintptr_t vaddr, uint32_t mmuflags) +{ + uintptr_t *lntable = (uintptr_t *)lnvaddr; + uint32_t index; + + DEBUGASSERT(ptlevel <= RV_MMU_PT_LEVELS); + + /* Test if this is a leaf PTE, if it is, set A+D even if they are not used + * by the implementation. + * + * If not, clear A+D+U because the spec. says: + * For non-leaf PTEs, the D, A, and U bits are reserved for future use and + * must be cleared by software for forward compatibility. + */ + + if (mmuflags & PTE_LEAF_MASK) + { + mmuflags |= (PTE_A | PTE_D); + } + else + { + mmuflags &= ~(PTE_A | PTE_D | PTE_U); + } + + /* Make sure the entry is valid */ + + mmuflags |= PTE_VALID; + + /* Calculate index for lntable */ + + index = (vaddr >> RV_MMU_VADDR_SHIFT(ptlevel)) & RV_MMU_VPN_MASK; + + /* Move PPN to correct position */ + + paddr >>= RV_MMU_PTE_PPN_SHIFT; + + /* Save it */ + + lntable[index] = (paddr | mmuflags); + + /* Update with memory by flushing the cache(s) */ + + mmu_invalidate_tlb_by_vaddr(vaddr); +} + +void mmu_ln_map_region(uint32_t ptlevel, uintptr_t lnvaddr, uintptr_t paddr, + uintptr_t vaddr, size_t size, uint32_t mmuflags) +{ + uintptr_t end_paddr = paddr + size; + + while (paddr < end_paddr) + { + mmu_ln_setentry(ptlevel, lnvaddr, paddr, vaddr, mmuflags); + paddr += RV_MMU_PAGE_SIZE; + vaddr += RV_MMU_PAGE_SIZE; + } +} diff --git a/arch/risc-v/src/common/riscv_mmu.h b/arch/risc-v/src/common/riscv_mmu.h new file mode 100644 index 0000000000..ec0b22875c --- /dev/null +++ b/arch/risc-v/src/common/riscv_mmu.h @@ -0,0 +1,206 @@ +/**************************************************************************** + * arch/risc-v/src/common/riscv_mmu.h + * + * 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. + * + ****************************************************************************/ + +#ifndef ___ARCH_RISC_V_SRC_COMMON_RISCV_MMU_H_ +#define ___ARCH_RISC_V_SRC_COMMON_RISCV_MMU_H_ + +/* RV32/64 page size */ + +#define RV_MMU_PAGE_SHIFT (12) +#define RV_MMU_PAGE_SIZE (1 << RV_MMU_PAGE_SHIFT) /* 4K pages */ + +/* Supervisor Address Translation and Protection (satp) */ + +#define SATP_PPN_SHIFT (0) +#define SATP_PPN_MASK (((1ul << 44) - 1) << SATP_PPN_SHIFT) +#define SATP_ASID_SHIFT (44) +#define SATP_ASID_MASK (((1ul << 16) - 1) << SATP_ASID_SHIFT) +#define SATP_MODE_SHIFT (60) +#define SATP_MODE_MASK (((1ul << 4) - 1) << SATP_MODE_SHIFT) + +/* Modes, for RV32 only 1 is valid, for RV64 1-7 and 10-15 are reserved */ + +#define SATP_MODE_BARE (0ul) +#define SATP_MODE_SV32 (1ul) +#define SATP_MODE_SV39 (8ul) +#define SATP_MODE_SV48 (9ul) + +/* satp address to PPN translation */ + +#define SATP_ADDR_TO_PPN(_addr) ((_addr) >> RV_MMU_PAGE_SHIFT) + +/* Common Page Table Entry (PTE) bits */ + +#define PTE_VALID (1 << 0) /* PTE is valid */ +#define PTE_R (1 << 1) /* Page is readable */ +#define PTE_W (1 << 2) /* Page is writable */ +#define PTE_X (1 << 3) /* Page is executable */ +#define PTE_U (1 << 4) /* Page is a user mode page */ +#define PTE_G (1 << 5) /* Page is a global mapping */ +#define PTE_A (1 << 6) /* Page has been accessed */ +#define PTE_D (1 << 7) /* Page is dirty */ + +/* Check if leaf PTE entry or not (if X/W/R are set it is) */ + +#define PTE_LEAF_MASK (7 << 1) + +/* SvX definitions, only Sv39 is currently supported, but it should be + * trivial to extend the driver to support other SvX implementations + * + * Sv39 has: + * - 4K page size + * - 3 page table levels + * - 9-bit VPN width + */ + +#ifdef CONFIG_ARCH_MMU_TYPE_SV39 +#define RV_MMU_PTE_PPN_SHIFT (2) +#define RV_MMU_VPN_WIDTH (9) +#define RV_MMU_VPN_MASK ((1 << RV_MMU_VPN_WIDTH) - 1) +#define RV_MMU_PT_LEVELS (3) +#define RV_MMU_VADDR_SHIFT(_n) (RV_MMU_PAGE_SHIFT + RV_MMU_VPN_WIDTH * \ + (RV_MMU_PT_LEVELS - (_n))) +#define RV_MMU_SATP_MODE SATP_MODE_SV39 +#else +#error "Unsupported RISC-V MMU implementation selected" +#endif /* CONFIG_ARCH_MMU_TYPE_SV39 */ + +/**************************************************************************** + * Name: mmu_enable + * + * Description: + * Enable MMU and set the base page table address + * + * Input Parameters: + * pgbase - The physical base address of the translation table base + * asid - Address space identifier. This can be used to identify different + * address spaces. It is not necessary to use this, nor is it necessary + * for the RISC-V implementation to implement such bits. This means in + * practice that the value should not be used in this generic driver. + * + ****************************************************************************/ + +static inline void mmu_enable(uintptr_t pgbase, uint16_t asid) +{ + uintptr_t reg; + reg = ((RV_MMU_SATP_MODE << SATP_MODE_SHIFT) & SATP_MODE_MASK); + reg |= (((uintptr_t)asid << SATP_ASID_SHIFT) & SATP_ASID_MASK); + reg |= ((SATP_ADDR_TO_PPN(pgbase) << SATP_PPN_SHIFT) & SATP_PPN_MASK); + + /* Commit to satp and synchronize */ + + __asm__ __volatile__ + ( + "csrw satp, %0\n" + "sfence.vma x0, x0\n" + "fence iorw, iorw\n" + : + : "rK" (reg) + : "memory" + ); +} + +/**************************************************************************** + * Name: mmu_invalidate_tlb_by_vaddr + * + * Description: + * Flush the TLB for vaddr entry + * + * Input Parameters: + * vaddr - The virtual address to flush + * + ****************************************************************************/ + +static inline void mmu_invalidate_tlb_by_vaddr(uintptr_t vaddr) +{ + __asm__ __volatile__ + ( + "sfence.vma x0, %0\n" + : + : "rK" (vaddr) + : "memory" + ); +} + +/**************************************************************************** + * Name: mmu_invalidate_tlbs + * + * Description: + * Flush the entire TLB + * + ****************************************************************************/ + +static inline void mmu_invalidate_tlbs(void) +{ + __asm__ __volatile__ + ( + "sfence.vma x0, x0\n" + : + : + : "memory" + ); +} + +/**************************************************************************** + * Name: mmu_ln_setentry + * + * Description: + * Set a level n translation table entry. + * + * Input Parameters: + * ptlevel - The translation table level, amount of levels is + * MMU implementation specific + * lnvaddr - The virtual address of the beginning of the page table at + * level n + * paddr - The physical address to be mapped. Must be aligned to a PPN + * address boundary which is dependent on the level of the entry + * vaddr - Must be aligned to a PPN + * address boundary which is dependent on the level of the entry + * mmuflags - The MMU flags to use in the mapping. + * + ****************************************************************************/ + +void mmu_ln_setentry(uint32_t ptlevel, uintptr_t lnvaddr, uintptr_t paddr, + uintptr_t vaddr, uint32_t mmuflags); + +/**************************************************************************** + * Name: mmu_ln_map_region + * + * Description: + * Set a translation table region for level n + * + * Input Parameters: + * ptlevel - The translation table level, amount of levels is + * MMU implementation specific + * lnvaddr - The virtual address of the beginning of the page table at + * level n + * paddr - The physical address to be mapped. Must be aligned to a PPN + * address boundary which is dependent on the level of the entry + * vaddr - Must be aligned to a PPN + * address boundary which is dependent on the level of the entry + * size - The size of the region in bytes + * mmuflags - The MMU flags to use in the mapping. + * + ****************************************************************************/ + +void mmu_ln_map_region(uint32_t ptlevel, uintptr_t lnvaddr, uintptr_t paddr, + uintptr_t vaddr, size_t size, uint32_t mmuflags); + +#endif /* ___ARCH_RISC_V_SRC_COMMON_RISCV_MMU_H_ */ diff --git a/arch/risc-v/src/mpfs/Make.defs b/arch/risc-v/src/mpfs/Make.defs index f20d1ae628..d9e5ea3623 100755 --- a/arch/risc-v/src/mpfs/Make.defs +++ b/arch/risc-v/src/mpfs/Make.defs @@ -60,13 +60,23 @@ CHIP_CSRCS += mpfs_dma.c endif ifeq ($(CONFIG_BUILD_PROTECTED),y) -CMN_CSRCS += riscv_task_start.c riscv_pthread_start.c -CMN_CSRCS += riscv_signal_dispatch.c riscv_pmp.c +CMN_CSRCS += riscv_task_start.c +CMN_CSRCS += riscv_pthread_start.c riscv_pthread_exit.c +CMN_CSRCS += riscv_signal_dispatch.c + CMN_UASRCS += riscv_signal_handler.S CHIP_CSRCS += mpfs_userspace.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_SPI),y) CHIP_CSRCS += mpfs_spi.c endif diff --git a/arch/risc-v/src/mpfs/mpfs_head.S b/arch/risc-v/src/mpfs/mpfs_head.S index 39d75808bb..5ab06fba07 100755 --- a/arch/risc-v/src/mpfs/mpfs_head.S +++ b/arch/risc-v/src/mpfs/mpfs_head.S @@ -108,6 +108,11 @@ __start: csrw mideleg, 0 csrw medeleg, 0 + /* Disable MMU if not done already */ + + csrw satp, zero + fence + /* invalid all MMU TLB Entry */ sfence.vma x0, x0