/**************************************************************************** * arch/arm/src/armv7/arm_addrenv_shm.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 "mmu.h" #include "cache.h" #include "addrenv.h" #include "pgalloc.h" #if defined(CONFIG_BUILD_KERNEL) && defined(CONFIG_MM_SHM) /**************************************************************************** * Pre-processor Definitions ****************************************************************************/ /**************************************************************************** * Private Data ****************************************************************************/ /**************************************************************************** * Private Functions ****************************************************************************/ /**************************************************************************** * Public Functions ****************************************************************************/ /**************************************************************************** * Name: up_shmat * * Description: * Attach, i.e, map, on shared memory region to a user virtual address * * Input Parameters: * pages - A pointer to the first element in a array of physical address, * each corresponding to one page of memory. * npages - The number of pages in the list of physical pages to be mapped. * vaddr - The virtual address corresponding to the beginning of the * (contiguous) virtual address region. * * Returned Value: * Zero (OK) is returned on success; a negated errno value is returned * on failure. * ****************************************************************************/ int up_shmat(FAR uintptr_t *pages, unsigned int npages, uintptr_t vaddr) { FAR struct tcb_s *tcb = sched_self(); FAR struct task_group_s *group; FAR uintptr_t *l1entry; FAR uint32_t *l2table; irqstate_t flags; uintptr_t paddr; #ifndef CONFIG_ARCH_PGPOOL_MAPPING uint32_t l1save; #endif unsigned int nmapped; unsigned int shmndx; shmvdbg("pages=%p npages=%d vaddr=%08lx\n", pages, npages, (unsigned long)vaddr); /* Sanity checks */ DEBUGASSERT(pages && npages > 0 && tcb && tcb->group); DEBUGASSERT(vaddr >= CONFIG_ARCH_SHM_VBASE && vaddr < ARCH_SHM_VEND); DEBUGASSERT(MM_ISALIGNED(vaddr)); group = tcb->group; /* Loop until all pages have been mapped into the caller's address space. */ for (nmapped = 0; nmapped < npages; ) { /* Get the shm[] index associated with the virtual address */ shmndx = (vaddr - CONFIG_ARCH_SHM_VBASE) >> SECTION_SHIFT; /* Has a level 1 page table entry been created for this virtual address */ l1entry = group->tg_addrenv.shm[shmndx]; if (l1entry == NULL) { /* No.. Allocate one physical page for the L2 page table */ paddr = mm_pgalloc(1); if (!paddr) { return -ENOMEM; } DEBUGASSERT(MM_ISALIGNED(paddr)); /* We need to be more careful after we begin modifying * global resources. */ flags = irqsave(); group->tg_addrenv.shm[shmndx] = (FAR uintptr_t *)paddr; #ifdef CONFIG_ARCH_PGPOOL_MAPPING /* Get the virtual address corresponding to the physical page * address. */ l2table = (FAR uint32_t *)arm_pgvaddr(paddr); #else /* Temporarily map the page into the virtual address space */ 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)); #endif /* Initialize the page table */ memset(l2table, 0, ENTRIES_PER_L2TABLE * sizeof(uint32_t)); } else { /* Get the physical address of the L2 page table from the L1 page * table entry. */ paddr = (uintptr_t)l1entry & ~SECTION_MASK; flags = irqsave(); #ifdef CONFIG_ARCH_PGPOOL_MAPPING /* Get the virtual address corresponding to the physical page\ * address. */ l2table = (FAR uint32_t *)arm_pgvaddr(paddr); #else /* Temporarily map the page into the virtual address space */ 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)); #endif } /* Map the virtual address to this physical address */ DEBUGASSERT(get_l2_entry(l2table, vaddr) == 0); paddr = *pages++; set_l2_entry(l2table, paddr, vaddr, MMU_MEMFLAGS); nmapped++; vaddr += MM_PGSIZE; /* Make sure that the initialized L2 table is flushed to physical * memory. * * REVISIT: We could be smarter in doing this. Currently, we may * flush the entire L2 page table numerous times. */ arch_flush_dcache((uintptr_t)l2table, (uintptr_t)l2table + ENTRIES_PER_L2TABLE * sizeof(uint32_t)); #ifndef CONFIG_ARCH_PGPOOL_MAPPING /* Restore the scratch section L1 page table entry */ mmu_l1_restore(ARCH_SCRATCH_VBASE, l1save); #endif irqrestore(flags); } return OK; } /**************************************************************************** * Name: up_shmdt * * Description: * Detach, i.e, unmap, on shared memory region from a user virtual address * * Input Parameters: * vaddr - The virtual address corresponding to the beginning of the * (contiguous) virtual address region. * npages - The number of pages to be unmapped * * Returned Value: * Zero (OK) is returned on success; a negated errno value is returned * on failure. * ****************************************************************************/ int up_shmdt(uintptr_t vaddr, unsigned int npages) { FAR struct tcb_s *tcb = sched_self(); FAR struct task_group_s *group; FAR uintptr_t *l1entry; FAR uint32_t *l2table; irqstate_t flags; uintptr_t paddr; #ifndef CONFIG_ARCH_PGPOOL_MAPPING uint32_t l1save; #endif unsigned int nunmapped; unsigned int shmndx; shmvdbg("npages=%d vaddr=%08lx\n", npages, (unsigned long)vaddr); /* Sanity checks */ DEBUGASSERT(npages > 0 && tcb && tcb->group); DEBUGASSERT(vaddr >= CONFIG_ARCH_SHM_VBASE && vaddr < ARCH_SHM_VEND); DEBUGASSERT(MM_ISALIGNED(vaddr)); group = tcb->group; /* Loop until all pages have been unmapped from the caller's address * space. */ for (nunmapped = 0; nunmapped < npages; ) { /* Get the shm[] index associated with the virtual address */ shmndx = (vaddr - CONFIG_ARCH_SHM_VBASE) >> SECTION_SHIFT; /* Get the level 1 page table entry for this virtual address */ l1entry = group->tg_addrenv.shm[shmndx]; DEBUGASSERT(l1entry != NULL); /* Get the physical address of the L2 page table from the L1 page * table entry. */ paddr = (uintptr_t)l1entry & ~SECTION_MASK; flags = irqsave(); #ifdef CONFIG_ARCH_PGPOOL_MAPPING /* Get the virtual address corresponding to the physical page\ * address. */ l2table = (FAR uint32_t *)arm_pgvaddr(paddr); #else /* Temporarily map the page into the virtual address space */ 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)); #endif /* Unmap this virtual page address. * * REVISIT: Note that the page allocated for the level 2 page table * is not freed nor is the level 1 page table entry ever cleared. * This means that the 4KiB page is still allocated to the process * even though it may not contain any mappings and that the it will * persist until the process terminates. That is not all bad because * it means that we will be able to re-instantiate the shared memory * mapping very quickly. */ DEBUGASSERT(get_l2_entry(l2table, vaddr) != 0); clr_l2_entry(l2table, vaddr); nunmapped++; vaddr += MM_PGSIZE; /* Make sure that the modified L2 table is flushed to physical * memory. * * REVISIT: We could be smarter in doing this. Currently, we may * flush the entire L2 page table numerous times. */ arch_flush_dcache((uintptr_t)l2table, (uintptr_t)l2table + ENTRIES_PER_L2TABLE * sizeof(uint32_t)); #ifndef CONFIG_ARCH_PGPOOL_MAPPING /* Restore the scratch section L1 page table entry */ mmu_l1_restore(ARCH_SCRATCH_VBASE, l1save); #endif irqrestore(flags); } return OK; } #endif /* CONFIG_BUILD_KERNEL && CONFIG_MM_SHM */