diff --git a/arch/risc-v/src/common/riscv_addrenv.c b/arch/risc-v/src/common/riscv_addrenv.c index a890366106f..df981d1660b 100644 --- a/arch/risc-v/src/common/riscv_addrenv.c +++ b/arch/risc-v/src/common/riscv_addrenv.c @@ -397,9 +397,9 @@ int up_addrenv_create(size_t textsize, size_t datasize, size_t heapsize, goto errout; } - /* Map each region in turn FIXME: Remove W-flag after .elf is loaded */ + /* Map each region in turn */ - ret = create_region(addrenv, textbase, textsize, MMU_UTEXT_FLAGS | PTE_W); + ret = create_region(addrenv, textbase, textsize, MMU_UTEXT_FLAGS); if (ret < 0) { diff --git a/arch/risc-v/src/common/riscv_addrenv_perms.c b/arch/risc-v/src/common/riscv_addrenv_perms.c new file mode 100644 index 00000000000..a234b647a61 --- /dev/null +++ b/arch/risc-v/src/common/riscv_addrenv_perms.c @@ -0,0 +1,151 @@ +/**************************************************************************** + * arch/risc-v/src/common/riscv_addrenv_perms.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 +#include + +#include + +#include "pgalloc.h" +#include "riscv_mmu.h" + +/**************************************************************************** + * Private Functions + ****************************************************************************/ + +static int modify_region(uintptr_t vstart, uintptr_t vend, uintptr_t setmask, + uintptr_t clrmask) +{ + uintptr_t l1vaddr; + uintptr_t lnvaddr; + uintptr_t entry; + uintptr_t paddr; + uintptr_t vaddr; + uint32_t ptlevel; + + /* Must perform a reverse table walk */ + + l1vaddr = riscv_pgvaddr(mmu_get_satp_pgbase()); + + if (!l1vaddr) + { + /* Oops, there is no address environment to modify at all ? */ + + return -EINVAL; + } + + for (vaddr = vstart; vaddr < vend; vaddr += MM_PGSIZE) + { + for (ptlevel = 1, lnvaddr = l1vaddr; + ptlevel < RV_MMU_PT_LEVELS; + ptlevel++) + { + paddr = mmu_pte_to_paddr(mmu_ln_getentry(ptlevel, lnvaddr, vaddr)); + lnvaddr = riscv_pgvaddr(paddr); + if (!lnvaddr) + { + return -EINVAL; + } + } + + /* Get entry and modify the flags */ + + entry = mmu_ln_getentry(ptlevel, lnvaddr, vaddr); + entry &= ~clrmask; + entry |= setmask; + + /* Restore the entry */ + + mmu_ln_restore(ptlevel, lnvaddr, vaddr, entry); + } + + /* When all is set and done, flush the data caches */ + + __ISB(); + __DMB(); + + return OK; +} + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: up_addrenv_text_enable_write + * + * Description: + * Temporarily enable write access to the .text section. This must be + * called prior to loading the process code into memory. + * + * Input Parameters: + * addrenv - The address environment to be modified. + * + * Returned Value: + * Zero (OK) on success; a negated errno value on failure. + * + ****************************************************************************/ + +int up_addrenv_text_enable_write(group_addrenv_t *addrenv) +{ + /* Sanity checks */ + + DEBUGASSERT(addrenv); + DEBUGASSERT(MM_ISALIGNED(addrenv->textvbase)); + DEBUGASSERT(MM_ISALIGNED(addrenv->datavbase)); + + return modify_region(addrenv->textvbase, addrenv->datavbase, PTE_W, 0); +} + +/**************************************************************************** + * Name: up_addrenv_text_disable_write + * + * Description: + * Disable write access to the .text section. This must be called after the + * process code is loaded into memory. + * + * Input Parameters: + * addrenv - The address environment to be modified. + * + * Returned Value: + * Zero (OK) on success; a negated errno value on failure. + * + ****************************************************************************/ + +int up_addrenv_text_disable_write(group_addrenv_t *addrenv) +{ + /* Sanity checks */ + + DEBUGASSERT(addrenv); + DEBUGASSERT(MM_ISALIGNED(addrenv->textvbase)); + DEBUGASSERT(MM_ISALIGNED(addrenv->datavbase)); + + return modify_region(addrenv->textvbase, addrenv->datavbase, 0, PTE_W); +} diff --git a/arch/risc-v/src/common/riscv_mmu.c b/arch/risc-v/src/common/riscv_mmu.c index 20c8f75697b..de94f0358df 100644 --- a/arch/risc-v/src/common/riscv_mmu.c +++ b/arch/risc-v/src/common/riscv_mmu.c @@ -113,6 +113,23 @@ uintptr_t mmu_ln_getentry(uint32_t ptlevel, uintptr_t lnvaddr, return lntable[index]; } +void mmu_ln_restore(uint32_t ptlevel, uintptr_t lnvaddr, uintptr_t vaddr, + uintptr_t entry) +{ + uintptr_t *lntable = (uintptr_t *)lnvaddr; + uint32_t index; + + DEBUGASSERT(ptlevel > 0 && ptlevel <= RV_MMU_PT_LEVELS); + + index = (vaddr >> RV_MMU_VADDR_SHIFT(ptlevel)) & RV_MMU_VPN_MASK; + + lntable[index] = entry; + + /* 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) { diff --git a/arch/risc-v/src/common/riscv_mmu.h b/arch/risc-v/src/common/riscv_mmu.h index b02a1a72a8e..4eae21645f5 100644 --- a/arch/risc-v/src/common/riscv_mmu.h +++ b/arch/risc-v/src/common/riscv_mmu.h @@ -49,6 +49,7 @@ /* satp address to PPN translation */ #define SATP_ADDR_TO_PPN(_addr) ((_addr) >> RV_MMU_PAGE_SHIFT) +#define SATP_PPN_TO_ADDR(_ppn) ((_ppn) << RV_MMU_PAGE_SHIFT) /* Common Page Table Entry (PTE) bits */ @@ -270,6 +271,25 @@ static inline uintptr_t mmu_pte_to_paddr(uintptr_t pte) return paddr; } +/**************************************************************************** + * Name: mmu_get_satp_pgbase + * + * Description: + * Utility function to read the base page table physical address + * + * Returned Value: + * Physical address of the current base page table + * + ****************************************************************************/ + +static inline uintptr_t mmu_get_satp_pgbase(void) +{ + uintptr_t ppn; + ppn = mmu_read_satp(); + ppn = ((ppn >> SATP_PPN_SHIFT) & SATP_PPN_MASK); + return SATP_PPN_TO_ADDR(ppn); +} + /**************************************************************************** * Name: mmu_ln_setentry * @@ -311,6 +331,26 @@ void mmu_ln_setentry(uint32_t ptlevel, uintptr_t lnvaddr, uintptr_t paddr, uintptr_t mmu_ln_getentry(uint32_t ptlevel, uintptr_t lnvaddr, uintptr_t vaddr); +/**************************************************************************** + * Name: mmu_ln_restore + * + * Description: + * Restore 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 + * vaddr - The virtual address to get pte for. Must be aligned to a PPN + * address boundary which is dependent on the level of the entry + * entry - Entry to restore, previously obtained by mmu_ln_getentry + * + ****************************************************************************/ + +void mmu_ln_restore(uint32_t ptlevel, uintptr_t lnvaddr, uintptr_t vaddr, + uintptr_t entry); + /**************************************************************************** * Name: mmu_ln_map_region * diff --git a/arch/risc-v/src/mpfs/Make.defs b/arch/risc-v/src/mpfs/Make.defs index 2efe9299a55..77c55914a45 100755 --- a/arch/risc-v/src/mpfs/Make.defs +++ b/arch/risc-v/src/mpfs/Make.defs @@ -100,7 +100,7 @@ CMN_CSRCS += riscv_addrenv_kstack.c endif ifeq ($(CONFIG_ARCH_ADDRENV),y) -CMN_CSRCS += riscv_addrenv.c riscv_pgalloc.c +CMN_CSRCS += riscv_addrenv.c riscv_pgalloc.c riscv_addrenv_perms.c endif ifeq ($(CONFIG_MM_PGALLOC),y) diff --git a/binfmt/libelf/libelf.h b/binfmt/libelf/libelf.h index e5e4a5bba6c..d2f7b6607e0 100644 --- a/binfmt/libelf/libelf.h +++ b/binfmt/libelf/libelf.h @@ -290,7 +290,7 @@ int elf_addrenv_alloc(FAR struct elf_loadinfo_s *loadinfo, size_t textsize, ****************************************************************************/ #ifdef CONFIG_ARCH_ADDRENV -# define elf_addrenv_select(l) up_addrenv_select(&(l)->addrenv, &(l)->oldenv) +int elf_addrenv_select(FAR struct elf_loadinfo_s *loadinfo); #endif /**************************************************************************** @@ -308,7 +308,7 @@ int elf_addrenv_alloc(FAR struct elf_loadinfo_s *loadinfo, size_t textsize, ****************************************************************************/ #ifdef CONFIG_ARCH_ADDRENV -# define elf_addrenv_restore(l) up_addrenv_restore(&(l)->oldenv) +int elf_addrenv_restore(FAR struct elf_loadinfo_s *loadinfo); #endif /**************************************************************************** diff --git a/binfmt/libelf/libelf_addrenv.c b/binfmt/libelf/libelf_addrenv.c index 9ede25a383b..cece78388ee 100644 --- a/binfmt/libelf/libelf_addrenv.c +++ b/binfmt/libelf/libelf_addrenv.c @@ -145,6 +145,92 @@ int elf_addrenv_alloc(FAR struct elf_loadinfo_s *loadinfo, size_t textsize, #endif } +/**************************************************************************** + * Name: elf_addrenv_restore + * + * Description: + * Restore the address environment before elf_addrenv_select() was called.. + * + * Input Parameters: + * loadinfo - Load state information + * + * Returned Value: + * Zero (OK) on success; a negated errno value on failure. + * + ****************************************************************************/ + +#ifdef CONFIG_ARCH_ADDRENV +int elf_addrenv_select(FAR struct elf_loadinfo_s *loadinfo) +{ + int ret; + + /* Instantiate the new address environment */ + + ret = up_addrenv_select(&loadinfo->addrenv, &loadinfo->oldenv); + if (ret < 0) + { + berr("ERROR: up_addrenv_select failed: %d\n", ret); + return ret; + } + + /* Allow write access to .text */ + + ret = up_addrenv_text_enable_write(&loadinfo->addrenv); + if (ret < 0) + { + berr("ERROR: up_addrenv_text_enable_write failed: %d\n", ret); + return ret; + } + + return OK; +} +#endif + +/**************************************************************************** + * Name: elf_addrenv_free + * + * Description: + * Release the address environment previously created by + * elf_addrenv_alloc(). This function is called only under certain error + * conditions after the module has been loaded but not yet started. + * After the module has been started, the address environment will + * automatically be freed when the module exits. + * + * Input Parameters: + * loadinfo - Load state information + * + * Returned Value: + * None. + * + ****************************************************************************/ + +#ifdef CONFIG_ARCH_ADDRENV +int elf_addrenv_restore(FAR struct elf_loadinfo_s *loadinfo) +{ + int ret; + + /* Remove write access to .text */ + + ret = up_addrenv_text_disable_write(&loadinfo->addrenv); + if (ret < 0) + { + berr("ERROR: up_addrenv_text_disable_write failed: %d\n", ret); + return ret; + } + + /* Restore the old address environment */ + + ret = up_addrenv_restore(&loadinfo->oldenv); + if (ret < 0) + { + berr("ERROR: up_addrenv_restore failed: %d\n", ret); + return ret; + } + + return OK; +} +#endif + /**************************************************************************** * Name: elf_addrenv_free * diff --git a/include/nuttx/arch.h b/include/nuttx/arch.h index 30b78f971a6..7e64e434274 100644 --- a/include/nuttx/arch.h +++ b/include/nuttx/arch.h @@ -1185,6 +1185,44 @@ int up_addrenv_attach(FAR struct task_group_s *group, FAR struct tcb_s *tcb); int up_addrenv_detach(FAR struct task_group_s *group, FAR struct tcb_s *tcb); #endif +/**************************************************************************** + * Name: up_addrenv_text_enable_write + * + * Description: + * Temporarily enable write access to the .text section. This must be + * called prior to loading the process code into memory. + * + * Input Parameters: + * addrenv - The address environment to be modified. + * + * Returned Value: + * Zero (OK) on success; a negated errno value on failure. + * + ****************************************************************************/ + +#ifdef CONFIG_ARCH_ADDRENV +int up_addrenv_text_enable_write(FAR group_addrenv_t *addrenv); +#endif + +/**************************************************************************** + * Name: up_addrenv_text_disable_write + * + * Description: + * Disable write access to the .text section. This must be called after the + * process code is loaded into memory. + * + * Input Parameters: + * addrenv - The address environment to be modified. + * + * Returned Value: + * Zero (OK) on success; a negated errno value on failure. + * + ****************************************************************************/ + +#ifdef CONFIG_ARCH_ADDRENV +int up_addrenv_text_disable_write(FAR group_addrenv_t *addrenv); +#endif + /**************************************************************************** * Name: up_addrenv_ustackalloc *