diff --git a/arch/arm/include/arch.h b/arch/arm/include/arch.h index 1d1661c0d9d..2f199162235 100644 --- a/arch/arm/include/arch.h +++ b/arch/arm/include/arch.h @@ -135,9 +135,7 @@ struct group_addrenv_s { FAR uintptr_t *text[ARCH_TEXT_NSECTS]; FAR uintptr_t *data[ARCH_DATA_NSECTS]; -#if 0 /* Not yet implemented */ FAR uintptr_t *heap[ARCH_HEAP_NSECTS]; -#endif }; typedef struct group_addrenv_s group_addrenv_t; diff --git a/arch/arm/src/a1x/Make.defs b/arch/arm/src/a1x/Make.defs index 92825cc1247..6c53ce0e84c 100644 --- a/arch/arm/src/a1x/Make.defs +++ b/arch/arm/src/a1x/Make.defs @@ -87,7 +87,7 @@ CMN_CSRCS += up_task_start.c up_pthread_start.c up_stackframe.c endif ifeq ($(CONFIG_ARCH_ADDRENV),y) -CMN_CSRCS += arm_addrenv.c +CMN_CSRCS += arm_addrenv.c arm_pgalloc.c endif ifeq ($(CONFIG_ELF),y) diff --git a/arch/arm/src/armv7-a/arm_addrenv.c b/arch/arm/src/armv7-a/arm_addrenv.c index de5df1473ab..fe765a18205 100644 --- a/arch/arm/src/armv7-a/arm_addrenv.c +++ b/arch/arm/src/armv7-a/arm_addrenv.c @@ -199,7 +199,7 @@ static int up_addrenv_create_region(FAR uintptr_t **list, } DEBUGASSERT(MM_ISALIGNED(paddr)); - list[i] = (FAR uint32_t *)paddr; + list[i] = (FAR uintptr_t *)paddr; /* Temporarily map the page into the virtual address space */ @@ -241,7 +241,7 @@ static int up_addrenv_create_region(FAR uintptr_t **list, (uintptr_t)l2table + ENTRIES_PER_L2TABLE * sizeof(uint32_t)); - /* Restore the original L1 page table entry */ + /* Restore the scratch section L1 page table entry */ mmu_l1_restore(ARCH_SCRATCH_VBASE, l1save); irqrestore(flags); @@ -300,7 +300,7 @@ static void up_addrenv_destroy_region(FAR uintptr_t **list, } } - /* Restore the original L1 page table entry */ + /* Restore the scratch section L1 page table entry */ mmu_l1_restore(ARCH_SCRATCH_VBASE, l1save); irqrestore(flags); diff --git a/arch/arm/src/armv7-a/arm_pgalloc.c b/arch/arm/src/armv7-a/arm_pgalloc.c new file mode 100644 index 00000000000..e55e92c25ab --- /dev/null +++ b/arch/arm/src/armv7-a/arm_pgalloc.c @@ -0,0 +1,280 @@ +/**************************************************************************** + * arch/arm/src/armv7/arm_pgalloc.c + * + * Copyright (C) 2014 Gregory Nutt. All rights reserved. + * Author: Gregory Nutt + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * 3. Neither the name NuttX nor the names of its contributors may be + * used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS + * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include + +#include +#include +#include + +#include +#include +#include + +#include "cache.h" +#include "mmu.h" + +#if defined(CONFIG_MM_PGALLOC) && defined(CONFIG_ARCH_USE_MMU) + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +/**************************************************************************** + * Private Data + ****************************************************************************/ + +/**************************************************************************** + * Private Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: alloc_pgtable + * + * Description: + * Add one page table to a memory region. + * + ****************************************************************************/ + +static uintptr_t alloc_pgtable(void) +{ + irqstate_t flags; + uintptr_t paddr; + FAR uint32_t *l2table; + uint32_t l1save; + + /* Allocate one physical page for the L2 page table */ + + paddr = mm_pgalloc(1); + if (paddr) + { + DEBUGASSERT(MM_ISALIGNED(paddr)); + + /* Temporarily map the page into the virtual address space */ + + flags = irqsave(); + l1save = mmu_l1_getentry(ARCH_SCRATCH_VBASE); + mmu_l1_setentry(paddr & ~SECTION_MASK, ARCH_SCRATCH_VBASE, MMU_MEMFLAGS); + l2table = (FAR uint32_t *)(ARCH_SCRATCH_VBASE | (paddr & SECTION_MASK)); + + /* Initialize the page table */ + + memset(l2table, 0, MM_PGSIZE); + + /* Make sure that the initialized L2 table is flushed to physical + * memory. + */ + + arch_flush_dcache((uintptr_t)l2table, + (uintptr_t)l2table + MM_PGSIZE); + + /* Restore the scratch section page table entry */ + + mmu_l1_restore(ARCH_SCRATCH_VBASE, l1save); + irqrestore(flags); + } + + return paddr; +} + +/**************************************************************************** + * Name: get_pgtable + * + * Description: + * Return the physical address of the L2 page table corresponding to + * 'vaddr' + * + ****************************************************************************/ + +static int get_pgtable(FAR group_addrenv_t *addrenv, uintptr_t vaddr) +{ + uint32_t l1entry; + uintptr_t paddr; + unsigned int hpoffset; + unsigned int hpndx; + + /* The current implementation only supports extending the user heap + * region as part of the implementation of user sbrk(). + */ + + DEBUGASSERT(vadddr >= CONFIG_ARCH_HEAP_VBASE && vaddr < ARCH_HEAP_VEND); + + /* Get the current level 1 entry corresponding to this vaddr */ + + hpoffset = vaddr - CONFIG_ARCH_HEAP_VBASE; + if (hpoffset >= ARCH_HEAP_SIZE) + { + return 0; + } + + hpndx = hpoffset >> 20; + l1entry = (uintptr_t)addrenv->heap[hpndx]; + if (l1entry == 0) + { + /* No page table has been allocated... allocate one now */ + + paddr = alloc_pgtable(); + if (paddr != 0) + { + /* Set the new level 1 page table entry in the address environment. */ + + l1entry = paddr | MMU_L1_PGTABFLAGS; + addrenv->heap[hpndx] = (FAR uintptr_t *)l1entry; + + /* And instantiate the modified environment */ + + (void)up_addrenv_select(addrenv, NULL); + } + } + + return l1entry & ~SECTION_MASK; +} + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: pgalloc + * + * Description: + * If there is a page allocator in the configuration and if and MMU is + * available to map physical addresses to virtual address, then function + * must be provided by the platform-specific code. This is part of the + * implementation of sbrk(). This function will allocate the requested + * number of pages using the page allocator and map them into consecutive + * virtual addresses beginning with 'brkaddr' + * + * NOTE: This function does not use the up_ naming standard because it + * is indirectly callable from user-space code via a system trap. + * Therefore, it is a system interface and follows a different naming + * convention. + * + ****************************************************************************/ + +int pgalloc(uintptr_t vaddr, unsigned int npages) +{ + FAR struct tcb_s *tcb = sched_self(); + FAR struct task_group_s *group; + FAR uint32_t *l2table; + irqstate_t flags; + uintptr_t paddr; + uint32_t l1save; + unsigned int index; + + DEBUGASSERT(tcb && tcb->group); + group = tcb->group; + + /* The current implementation only supports extending the user heap + * region as part of the implementation of user sbrk(). This function + * needs to be expanded to also handle (1) extending the user stack + * space and (2) extending the kernel memory regions as well. + */ + + DEBUGASSERT((group->flags & GROUP_FLAG_ADDRENV) != 0); + + /* vaddr = 0 means that no heap has yet been allocated */ + + if (vaddr == 0) + { + vaddr = CONFIG_ARCH_HEAP_VBASE; + } + + DEBUGASSERT(vadddr >= CONFIG_ARCH_HEAP_VBASE && vaddr < ARCH_HEAP_VEND); + DEBUGASSERT(MM_ISALIGNED(vaddr)); + + for (; npages > 0; npages--) + { + /* Get the physical address of the level 2 page table */ + + paddr = get_pgtable(&group->addrenv, vaddr); + if (paddr == 0) + { + return -ENOMEM; /* ENOMEM might have correct meaning for sbrk? */ + } + + /* Temporarily map the level 2 page table into the "scratch" virtual + * address space + */ + + flags = irqsave(); + l1save = mmu_l1_getentry(ARCH_SCRATCH_VBASE); + mmu_l1_setentry(paddr & ~SECTION_MASK, ARCH_SCRATCH_VBASE, MMU_MEMFLAGS); + l2table = (FAR uint32_t *)(ARCH_SCRATCH_VBASE | (paddr & SECTION_MASK)); + + /* Back up L2 entry with physical memory */ + + paddr = mm_pgalloc(1); + if (paddr == 0) + { + mmu_l1_restore(ARCH_SCRATCH_VBASE, l1save); + irqrestore(flags); + return -EAGAIN; /* ENOMEM has different meaning for sbrk */ + } + + /* The table divides a 1Mb address space up into 256 entries, each + * corresponding to 4Kb of address space. The page table index is + * related to the offset from the beginning of 1Mb region. + */ + + index = (vaddr & 0x000ff000) >> 12; + + /* Map the .text region virtual address to this physical address */ + + DEBUGASSERT(l2table[index] == 0); + l2table[index] = paddr | MMU_L2_UDATAFLAGS; + vaddr += MM_PGSIZE; + + /* Make sure that the modified L2 table is flushed to physical + * memory. + */ + + arch_flush_dcache((uintptr_t)&l2table[index], + (uintptr_t)&l2table[index] + sizeof(uint32_t)); + + /* Restore the scratch L1 page table entry */ + + mmu_l1_restore(ARCH_SCRATCH_VBASE, l1save); + irqrestore(flags); + } + + return OK; +} + +#endif /* CONFIG_MM_PGALLOC && CONFIG_ARCH_USE_MMU */ diff --git a/arch/arm/src/sama5/Make.defs b/arch/arm/src/sama5/Make.defs index e2ecb971625..6daa81b5ead 100644 --- a/arch/arm/src/sama5/Make.defs +++ b/arch/arm/src/sama5/Make.defs @@ -89,7 +89,7 @@ CMN_CSRCS += up_task_start.c up_pthread_start.c up_stackframe.c endif ifeq ($(CONFIG_ARCH_ADDRENV),y) -CMN_CSRCS += arm_addrenv.c +CMN_CSRCS += arm_addrenv.c arm_pgalloc.c endif ifeq ($(CONFIG_ELF),y)