arm64: Initial implementation of CONFIG_BUILD_KERNEL

This is the initial version for kernel mode build on the arm64 platform.
It works much in the same way as the risc-v implementation so any
highlights can be read from there.

Features that have been tested working:
- Creating address environments
- Loading init (nsh) from elf file
- Booting to nsh
- Starting other processes from nsh
- ostest runs to completion

Features that are not tested / do not work:
- SHM / shared memory support
- Kernel memory mapping (MM_KMAP)
- fork/vfork

An example qemu target is provided as a separate patch:
tools/configure.sh qemu-armv8a:knsh
This commit is contained in:
Ville Juven 2024-08-09 14:31:03 +03:00 committed by Alan Carvalho de Assis
parent 7f228b1554
commit 29f8648ecc
25 changed files with 3146 additions and 436 deletions

View file

@ -33,8 +33,7 @@
#ifndef __ASSEMBLY__
# include <stdint.h>
# include <nuttx/pgalloc.h>
# include <nuttx/addrenv.h>
# include <stddef.h>
#endif
/****************************************************************************
@ -46,6 +45,11 @@
# error Only pages sizes of 4096 are currently supported (CONFIG_ARCH_ADDRENV)
#endif
/* All implementations have 3 levels of page tables */
#define ARCH_PGT_MAX_LEVELS (3)
#define ARCH_SPGTS (ARCH_PGT_MAX_LEVELS - 1)
#endif /* CONFIG_ARCH_ADDRENV */
/****************************************************************************
@ -57,40 +61,46 @@
****************************************************************************/
#ifdef CONFIG_ARCH_ADDRENV
/* The task group resources are retained in a single structure, task_group_s
* that is defined in the header file nuttx/include/nuttx/sched.h. The type
* arch_addrenv_t must be defined by platform specific logic in
* nuttx/arch/<architecture>/include/arch.h.
#ifndef __ASSEMBLY__
/* A task group must have its L1 table in memory always, and the rest can
* be dynamically committed to memory (and even swapped).
*
* These tables would hold the physical address of the level 2 page tables.
* All would be initially NULL and would not be backed up with physical
* memory until mappings in the level 2 page table are required.
* In this implementation level tables except the final level N are always
* kept in static memory, while the level N tables are always dynamically
* allocated. There is one static page per level in `spgtables[]`.
*
* For the VMSAv8-64 address translation system this means that:
* - A task can not have more than 1GB of memory allocated. This should be
* plenty enough...
* - The minimum amount of memory needed for page tables per task is 12K,
* which gives access to 2MB of memory. This is plenty for many tasks.
*/
struct arch_addrenv_s
{
/* Level 1 page table entries for each group section */
uintptr_t *text[ARCH_TEXT_NSECTS];
uintptr_t *data[ARCH_DATA_NSECTS];
#ifdef CONFIG_BUILD_KERNEL
uintptr_t *heap[ARCH_HEAP_NSECTS];
#ifdef CONFIG_ARCH_VMA_MAPPING
uintptr_t *shm[ARCH_SHM_NSECTS];
#endif
/* Initial heap allocation (in bytes). This exists only provide an
* indirect path for passing the size of the initial heap to the heap
* initialization logic. These operations are separated in time and
* architecture. REVISIT: I would like a better way to do this.
/* Physical addresses of the static page tables (levels N-1) here, these
* are allocated when a task is created.
*/
size_t heapsize;
#endif
uintptr_t spgtables[ARCH_SPGTS];
/* The text, data, heap bases and heap size here */
uintptr_t textvbase;
uintptr_t datavbase;
uintptr_t heapvbase;
size_t heapsize;
/* The page directory root (ttbr0) value */
uintptr_t ttbr0;
};
typedef struct arch_addrenv_s arch_addrenv_t;
#endif
#endif /* __ASSEMBLY__ */
#endif /* CONFIG_ARCH_ADDRENV */
/****************************************************************************
* Public Data

View file

@ -247,6 +247,12 @@ extern "C"
EXTERN volatile uint64_t *g_current_regs[CONFIG_SMP_NCPUS];
#define CURRENT_REGS (g_current_regs[up_cpu_index()])
/****************************************************************************
* Public Types
****************************************************************************/
#ifndef __ASSEMBLY__
struct xcptcontext
{
/* The following function pointer is non-zero if there are pending signals
@ -308,11 +314,12 @@ struct xcptcontext
uintptr_t *ustkptr; /* Saved user stack pointer */
uintptr_t *kstack; /* Allocate base of the (aligned) kernel stack */
uintptr_t *kstkptr; /* Saved kernel stack pointer */
# endif
#endif
};
#endif /* __ASSEMBLY__ */
/* Name: up_irq_save, up_irq_restore, and friends.
*
* NOTE: This function should never be called from application code and,

View file

@ -52,7 +52,7 @@
*/
#ifndef CONFIG_BUILD_FLAT
# define CONFIG_SYS_RESERVED 8
# define CONFIG_SYS_RESERVED 6
#else
# define CONFIG_SYS_RESERVED 4
#endif
@ -80,32 +80,7 @@
#define SYS_switch_context (2)
#ifdef CONFIG_LIB_SYSCALL
/* SYS call 3:
*
* void arm_syscall_return(void);
*/
#define SYS_syscall_return (3)
#endif /* CONFIG_LIB_SYSCALL */
#ifndef CONFIG_BUILD_FLAT
/* SYS call 4:
*
* void up_task_start(main_t taskentry, int argc, char *argv[])
* noreturn_function;
*/
#define SYS_task_start (4)
/* SYS call 5:
*
* void up_pthread_start((pthread_startroutine_t startup,
* pthread_startroutine_t entrypt, pthread_addr_t arg)
* noreturn_function
*/
#define SYS_pthread_start (5)
/* SYS call 6:
*
@ -114,14 +89,14 @@
* void *ucontext);
*/
#define SYS_signal_handler (6)
#define SYS_signal_handler (4)
/* SYS call 7:
*
* void signal_handler_return(void);
*/
#define SYS_signal_handler_return (7)
#define SYS_signal_handler_return (5)
#endif /* !CONFIG_BUILD_FLAT */
#define ARM_SMCC_RES_A0 (0)

View file

@ -76,17 +76,17 @@ if(CONFIG_BUILD_KERNEL)
arm64_signal_dispatch.c)
endif()
if(CONFIG_ARCH_KERNEL_STACK)
list(APPEND SRCS arm64_addrenv_kstack.c)
endif()
if(CONFIG_ARCH_ADDRENV)
list(APPEND SRCS arm64_addrenv.c arm64_addrenv_utils.c arm64_pgalloc.c)
list(APPEND SRCS arm64_addrenv.c arm64_pgalloc.c arm64_addrenv_perms.c)
list(APPEND SRCS arm64_addrenv_utils.c arm64_addrenv_shm.c)
list(APPEND SRCS arm64_addrenv_pgmap.c)
if(CONFIG_ARCH_STACK_DYNAMIC)
list(APPEND SRCS arm64_addrenv_ustack.c)
endif()
if(CONFIG_ARCH_KERNEL_STACK)
list(APPEND SRCS arm64_addrenv_kstack.c)
endif()
if(CONFIG_ARCH_VMA_MAPPING)
list(APPEND SRCS arm64_addrenv_shm.c)
endif()
endif()
if(CONFIG_MM_PGALLOC)

View file

@ -88,23 +88,15 @@ ifeq ($(CONFIG_BUILD_KERNEL),y)
CMN_CSRCS += arm64_task_start.c arm64_pthread_start.c arm64_signal_dispatch.c
endif
ifeq ($(CONFIG_ARCH_ADDRENV),y)
CMN_CSRCS += arm64_addrenv.c arm64_addrenv_utils.c arm64_pgalloc.c
ifeq ($(CONFIG_ARCH_STACK_DYNAMIC),y)
CMN_CSRCS += arm64_addrenv_ustack.c
endif
ifeq ($(CONFIG_ARCH_KERNEL_STACK),y)
CMN_CSRCS += arm64_addrenv_kstack.c
endif
ifeq ($(CONFIG_ARCH_VMA_MAPPING),y)
CMN_CSRCS += arm64_addrenv_shm.c
endif
endif
ifeq ($(CONFIG_MM_PGALLOC),y)
CMN_CSRCS += arm64_physpgaddr.c
ifeq ($(CONFIG_ARCH_PGPOOL_MAPPING),y)
CMN_CSRCS += arm64_virtpgaddr.c
ifeq ($(CONFIG_ARCH_ADDRENV),y)
CMN_CSRCS += arm64_addrenv.c arm64_pgalloc.c arm64_addrenv_perms.c
CMN_CSRCS += arm64_addrenv_utils.c arm64_addrenv_shm.c arm64_addrenv_pgmap.c
ifeq ($(CONFIG_ARCH_STACK_DYNAMIC),y)
CMN_CSRCS += arm64_addrenv_ustack.c
endif
endif

View file

@ -41,62 +41,92 @@
/* Aligned size of the kernel stack */
#ifdef CONFIG_ARCH_KERNEL_STACK
# define ARCH_KERNEL_STACKSIZE STACK_ALIGN_UP(CONFIG_ARCH_KERNEL_STACKSIZE)
# define ARCH_KERNEL_STACKSIZE STACK_ALIGN_UP(CONFIG_ARCH_KERNEL_STACKSIZE)
#endif
/****************************************************************************
* Inline Functions
****************************************************************************/
/* Base address for address environment */
/****************************************************************************
* Public Data
****************************************************************************/
#ifndef __ASSEMBLY__
#ifdef __cplusplus
#define EXTERN extern "C"
extern "C"
{
#if CONFIG_ARCH_TEXT_VBASE != 0
# define ARCH_ADDRENV_VBASE (CONFIG_ARCH_TEXT_VBASE)
#else
#define EXTERN extern
# define ARCH_ADDRENV_VBASE (CONFIG_ARCH_DATA_VBASE)
#endif
/* Maximum user address environment size */
#define ARCH_ADDRENV_MAX_SIZE (0x40000000)
/* User address environment end */
#define ARCH_ADDRENV_VEND (ARCH_ADDRENV_VBASE + ARCH_ADDRENV_MAX_SIZE - 1)
/****************************************************************************
* Public Function Prototypes
****************************************************************************/
/****************************************************************************
* Name: arm_addrenv_create_region
* Name: arm64_get_pgtable
*
* Description:
* Create one memory region.
* Get the physical address of the final level page table corresponding to
* 'vaddr'. If one does not exist, it will be allocated.
*
* Input Parameters:
* addrenv - Pointer to a structure describing the address environment
* vaddr - Virtual address to query for
*
* Returned Value:
* On success, the number of pages allocated is returned. Otherwise, a
* negated errno value is returned.
* The physical address of the corresponding final level page table, or
* NULL if one does not exist, and there is no free memory to allocate one
*
****************************************************************************/
int arm64_addrenv_create_region(uintptr_t **list, size_t listlen,
uintptr_t vaddr, size_t regionsize,
uint32_t mmuflags);
uintptr_t arm64_get_pgtable(arch_addrenv_t *addrenv, uintptr_t vaddr);
/****************************************************************************
* Name: arm_addrenv_destroy_region
* Name: arm64_map_pages
*
* Description:
* Destroy one memory region.
* Map physical pages into a continuous virtual memory block.
*
* Input Parameters:
* addrenv - Pointer to a structure describing the address environment.
* 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
* (continuous) virtual address region.
* prot - MMU flags to use.
*
* Returned Value:
* Zero (OK) is returned on success; a negated errno value is returned
* on failure.
*
****************************************************************************/
void arm64_addrenv_destroy_region(uintptr_t **list, size_t listlen,
uintptr_t vaddr, bool keep);
int arm64_map_pages(arch_addrenv_t *addrenv, uintptr_t *pages,
unsigned int npages, uintptr_t vaddr, uint64_t prot);
#undef EXTERN
#ifdef __cplusplus
}
#endif
#endif /* __ASSEMBLY__ */
/****************************************************************************
* Name: arm64_unmap_pages
*
* Description:
* Unmap a previously mapped virtual memory region.
*
* Input Parameters:
* addrenv - Pointer to a structure describing the address environment.
* vaddr - The virtual address corresponding to the beginning of the
* (continuous) 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 arm64_unmap_pages(arch_addrenv_t *addrenv, uintptr_t vaddr,
unsigned int npages);
#endif /* CONFIG_ARCH_ADDRENV */
#endif /* __ARCH_ARM64_SRC_COMMON_ADDRENV_H */

View file

@ -0,0 +1,818 @@
/****************************************************************************
* arch/arm64/src/common/arm64_addrenv.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.
*
****************************************************************************/
/****************************************************************************
* Address Environment Interfaces
*
* Low-level interfaces used in binfmt/ to instantiate tasks with address
* environments. These interfaces all operate on type arch_addrenv_t which
* is an abstract representation of a task group's address environment and
* must be defined in arch/arch.h if CONFIG_ARCH_ADDRENV is defined.
*
* up_addrenv_create - Create an address environment
* up_addrenv_destroy - Destroy an address environment.
* up_addrenv_vtext - Returns the virtual base address of the .text
* address environment
* up_addrenv_vdata - Returns the virtual base address of the .bss/.data
* address environment
* up_addrenv_heapsize - Returns the size of the initial heap allocation.
* up_addrenv_select - Instantiate an address environment
* up_addrenv_clone - Copy an address environment from one location to
* another.
*
* Higher-level interfaces used by the tasking logic. These interfaces are
* used by the functions in sched/ and all operate on the thread which whose
* group been assigned an address environment by up_addrenv_clone().
*
* up_addrenv_attach - Clone the address environment assigned to one TCB
* to another. This operation is done when a pthread
* is created that share's the same address
* environment.
* up_addrenv_detach - Release the threads reference to an address
* environment when a task/thread exits.
*
****************************************************************************/
/****************************************************************************
* Included Files
****************************************************************************/
#include <nuttx/config.h>
#include <errno.h>
#include <assert.h>
#include <debug.h>
#include <nuttx/addrenv.h>
#include <nuttx/arch.h>
#include <nuttx/compiler.h>
#include <nuttx/irq.h>
#include <nuttx/pgalloc.h>
#include "addrenv.h"
#include "barriers.h"
#include "pgalloc.h"
#include "arm64_mmu.h"
/****************************************************************************
* Pre-processor Definitions
****************************************************************************/
/* Only CONFIG_BUILD_KERNEL is supported (i.e. tested) */
#ifndef CONFIG_BUILD_KERNEL
# error "This module is intended to be used with CONFIG_BUILD_KERNEL"
#endif
/* Entries per PGT */
#define ENTRIES_PER_PGT (MMU_PAGE_ENTRIES)
/* Make sure the address environment virtual address boundary is valid */
static_assert((ARCH_ADDRENV_VBASE & MMU_L2_PAGE_SIZE) == 0,
"Addrenv start address is not aligned to section boundary");
/****************************************************************************
* Public Data
****************************************************************************/
extern uintptr_t g_kernel_mappings;
/****************************************************************************
* Private Functions
****************************************************************************/
/****************************************************************************
* Name: map_spgtables
*
* Description:
* Map vaddr to the static page tables.
*
* Input Parameters:
* addrenv - Describes the address environment
*
****************************************************************************/
static void map_spgtables(arch_addrenv_t *addrenv, uintptr_t vaddr)
{
int i;
uintptr_t prev;
/* Start from L1, and connect until max level - 1 */
prev = arm64_pgvaddr(addrenv->spgtables[0]);
/* Check if the mapping already exists */
if (mmu_ln_getentry(1, prev, vaddr) != 0)
{
return;
}
/* No mapping yet, create it */
for (i = 0; i < (ARCH_SPGTS - 1); i++)
{
uintptr_t next = addrenv->spgtables[i + 1];
mmu_ln_setentry(i + 1, prev, next, vaddr, MMU_UPGT_FLAGS);
prev = arm64_pgvaddr(next);
}
}
/****************************************************************************
* Name: create_spgtables
*
* Description:
* Create the static page tables. Allocate memory for them and connect them
* together.
*
* Input Parameters:
* addrenv - Describes the address environment
*
* Returned value:
* Amount of pages created on success; a negated errno value on failure
*
****************************************************************************/
static int create_spgtables(arch_addrenv_t *addrenv)
{
int i;
uintptr_t paddr;
for (i = 0; i < ARCH_SPGTS; i++)
{
paddr = mm_pgalloc(1);
if (!paddr)
{
return -ENOMEM;
}
/* Wipe the memory and assign it */
arm64_pgwipe(paddr);
addrenv->spgtables[i] = paddr;
}
/* Synchronize data and instruction pipelines */
ARM64_DSB();
ARM64_ISB();
return i;
}
/****************************************************************************
* Name: copy_kernel_mappings
*
* Description:
* Copy kernel mappings to address environment. Expects that the user page
* table does not contain any mappings yet (as they will be wiped).
*
* Input Parameters:
* addrenv - Describes the address environment. The page tables must exist
* at this point.
*
* Returned value:
* OK on success, ERROR on failure
*
****************************************************************************/
static int copy_kernel_mappings(arch_addrenv_t *addrenv)
{
uintptr_t user_mappings = arm64_pgvaddr(addrenv->spgtables[0]);
/* Copy the L1 references */
if (user_mappings == 0)
{
return -EINVAL;
}
memcpy((void *)user_mappings, (void *)g_kernel_mappings, MMU_PAGE_SIZE);
return OK;
}
/****************************************************************************
* Name: create_region
*
* Description:
* Map a single region of memory to MMU. Assumes that the static page
* tables exist. Allocates the final level page tables and commits the
* region memory to physical memory.
*
* Input Parameters:
* addrenv - Describes the address environment
* vaddr - Base virtual address for the mapping
* size - Size of the region in bytes
* mmuflags - MMU flags to use
*
* Returned value:
* Amount of pages created on success; a negated errno value on failure
*
****************************************************************************/
static int create_region(arch_addrenv_t *addrenv, uintptr_t vaddr,
size_t size, uint64_t mmuflags)
{
uintptr_t ptlast;
uintptr_t ptprev;
uintptr_t paddr;
uint32_t ptlevel;
int npages;
int nmapped;
int i;
int j;
nmapped = 0;
npages = MM_NPAGES(size);
ptprev = arm64_pgvaddr(addrenv->spgtables[ARCH_SPGTS - 1]);
ptlevel = ARCH_SPGTS;
/* Create mappings for the lower level tables */
map_spgtables(addrenv, vaddr);
/* Begin allocating memory for the page tables */
for (i = 0; i < npages; i += ENTRIES_PER_PGT)
{
/* Get the current final level entry corresponding to this vaddr */
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 */
arm64_pgwipe(paddr);
}
ptlast = arm64_pgvaddr(paddr);
/* Then allocate memory for the region data */
for (j = 0;
#ifdef CONFIG_PAGING
j < 1;
#else
j < ENTRIES_PER_PGT && nmapped < size;
#endif
j++)
{
paddr = mm_pgalloc(1);
if (!paddr)
{
return -ENOMEM;
}
/* Wipe the physical page memory */
arm64_pgwipe(paddr);
/* Then map the virtual address to the physical address */
mmu_ln_setentry(ptlevel + 1, ptlast, paddr, vaddr, mmuflags);
nmapped += MM_PGSIZE;
vaddr += MM_PGSIZE;
}
}
/* Synchronize data and instruction pipelines */
ARM64_DSB();
ARM64_ISB();
return npages;
}
/****************************************************************************
* Name: vaddr_is_shm
*
* Description:
* Check if a vaddr is part of the SHM area
*
* Input Parameters:
* vaddr - Virtual address to check
*
* Returned value:
* true if it is; false if not
*
****************************************************************************/
static inline bool vaddr_is_shm(uintptr_t vaddr)
{
#if defined (CONFIG_ARCH_SHM_VBASE) && defined(ARCH_SHM_VEND)
return vaddr >= CONFIG_ARCH_SHM_VBASE && vaddr < ARCH_SHM_VEND;
#else
UNUSED(vaddr);
return false;
#endif
}
/****************************************************************************
* Public Functions
****************************************************************************/
/****************************************************************************
* Name: up_addrenv_create
*
* Description:
* This function is called when a new task is created in order to
* instantiate an address environment for the new task group.
* up_addrenv_create() is essentially the allocator of the physical
* memory for the new task.
*
* Input Parameters:
* textsize - The size (in bytes) of the .text address environment needed
* by the task. This region may be read/execute only.
* datasize - The size (in bytes) of the .data/.bss address environment
* needed by the task. This region may be read/write only. NOTE: The
* actual size of the data region that is allocated will include a
* OS private reserved region at the beginning. The size of the
* private, reserved region is give by ARCH_DATA_RESERVE_SIZE.
* heapsize - The initial size (in bytes) of the heap address environment
* needed by the task. This region may be read/write only.
* addrenv - The location to return the representation of the task address
* environment.
*
* Returned Value:
* Zero (OK) on success; a negated errno value on failure.
*
****************************************************************************/
int up_addrenv_create(size_t textsize, size_t datasize, size_t heapsize,
arch_addrenv_t *addrenv)
{
int ret;
uintptr_t resvbase;
uintptr_t resvsize;
uintptr_t textbase;
uintptr_t database;
uintptr_t heapbase;
DEBUGASSERT(addrenv);
DEBUGASSERT(MM_ISALIGNED(ARCH_ADDRENV_VBASE));
/* Make sure the address environment is wiped before doing anything */
memset(addrenv, 0, sizeof(arch_addrenv_t));
/* Create the static page tables */
ret = create_spgtables(addrenv);
if (ret < 0)
{
serr("ERROR: Failed to create static page tables\n");
goto errout;
}
/* Map the kernel memory for the user */
ret = copy_kernel_mappings(addrenv);
if (ret < 0)
{
serr("ERROR: Failed to copy kernel mappings to new environment");
goto errout;
}
/* Calculate the base addresses for convenience */
#if (CONFIG_ARCH_TEXT_VBASE != 0x0) && (CONFIG_ARCH_HEAP_VBASE != 0x0)
resvbase = CONFIG_ARCH_DATA_VBASE;
resvsize = ARCH_DATA_RESERVE_SIZE;
textbase = CONFIG_ARCH_TEXT_VBASE;
database = resvbase + MM_PGALIGNUP(resvsize);
heapbase = CONFIG_ARCH_HEAP_VBASE;
#else
resvbase = ARCH_ADDRENV_VBASE;
resvsize = ARCH_DATA_RESERVE_SIZE;
textbase = resvbase + MM_PGALIGNUP(resvsize);
database = textbase + MM_PGALIGNUP(textsize);
heapbase = database + MM_PGALIGNUP(datasize);
#endif
/* Allocate 1 extra page for heap, temporary fix for #5811 */
heapsize = heapsize + MM_PGALIGNUP(4*CONFIG_DEFAULT_TASK_STACKSIZE);
/* Map the reserved area */
ret = create_region(addrenv, resvbase, resvsize, MMU_UDATA_FLAGS);
if (ret < 0)
{
berr("ERROR: Failed to create reserved region: %d\n", ret);
goto errout;
}
/* Map each region in turn */
ret = create_region(addrenv, textbase, textsize, MMU_UTEXT_FLAGS);
if (ret < 0)
{
berr("ERROR: Failed to create .text region: %d\n", ret);
goto errout;
}
ret = create_region(addrenv, database, datasize, MMU_UDATA_FLAGS);
if (ret < 0)
{
berr("ERROR: Failed to create .bss/.data region: %d\n", ret);
goto errout;
}
ret = create_region(addrenv, heapbase, heapsize, MMU_UDATA_FLAGS);
if (ret < 0)
{
berr("ERROR: Failed to create heap region: %d\n", ret);
goto errout;
}
/* Save the heap base and initial size allocated. These will be needed when
* the heap data structures are initialized.
*/
addrenv->heapvbase = heapbase;
addrenv->heapsize = (size_t)ret << MM_PGSHIFT;
/* Save the text base */
addrenv->textvbase = textbase;
/* Save the data base */
addrenv->datavbase = database;
/* Provide the ttbr0 value for context switch */
addrenv->ttbr0 = mmu_ttbr_reg(addrenv->spgtables[0], 0);
/* Synchronize data and instruction pipelines */
ARM64_DSB();
ARM64_ISB();
return OK;
errout:
up_addrenv_destroy(addrenv);
return ret;
}
/****************************************************************************
* Name: up_addrenv_destroy
*
* Description:
* This function is called when a final thread leaves the task group and
* the task group is destroyed. This function then destroys the defunct
* address environment, releasing the underlying physical memory.
*
* Input Parameters:
* addrenv - The address environment to be destroyed.
*
* Returned Value:
* Zero (OK) on success; a negated errno value on failure.
*
****************************************************************************/
int up_addrenv_destroy(arch_addrenv_t *addrenv)
{
/* Recursively destroy it all, need to table walk */
uintptr_t *ptprev;
uintptr_t *ptlast;
uintptr_t paddr;
uintptr_t vaddr;
size_t pgsize;
int i;
int j;
DEBUGASSERT(addrenv);
/* Things start from the beginning of the user virtual memory */
vaddr = ARCH_ADDRENV_VBASE;
pgsize = mmu_get_region_size(ARCH_SPGTS);
/* First destroy the allocated memory and the final level page table */
ptprev = (uintptr_t *)arm64_pgvaddr(addrenv->spgtables[ARCH_SPGTS - 1]);
if (ptprev)
{
/* walk user space only */
i = (ARCH_SPGTS < 2) ? vaddr / pgsize : 0;
for (; i < ENTRIES_PER_PGT; i++, vaddr += pgsize)
{
ptlast = (uintptr_t *)arm64_pgvaddr(mmu_pte_to_paddr(ptprev[i]));
if (ptlast)
{
if (!vaddr_is_shm(vaddr))
{
/* Free the allocated pages, but not from SHM area */
for (j = 0; j < ENTRIES_PER_PGT; j++)
{
paddr = mmu_pte_to_paddr(ptlast[j]);
if (paddr)
{
mm_pgfree(paddr, 1);
}
}
}
/* Regardless, free the page table itself */
mm_pgfree((uintptr_t)ptlast, 1);
}
}
}
/* Then destroy the static tables */
for (i = 0; i < ARCH_SPGTS; i++)
{
paddr = addrenv->spgtables[i];
if (paddr)
{
mm_pgfree(paddr, 1);
}
}
/* Synchronize data and instruction pipelines */
ARM64_DSB();
ARM64_ISB();
memset(addrenv, 0, sizeof(arch_addrenv_t));
return OK;
}
/****************************************************************************
* Name: up_addrenv_vtext
*
* Description:
* Return the virtual address associated with the newly create .text
* address environment. This function is used by the binary loaders in
* order get an address that can be used to initialize the new task.
*
* Input Parameters:
* addrenv - The representation of the task address environment previously
* returned by up_addrenv_create.
* vtext - The location to return the virtual address.
*
* Returned Value:
* Zero (OK) on success; a negated errno value on failure.
*
****************************************************************************/
int up_addrenv_vtext(arch_addrenv_t *addrenv, void **vtext)
{
DEBUGASSERT(addrenv && vtext);
*vtext = (void *)addrenv->textvbase;
return OK;
}
/****************************************************************************
* Name: up_addrenv_vdata
*
* Description:
* Return the virtual address associated with the newly create .text
* address environment. This function is used by the binary loaders in
* order get an address that can be used to initialize the new task.
*
* Input Parameters:
* addrenv - The representation of the task address environment previously
* returned by up_addrenv_create.
* textsize - For some implementations, the text and data will be saved
* in the same memory region (read/write/execute) and, in this case,
* the virtual address of the data just lies at this offset into the
* common region.
* vdata - The location to return the virtual address.
*
* Returned Value:
* Zero (OK) on success; a negated errno value on failure.
*
****************************************************************************/
int up_addrenv_vdata(arch_addrenv_t *addrenv, uintptr_t textsize,
void **vdata)
{
DEBUGASSERT(addrenv && vdata);
*vdata = (void *)addrenv->datavbase;
return OK;
}
/****************************************************************************
* Name: up_addrenv_vheap
*
* Description:
* Return the heap virtual address associated with the newly created
* address environment. This function is used by the binary loaders in
* order get an address that can be used to initialize the new task.
*
* Input Parameters:
* addrenv - The representation of the task address environment previously
* returned by up_addrenv_create.
* vheap - The location to return the virtual address.
*
* Returned Value:
* Zero (OK) on success; a negated errno value on failure.
*
****************************************************************************/
#ifdef CONFIG_BUILD_KERNEL
int up_addrenv_vheap(const arch_addrenv_t *addrenv, void **vheap)
{
DEBUGASSERT(addrenv && vheap);
*vheap = (void *)addrenv->heapvbase;
return OK;
}
#endif
/****************************************************************************
* Name: up_addrenv_heapsize
*
* Description:
* Return the initial heap allocation size. That is the amount of memory
* allocated by up_addrenv_create() when the heap memory region was first
* created. This may or may not differ from the heapsize parameter that
* was passed to up_addrenv_create()
*
* Input Parameters:
* addrenv - The representation of the task address environment previously
* returned by up_addrenv_create.
*
* Returned Value:
* The initial heap size allocated is returned on success; a negated
* errno value on failure.
*
****************************************************************************/
#ifdef CONFIG_BUILD_KERNEL
ssize_t up_addrenv_heapsize(const arch_addrenv_t *addrenv)
{
DEBUGASSERT(addrenv);
return (ssize_t)addrenv->heapsize;
}
#endif
/****************************************************************************
* Name: up_addrenv_select
*
* Description:
* After an address environment has been established for a task (via
* up_addrenv_create()), this function may be called to instantiate
* that address environment in the virtual address space. This might be
* necessary, for example, to load the code for the task from a file or
* to access address environment private data.
*
* Input Parameters:
* addrenv - The representation of the task address environment previously
* returned by up_addrenv_create.
*
* Returned Value:
* Zero (OK) on success; a negated errno value on failure.
*
****************************************************************************/
int up_addrenv_select(const arch_addrenv_t *addrenv)
{
DEBUGASSERT(addrenv && addrenv->ttbr0);
mmu_write_ttbr0(addrenv->ttbr0);
return OK;
}
/****************************************************************************
* Name: up_addrenv_coherent
*
* Description:
* Flush D-Cache and invalidate I-Cache in preparation for a change in
* address environments. This should immediately precede a call to
* up_addrenv_select();
*
* Input Parameters:
* addrenv - Describes the address environment to be made coherent.
*
* Returned Value:
* Zero (OK) on success; a negated errno value on failure.
*
****************************************************************************/
int up_addrenv_coherent(const arch_addrenv_t *addrenv)
{
/* Nothing needs to be done */
return OK;
}
/****************************************************************************
* Name: up_addrenv_clone
*
* Description:
* Duplicate an address environment. This does not copy the underlying
* memory, only the representation that can be used to instantiate that
* memory as an address environment.
*
* Input Parameters:
* src - The address environment to be copied.
* dest - The location to receive the copied address environment.
*
* Returned Value:
* Zero (OK) on success; a negated errno value on failure.
*
****************************************************************************/
int up_addrenv_clone(const arch_addrenv_t *src,
arch_addrenv_t *dest)
{
DEBUGASSERT(src && dest);
memcpy(dest, src, sizeof(arch_addrenv_t));
return OK;
}
/****************************************************************************
* Name: up_addrenv_attach
*
* Description:
* This function is called from the core scheduler logic when a thread
* is created that needs to share the address environment of its task
* group.
*
* Input Parameters:
* ptcb - The tcb of the parent task.
* tcb - The tcb of the thread needing the address environment.
*
* Returned Value:
* Zero (OK) on success; a negated errno value on failure.
*
****************************************************************************/
int up_addrenv_attach(struct tcb_s *ptcb, struct tcb_s *tcb)
{
/* There is nothing that needs to be done */
return OK;
}
/****************************************************************************
* Name: up_addrenv_detach
*
* Description:
* This function is called when a task or thread exits in order to release
* its reference to an address environment. The address environment,
* however, should persist until up_addrenv_destroy() is called when the
* task group is itself destroyed. Any resources unique to this thread
* may be destroyed now.
*
* Input Parameters:
* tcb - The TCB of the task or thread whose the address environment will
* be released.
*
* Returned Value:
* Zero (OK) on success; a negated errno value on failure.
*
****************************************************************************/
int up_addrenv_detach(struct tcb_s *tcb)
{
/* There is nothing that needs to be done */
return OK;
}

View file

@ -0,0 +1,110 @@
/****************************************************************************
* arch/arm64/src/common/arm64_addrenv_kstack.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 <nuttx/config.h>
#include <errno.h>
#include <assert.h>
#include <debug.h>
#include <nuttx/sched.h>
#include <nuttx/kmalloc.h>
#include <nuttx/addrenv.h>
#include <nuttx/arch.h>
#include "addrenv.h"
#include "arm64_internal.h"
#if defined(CONFIG_ARCH_ADDRENV) && defined(CONFIG_ARCH_KERNEL_STACK)
/****************************************************************************
* Public Functions
****************************************************************************/
/****************************************************************************
* Name: up_addrenv_kstackalloc
*
* Description:
* This function is called when a new thread is created to allocate
* the new thread's kernel stack. This function may be called for certain
* terminating threads which have no kernel stack. It must be tolerant of
* that case.
*
* Input Parameters:
* tcb - The TCB of the thread that requires the kernel stack.
*
* Returned Value:
* Zero (OK) on success; a negated errno value on failure.
*
****************************************************************************/
int up_addrenv_kstackalloc(struct tcb_s *tcb)
{
DEBUGASSERT(tcb && tcb->xcp.kstack == NULL);
/* Allocate the kernel stack */
tcb->xcp.kstack = kmm_memalign(STACK_ALIGNMENT, ARCH_KERNEL_STACKSIZE);
if (!tcb->xcp.kstack)
{
berr("ERROR: Failed to allocate the kernel stack\n");
return -ENOMEM;
}
return OK;
}
/****************************************************************************
* Name: up_addrenv_kstackfree
*
* Description:
* This function is called when any thread exits. This function frees
* the kernel stack.
*
* Input Parameters:
* tcb - The TCB of the thread that no longer requires the kernel stack.
*
* Returned Value:
* Zero (OK) on success; a negated errno value on failure.
*
****************************************************************************/
int up_addrenv_kstackfree(struct tcb_s *tcb)
{
DEBUGASSERT(tcb);
/* Does the exiting thread have a kernel stack? */
if (tcb->xcp.kstack)
{
/* Yes.. Free the kernel stack */
kmm_free(tcb->xcp.kstack);
tcb->xcp.kstack = NULL;
}
return OK;
}
#endif /* CONFIG_ARCH_ADDRENV && CONFIG_ARCH_KERNEL_STACK */

View file

@ -0,0 +1,148 @@
/****************************************************************************
* arch/arm64/src/common/arm64_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 <nuttx/config.h>
#include <errno.h>
#include <assert.h>
#include <nuttx/addrenv.h>
#include <nuttx/arch.h>
#include <nuttx/pgalloc.h>
#include <sys/mman.h>
#include "barriers.h"
#include "pgalloc.h"
#include "arm64_arch.h"
#include "arm64_mmu.h"
/****************************************************************************
* Pre-processor Definitions
****************************************************************************/
#define CLR_MASK (PTE_BLOCK_DESC_AP_MASK | PTE_BLOCK_DESC_UXN)
/****************************************************************************
* Private Functions
****************************************************************************/
static int modify_region(uintptr_t vstart, uintptr_t vend, uintptr_t setmask)
{
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 = arm64_pgvaddr(mmu_get_ttbr0_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 < MMU_PGT_LEVELS;
ptlevel++)
{
paddr = mmu_pte_to_paddr(mmu_ln_getentry(ptlevel, lnvaddr, vaddr));
lnvaddr = arm64_pgvaddr(paddr);
if (!lnvaddr)
{
return -EINVAL;
}
}
/* Get entry and modify the flags */
entry = mmu_ln_getentry(ptlevel, lnvaddr, vaddr);
entry &= ~CLR_MASK;
entry |= setmask | PTE_BLOCK_DESC_AP_USER;
/* Restore the entry */
mmu_ln_restore(ptlevel, lnvaddr, vaddr, entry);
}
return OK;
}
/****************************************************************************
* Public Functions
****************************************************************************/
/****************************************************************************
* Name: up_addrenv_mprot
*
* Description:
* Modify access rights to an address range.
*
* Input Parameters:
* addrenv - The address environment to be modified.
* addr - Base address of the region.
* len - Size of the region.
* prot - Access right flags.
*
* Returned Value:
* Zero (OK) on success; a negated errno value on failure.
*
****************************************************************************/
int up_addrenv_mprot(arch_addrenv_t *addrenv, uintptr_t addr, size_t len,
int prot)
{
uintptr_t setmask;
/* addrenv not needed by this implementation */
UNUSED(addrenv);
/* The access is either RO or RW, read access cannot be revoked */
if ((prot & PROT_WRITE) == 0)
{
setmask = PTE_BLOCK_DESC_AP_RO;
}
else
{
setmask = PTE_BLOCK_DESC_AP_RW;
}
/* Execute access is explicitly revoked, implicitly granted */
if ((prot & PROT_EXEC) == 0)
{
setmask |= PTE_BLOCK_DESC_UXN;
}
return modify_region(addr, addr + MM_PGALIGNUP(len), setmask);
}

View file

@ -0,0 +1,312 @@
/****************************************************************************
* arch/arm64/src/common/arm64_addrenv_pgmap.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 <nuttx/config.h>
#include <nuttx/arch.h>
#include <nuttx/addrenv.h>
#include <nuttx/irq.h>
#include <nuttx/pgalloc.h>
#include <nuttx/sched.h>
#include <sys/mman.h>
#include "barriers.h"
#include "pgalloc.h"
#include "arm64_mmu.h"
#ifdef CONFIG_BUILD_KERNEL
/****************************************************************************
* Private Data
****************************************************************************/
#ifdef CONFIG_MM_KMAP
static struct arch_addrenv_s g_kernel_addrenv;
#endif
/****************************************************************************
* Public Functions
****************************************************************************/
/****************************************************************************
* Name: up_addrenv_find_page
*
* Description:
* Find physical page mapped to user virtual address from the address
* environment page directory.
*
* Input Parameters:
* addrenv - The user address environment.
* vaddr - The user virtual address
*
* Returned Value:
* Page physical address on success; NULL on failure.
*
****************************************************************************/
uintptr_t up_addrenv_find_page(arch_addrenv_t *addrenv, uintptr_t vaddr)
{
uintptr_t pgdir;
uintptr_t lnvaddr;
uintptr_t paddr;
uint32_t ptlevel;
/* If vaddr is not user space, get out */
if (!arm64_uservaddr(vaddr))
{
return 0;
}
/* Get the kernel addressable virtual address of the page directory root */
pgdir = arm64_pgvaddr(mmu_ttbr_to_paddr(addrenv->ttbr0));
if (!pgdir)
{
return 0;
}
/* Make table walk to find the page */
for (ptlevel = 1, lnvaddr = pgdir; ptlevel < MMU_PGT_LEVELS; ptlevel++)
{
paddr = mmu_pte_to_paddr(mmu_ln_getentry(ptlevel, lnvaddr, vaddr));
lnvaddr = arm64_pgvaddr(paddr);
if (!lnvaddr)
{
return 0;
}
}
/* Got it, now convert it to physical page */
paddr = mmu_ln_getentry(ptlevel, lnvaddr, vaddr);
paddr = mmu_pte_to_paddr(paddr);
return paddr;
}
/****************************************************************************
* Name: up_addrenv_page_vaddr
*
* Description:
* Find the kernel virtual address associated with physical page.
*
* Input Parameters:
* page - The page physical address.
*
* Returned Value:
* Page kernel virtual address on success; NULL on failure.
*
****************************************************************************/
uintptr_t up_addrenv_page_vaddr(uintptr_t page)
{
return arm64_pgvaddr(page);
}
/****************************************************************************
* Name: up_addrenv_user_vaddr
*
* Description:
* Check if a virtual address is in user virtual address space.
*
* Input Parameters:
* vaddr - The virtual address.
*
* Returned Value:
* True if it is; false if it's not
*
****************************************************************************/
bool up_addrenv_user_vaddr(uintptr_t vaddr)
{
return arm64_uservaddr(vaddr);
}
/****************************************************************************
* Name: up_addrenv_page_wipe
*
* Description:
* Wipe a page of physical memory, first mapping it into kernel virtual
* memory.
*
* Input Parameters:
* page - The page physical address.
*
* Returned Value:
* None.
*
****************************************************************************/
void up_addrenv_page_wipe(uintptr_t page)
{
arm64_pgwipe(page);
}
#ifdef CONFIG_MM_KMAP
/****************************************************************************
* Name: up_addrenv_kmap_init
*
* Description:
* Initialize the architecture specific part of the kernel mapping
* interface.
*
* Input Parameters:
* None.
*
* Returned Value:
* Zero (OK) is returned on success; a negated errno value is returned
* on failure.
*
****************************************************************************/
int up_addrenv_kmap_init(void)
{
struct arch_addrenv_s *addrenv;
uintptr_t next;
uintptr_t vaddr;
int i;
/* Populate the static page tables one by one */
addrenv = &g_kernel_addrenv;
next = g_kernel_pgt_pbase;
vaddr = CONFIG_ARCH_KMAP_VBASE;
for (i = 0; i < ARCH_SPGTS; i++)
{
/* Connect the static page tables */
uintptr_t lnvaddr = arm64_pgvaddr(next);
addrenv->spgtables[i] = next;
next = mmu_pte_to_paddr(mmu_ln_getentry(i + 1, lnvaddr, vaddr));
}
/* Set the page directory root */
addrenv->ttbr0 = mmu_ttbr_reg(g_kernel_pgt_pbase, 0);
/* When all is set and done, flush the data caches */
ARM64_DSB();
return OK;
}
/****************************************************************************
* Name: up_addrenv_kmap_pages
*
* Description:
* Map physical pages into a continuous virtual memory block.
*
* 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
* (continuous) virtual address region.
* prot - Access right flags.
*
* Returned Value:
* Zero (OK) is returned on success; a negated errno value is returned
* on failure.
*
****************************************************************************/
int up_addrenv_kmap_pages(void **pages, unsigned int npages, uintptr_t vaddr,
int prot)
{
struct arch_addrenv_s *addrenv = &g_kernel_addrenv;
int mask = 0;
/* Sanity checks */
DEBUGASSERT(pages != NULL && npages > 0);
DEBUGASSERT(vaddr >= CONFIG_ARCH_KMAP_VBASE && vaddr < ARCH_KMAP_VEND);
DEBUGASSERT(MM_ISALIGNED(vaddr));
/* The access is either RO or RW, read access cannot be revoked */
if ((prot & PROT_WRITE) == 0)
{
mask = PTE_BLOCK_DESC_AP_RO;
}
else
{
mask = PTE_BLOCK_DESC_AP_RW;
}
/* Execute access is explicitly revoked, implicitly granted */
if ((prot & PROT_EXEC) == 0)
{
mask |= PTE_BLOCK_DESC_PXN;
}
/* This is a kernel (global) mapping */
mask &= ~PTE_BLOCK_DESC_NG;
/* Let arm64_map_pages do the work */
return arm64_map_pages(addrenv, (uintptr_t *)pages, npages, vaddr, mask);
}
/****************************************************************************
* Name: up_addrenv_kunmap_pages
*
* Description:
* Unmap a previously mapped virtual memory region.
*
* Input Parameters:
* vaddr - The virtual address corresponding to the beginning of the
* (continuous) 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_addrenv_kunmap_pages(uintptr_t vaddr, unsigned int npages)
{
struct arch_addrenv_s *addrenv = &g_kernel_addrenv;
/* Sanity checks */
DEBUGASSERT(npages > 0);
DEBUGASSERT(vaddr >= CONFIG_ARCH_KMAP_VBASE && vaddr < ARCH_KMAP_VEND);
DEBUGASSERT(MM_ISALIGNED(vaddr));
/* Let arm64_unmap_pages do the work */
return arm64_unmap_pages(addrenv, vaddr, npages);
}
#endif /* CONFIG_MM_KMAP */
#endif /* CONFIG_BUILD_KERNEL */

View file

@ -0,0 +1,117 @@
/****************************************************************************
* arch/arm64/src/common/arm64_addrenv_shm.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 <nuttx/config.h>
#include <errno.h>
#include <assert.h>
#include <debug.h>
#include <nuttx/addrenv.h>
#include <nuttx/arch.h>
#include <nuttx/irq.h>
#include <nuttx/pgalloc.h>
#include "addrenv.h"
#include "barriers.h"
#include "pgalloc.h"
#include "arm64_mmu.h"
#if defined(CONFIG_BUILD_KERNEL) && defined(CONFIG_ARCH_VMA_MAPPING)
/****************************************************************************
* 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(uintptr_t *pages, unsigned int npages, uintptr_t vaddr)
{
struct tcb_s *tcb = nxsched_self();
struct arch_addrenv_s *addrenv = &tcb->addrenv_own->addrenv;
/* Sanity checks */
DEBUGASSERT(tcb && tcb->addrenv_own);
DEBUGASSERT(pages != NULL && npages > 0);
DEBUGASSERT(vaddr >= CONFIG_ARCH_SHM_VBASE && vaddr < ARCH_SHM_VEND);
DEBUGASSERT(MM_ISALIGNED(vaddr));
/* Let arm64_map_pages do the work */
return arm64_map_pages(addrenv, pages, npages, vaddr, MMU_UDATA_FLAGS);
}
/****************************************************************************
* 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)
{
struct tcb_s *tcb = nxsched_self();
struct arch_addrenv_s *addrenv = &tcb->addrenv_own->addrenv;
/* Sanity checks */
DEBUGASSERT(tcb && tcb->addrenv_own);
DEBUGASSERT(npages > 0);
DEBUGASSERT(vaddr >= CONFIG_ARCH_SHM_VBASE && vaddr < ARCH_SHM_VEND);
DEBUGASSERT(MM_ISALIGNED(vaddr));
/* Let arm64_unmap_pages do the work */
return arm64_unmap_pages(addrenv, vaddr, npages);
}
#endif /* CONFIG_BUILD_KERNEL */

View file

@ -0,0 +1,219 @@
/****************************************************************************
* arch/arm64/src/common/arm64_addrenv_utils.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 <nuttx/config.h>
#include <nuttx/arch.h>
#include <nuttx/addrenv.h>
#include <nuttx/irq.h>
#include <nuttx/pgalloc.h>
#include <nuttx/sched.h>
#include "barriers.h"
#include "pgalloc.h"
#include "arm64_mmu.h"
#ifdef CONFIG_BUILD_KERNEL
/****************************************************************************
* Public Functions
****************************************************************************/
/****************************************************************************
* Name: arm64_get_pgtable
*
* Description:
* Get the physical address of the final level page table corresponding to
* 'vaddr'. If one does not exist, it will be allocated.
*
* Input Parameters:
* addrenv - Pointer to a structure describing the address environment
* vaddr - Virtual address to query for
*
* Returned Value:
* The physical address of the corresponding final level page table, or
* NULL if one does not exist, and there is no free memory to allocate one
*
****************************************************************************/
uintptr_t arm64_get_pgtable(arch_addrenv_t *addrenv, uintptr_t vaddr)
{
uintptr_t paddr;
uintptr_t ptprev;
uint32_t ptlevel;
uint32_t flags;
/* Get the current level MAX_LEVELS-1 entry corresponding to this vaddr */
ptlevel = ARCH_SPGTS;
ptprev = arm64_pgvaddr(addrenv->spgtables[ARCH_SPGTS - 1]);
if (!ptprev)
{
/* Something is very wrong */
return 0;
}
/* Find the physical address of the final level page table */
paddr = mmu_pte_to_paddr(mmu_ln_getentry(ptlevel, ptprev, vaddr));
if (!paddr)
{
/* No page table has been allocated... allocate one now */
paddr = mm_pgalloc(1);
if (paddr)
{
/* Determine page table flags */
if (arm64_uservaddr(vaddr))
{
flags = MMU_UPGT_FLAGS;
}
else
{
flags = MMU_KPGT_FLAGS;
}
/* Wipe the page and assign it */
arm64_pgwipe(paddr);
mmu_ln_setentry(ptlevel, ptprev, paddr, vaddr, flags);
}
}
return paddr;
}
/****************************************************************************
* Name: arm64_map_pages
*
* Description:
* Map physical pages into a continuous virtual memory block.
*
* Input Parameters:
* addrenv - Pointer to a structure describing the address environment.
* 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
* (continuous) virtual address region.
* prot - MMU flags to use.
*
* Returned Value:
* Zero (OK) is returned on success; a negated errno value is returned
* on failure.
*
****************************************************************************/
int arm64_map_pages(arch_addrenv_t *addrenv, uintptr_t *pages,
unsigned int npages, uintptr_t vaddr, uint64_t prot)
{
uintptr_t ptlast;
uintptr_t ptlevel;
uintptr_t paddr;
ptlevel = MMU_PGT_LEVELS;
/* Add the references to pages[] into the caller's address environment */
for (; npages > 0; npages--)
{
/* Get the address of the last level page table */
ptlast = arm64_pgvaddr(arm64_get_pgtable(addrenv, vaddr));
if (!ptlast)
{
return -ENOMEM;
}
/* Then add the reference */
paddr = *pages++;
mmu_ln_setentry(ptlevel, ptlast, paddr, vaddr, prot);
vaddr += MM_PGSIZE;
}
return OK;
}
/****************************************************************************
* Name: arm64_unmap_pages
*
* Description:
* Unmap a previously mapped virtual memory region.
*
* Input Parameters:
* addrenv - Pointer to a structure describing the address environment.
* vaddr - The virtual address corresponding to the beginning of the
* (continuous) 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 arm64_unmap_pages(arch_addrenv_t *addrenv, uintptr_t vaddr,
unsigned int npages)
{
uintptr_t ptlast;
uintptr_t ptprev;
uintptr_t ptlevel;
uintptr_t paddr;
ptprev = arm64_pgvaddr(addrenv->spgtables[ARCH_SPGTS - 1]);
if (!ptprev)
{
/* Something is very wrong */
return -EFAULT;
}
ptlevel = ARCH_SPGTS;
/* Remove the references from the caller's address environment */
for (; npages > 0; npages--)
{
/* Get the current final level entry corresponding to this vaddr */
paddr = mmu_pte_to_paddr(mmu_ln_getentry(ptlevel, ptprev, vaddr));
ptlast = arm64_pgvaddr(paddr);
if (!ptlast)
{
return -EFAULT;
}
/* Then wipe the reference */
mmu_ln_clear(ptlevel + 1, ptlast, vaddr);
vaddr += MM_PGSIZE;
}
return OK;
}
#endif /* CONFIG_BUILD_KERNEL */

View file

@ -238,8 +238,12 @@ pid_t arm64_fork(const struct fork_s *context)
pforkctx->elr = (uint64_t)context->lr;
pforkctx->exe_depth = 0;
pforkctx->sp_elx = (uint64_t)pforkctx;
pforkctx->sp_elx = (uint64_t)stack_ptr;
#ifdef CONFIG_ARCH_KERNEL_STACK
pforkctx->sp_el0 = (uint64_t)child->cmn.xcp.ustkptr;
#else
pforkctx->sp_el0 = (uint64_t)pforkctx;
#endif
pforkctx->tpidr_el0 = (uint64_t)(&child->cmn);
pforkctx->tpidr_el1 = (uint64_t)(&child->cmn);

View file

@ -41,6 +41,7 @@
#include <nuttx/power/pm.h>
#include <arch/chip/chip.h>
#include "addrenv.h"
#include "arm64_arch.h"
#include "arm64_internal.h"
#include "chip.h"
@ -59,6 +60,16 @@ void arm64_new_task(struct tcb_s * tcb)
uint64_t stack_ptr = (uintptr_t)tcb->stack_base_ptr + tcb->adj_stack_size;
struct regs_context *pinitctx;
#ifdef CONFIG_ARCH_KERNEL_STACK
/* Use the process kernel stack to store context for user processes */
if (tcb->xcp.kstack)
{
tcb->xcp.ustkptr = (uintptr_t *)stack_ptr;
stack_ptr = (uintptr_t)tcb->xcp.kstack + ARCH_KERNEL_STACKSIZE;
}
#endif
#ifdef CONFIG_ARCH_FPU
struct fpu_reg *pfpuctx;
pfpuctx = STACK_PTR_TO_FRAME(struct fpu_reg, stack_ptr);
@ -87,7 +98,11 @@ void arm64_new_task(struct tcb_s * tcb)
#endif /* CONFIG_SUPPRESS_INTERRUPTS */
pinitctx->sp_elx = (uint64_t)stack_ptr;
#ifdef CONFIG_ARCH_KERNEL_STACK
pinitctx->sp_el0 = (uint64_t)tcb->xcp.ustkptr;
#else
pinitctx->sp_el0 = (uint64_t)pinitctx;
#endif
pinitctx->exe_depth = 0;
pinitctx->tpidr_el0 = (uint64_t)tcb;
pinitctx->tpidr_el1 = (uint64_t)tcb;
@ -112,9 +127,16 @@ void arm64_new_task(struct tcb_s * tcb)
void up_initial_state(struct tcb_s *tcb)
{
struct xcptcontext *xcp = &tcb->xcp;
#ifdef CONFIG_ARCH_KERNEL_STACK
uint64_t *kstack = xcp->kstack;
#endif
memset(xcp, 0, sizeof(struct xcptcontext));
#ifdef CONFIG_ARCH_KERNEL_STACK
xcp->kstack = kstack;
#endif
if (tcb->pid == IDLE_PROCESS_ID)
{
/* Initialize the idle thread stack */

View file

@ -1,4 +1,4 @@
/***************************************************************************
/****************************************************************************
* arch/arm64/src/common/arm64_mmu.c
*
* Licensed to the Apache Software Foundation (ASF) under one or more
@ -16,11 +16,11 @@
* License for the specific language governing permissions and limitations
* under the License.
*
***************************************************************************/
****************************************************************************/
/***************************************************************************
/****************************************************************************
* Included Files
***************************************************************************/
****************************************************************************/
#include <nuttx/config.h>
#include <stdint.h>
@ -36,9 +36,9 @@
#include "arm64_fatal.h"
#include "arm64_mmu.h"
/***************************************************************************
/****************************************************************************
* Pre-processor Definitions
***************************************************************************/
****************************************************************************/
/* MMU debug option
* #define CONFIG_MMU_ASSERT 1
@ -75,14 +75,14 @@
/* We support only 4kB translation granule */
#define PAGE_SIZE_SHIFT 12U
#define PAGE_SIZE_SHIFT MMU_PAGE_SHIFT
#define PAGE_SIZE (1U << PAGE_SIZE_SHIFT)
#define XLAT_TABLE_SIZE_SHIFT PAGE_SIZE_SHIFT /* Size of one
* complete table */
#define XLAT_TABLE_SIZE (1U << XLAT_TABLE_SIZE_SHIFT)
#define XLAT_TABLE_ENTRY_SIZE_SHIFT 3U /* Each table entry is 8 bytes */
#define XLAT_TABLE_LEVEL_MAX 3U
#define XLAT_TABLE_LEVEL_MAX MMU_PGT_LEVELS
#define XLAT_TABLE_ENTRIES_SHIFT \
(XLAT_TABLE_SIZE_SHIFT - XLAT_TABLE_ENTRY_SIZE_SHIFT)
@ -145,9 +145,9 @@
#define TCR_PS_BITS TCR_PS_BITS_4GB
#endif
/***************************************************************************
/****************************************************************************
* Private Data
***************************************************************************/
****************************************************************************/
static uint64_t base_xlat_table[NUM_BASE_LEVEL_ENTRIES] aligned_data(
NUM_BASE_LEVEL_ENTRIES * sizeof(uint64_t));
@ -182,6 +182,13 @@ static const struct arm_mmu_region g_mmu_nxrt_regions[] =
(uint64_t)_sdata,
(uint64_t)_szdata,
MT_NORMAL | MT_RW | MT_SECURE),
#ifdef CONFIG_BUILD_KERNEL
MMU_REGION_FLAT_ENTRY("nx_pgpool",
(uint64_t)CONFIG_ARCH_PGPOOL_PBASE,
(uint64_t)CONFIG_ARCH_PGPOOL_SIZE,
MT_NORMAL | MT_RW | MT_SECURE),
#endif
};
static const struct arm_mmu_config g_mmu_nxrt_config =
@ -190,9 +197,23 @@ static const struct arm_mmu_config g_mmu_nxrt_config =
.mmu_regions = g_mmu_nxrt_regions,
};
/***************************************************************************
static const size_t g_pgt_sizes[] =
{
MMU_L1_PAGE_SIZE,
MMU_L2_PAGE_SIZE,
MMU_L3_PAGE_SIZE
};
/****************************************************************************
* Public Data
****************************************************************************/
uintptr_t g_kernel_mappings = (uintptr_t)&base_xlat_table;
uintptr_t g_kernel_pgt_pbase = (uintptr_t)&base_xlat_table;
/****************************************************************************
* Private Functions
***************************************************************************/
****************************************************************************/
/* Translation table control register settings */
@ -591,9 +612,9 @@ static void enable_mmu_el1(unsigned int flags)
}
#endif
/***************************************************************************
/****************************************************************************
* Public Functions
***************************************************************************/
****************************************************************************/
int arm64_mmu_set_memregion(const struct arm_mmu_region *region)
{
@ -673,3 +694,80 @@ int arm64_mmu_init(bool is_primary_core)
return 0;
}
void mmu_ln_setentry(uint32_t ptlevel, uintptr_t lnvaddr, uintptr_t paddr,
uintptr_t vaddr, uint64_t mmuflags)
{
uintptr_t *lntable = (uintptr_t *)lnvaddr;
uint32_t index;
DEBUGASSERT(ptlevel > 0 && ptlevel <= XLAT_TABLE_LEVEL_MAX);
/* Calculate index for lntable */
index = XLAT_TABLE_VA_IDX(vaddr, ptlevel);
/* Setup the page descriptor and access flag */
mmuflags |= PTE_PAGE_DESC | PTE_BLOCK_DESC_AF;
/* Save it */
lntable[index] = (paddr | mmuflags);
/* Update with memory by flushing the cache */
up_flush_dcache((uintptr_t)&lntable[index],
(uintptr_t)&lntable[index] + sizeof(uintptr_t));
/* Remove TLB entry for the modified page */
mmu_invalidate_tlb_by_vaddr(vaddr);
}
uintptr_t mmu_ln_getentry(uint32_t ptlevel, uintptr_t lnvaddr,
uintptr_t vaddr)
{
uintptr_t *lntable = (uintptr_t *)lnvaddr;
uint32_t index;
DEBUGASSERT(ptlevel > 0 && ptlevel <= XLAT_TABLE_LEVEL_MAX);
index = XLAT_TABLE_VA_IDX(vaddr, ptlevel);
/* Invalidate D-Cache so that we read from the physical memory */
up_invalidate_dcache((uintptr_t)&lntable[index],
(uintptr_t)&lntable[index] + sizeof(uintptr_t));
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 <= XLAT_TABLE_LEVEL_MAX);
index = XLAT_TABLE_VA_IDX(vaddr, ptlevel);
lntable[index] = entry;
/* Update with memory by flushing the cache */
up_flush_dcache((uintptr_t)&lntable[index],
(uintptr_t)&lntable[index] + sizeof(uintptr_t));
/* Remove TLB entry for the modified page */
mmu_invalidate_tlb_by_vaddr(vaddr);
}
size_t mmu_get_region_size(uint32_t ptlevel)
{
DEBUGASSERT(ptlevel > 0 && ptlevel <= XLAT_TABLE_LEVEL_MAX);
return g_pgt_sizes[ptlevel - 1];
}

View file

@ -40,6 +40,7 @@
*
* See Arm® Architecture Reference Manual, ARM DDI 0487E.a, B2.7.2
*/
#define MT_TYPE_MASK 0x7U
#define MT_TYPE(attr) ((attr) & MT_TYPE_MASK)
#define MT_DEVICE_NGNRNE 0U
@ -97,16 +98,25 @@
/* Block and Page descriptor attributes fields */
#define PTE_BLOCK_DESC_MEMTYPE(x) ((x) << 2)
#define PTE_BLOCK_DESC_NS (1ULL << 5)
#define PTE_BLOCK_DESC_AP_RO (1ULL << 7)
#define PTE_BLOCK_DESC_AP_RW (0ULL << 7)
#define PTE_BLOCK_DESC_NS (1ULL << 5) /* Non-secure */
#define PTE_BLOCK_DESC_AP_USER (1ULL << 6) /* User */
#define PTE_BLOCK_DESC_AP_RO (1ULL << 7) /* Read-only */
#define PTE_BLOCK_DESC_AP_RW (0ULL << 7) /* Read-write */
#define PTE_BLOCK_DESC_AP_MASK (3ULL << 6)
#define PTE_BLOCK_DESC_NON_SHARE (0ULL << 8)
#define PTE_BLOCK_DESC_OUTER_SHARE (2ULL << 8)
#define PTE_BLOCK_DESC_INNER_SHARE (3ULL << 8)
#define PTE_BLOCK_DESC_AF (1ULL << 10)
#define PTE_BLOCK_DESC_NG (1ULL << 11)
#define PTE_BLOCK_DESC_PXN (1ULL << 53)
#define PTE_BLOCK_DESC_UXN (1ULL << 54)
#define PTE_BLOCK_DESC_AF (1ULL << 10) /* A-flag */
#define PTE_BLOCK_DESC_NG (1ULL << 11) /* Non-global */
#define PTE_BLOCK_DESC_DIRTY (1ULL << 51) /* D-flag */
#define PTE_BLOCK_DESC_PXN (1ULL << 53) /* Kernel execute never */
#define PTE_BLOCK_DESC_UXN (1ULL << 54) /* User execute never */
/* PTE address field */
#define PTE_PADDR_SHIFT (12U)
#define PTE_PADDR_WIDTH (36U)
#define PTE_PADDR_MASK (((1UL << PTE_PADDR_WIDTH) - 1) << PTE_PADDR_SHIFT)
/* TCR definitions.
*
@ -119,7 +129,7 @@
#define TCR_EL3_PS_SHIFT 16U
#define TCR_T0SZ_SHIFT 0U
#define TCR_T0SZ(x) ((64 - (x)) << TCR_T0SZ_SHIFT)
#define TCR_T0SZ(x) ((64 - (x)) << TCR_T0SZ_SHIFT)
#define TCR_IRGN_NC (0ULL << 8)
#define TCR_IRGN_WBWA (1ULL << 8)
@ -167,6 +177,15 @@
#define CCSIDR_EL1_SETS_SHIFT 13
#define CCSIDR_EL1_SETS_MASK BIT_MASK(15)
/* ttbr0/1_el1 */
#define TTBR_BADDR_SHIFT (0)
#define TTBR_BADDR_WIDTH (48)
#define TTBR_BADDR_MASK (((1UL << TTBR_BADDR_WIDTH) - 1) << TTBR_BADDR_SHIFT)
#define TTBR_ASID_SHIFT (48)
#define TTBR_ASID_WIDTH (16)
#define TTBR_ASID_MASK (((1UL << TTBR_ASID_WIDTH) - 1) << TTBR_ASID_SHIFT)
/* Convenience macros to represent the ARMv8-A-specific
* configuration for memory access permission and
* cache-ability attribution.
@ -184,6 +203,50 @@
#define MMU_REGION_FLAT_ENTRY(name, adr, sz, attrs) \
MMU_REGION_ENTRY(name, adr, adr, sz, attrs)
#define MMU_PAGE_SHIFT (12U)
#define MMU_PAGE_SIZE (1U << MMU_PAGE_SHIFT) /* 4K pages */
#define MMU_PAGE_MASK (MMU_PAGE_SIZE - 1)
/* Entries per PGT */
#define MMU_PAGE_ENTRIES (MMU_PAGE_SIZE / sizeof(uintptr_t))
/* Amount of page table levels */
#define MMU_PGT_LEVELS (3U)
/* Page sizes per page table level */
#define MMU_L1_PAGE_SIZE (0x40000000) /* 1G */
#define MMU_L2_PAGE_SIZE (0x200000) /* 2M */
#define MMU_L3_PAGE_SIZE (0x1000) /* 4K */
/* Flags for user page tables */
#define MMU_UPGT_FLAGS (0)
/* Flags for normal memory region */
#define MMU_MT_NORMAL_FLAGS (PTE_BLOCK_DESC_INNER_SHARE | PTE_BLOCK_DESC_MEMTYPE(MT_NORMAL))
/* Flags for user FLASH (RX) and user RAM (RW) */
#define MMU_UTEXT_FLAGS (PTE_BLOCK_DESC_AP_RO | PTE_BLOCK_DESC_PXN | PTE_BLOCK_DESC_AP_USER | PTE_BLOCK_DESC_NG | MMU_MT_NORMAL_FLAGS)
#define MMU_UDATA_FLAGS (PTE_BLOCK_DESC_AP_RW | PTE_BLOCK_DESC_PXN | PTE_BLOCK_DESC_UXN | PTE_BLOCK_DESC_AP_USER | PTE_BLOCK_DESC_NG | MMU_MT_NORMAL_FLAGS)
/* I/O region flags */
#define MMU_IO_FLAGS (PTE_BLOCK_DESC_AP_RW | PTE_BLOCK_DESC_UXN | PTE_BLOCK_DESC_PXN)
/* Flags for kernel page tables */
#define MMU_KPGT_FLAGS (0)
/* Kernel FLASH and RAM are mapped globally */
#define MMU_KTEXT_FLAGS (PTE_BLOCK_DESC_AP_RO | PTE_BLOCK_DESC_UXN | MMU_MT_NORMAL_FLAGS)
#define MMU_KDATA_FLAGS (PTE_BLOCK_DESC_AP_RW | PTE_BLOCK_DESC_UXN | MMU_MT_NORMAL_FLAGS)
#ifndef __ASSEMBLY__
/****************************************************************************
@ -233,6 +296,10 @@ struct arm_mmu_ptables
uint64_t *base_xlat_table;
};
/****************************************************************************
* Public Data
****************************************************************************/
/* Reference to the MMU configuration.
*
* This struct is defined and populated for each SoC,
@ -242,6 +309,193 @@ struct arm_mmu_ptables
extern const struct arm_mmu_config g_mmu_config;
/* Kernel page directory root */
extern uintptr_t g_kernel_pgt_pbase;
/****************************************************************************
* Inline Functions
****************************************************************************/
/****************************************************************************
* Name: mmu_ttbr_reg
*
* Description:
* Utility function to build ttbr register value for input parameters
*
* 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.
*
****************************************************************************/
static inline uintptr_t mmu_ttbr_reg(uintptr_t pgbase, uint16_t asid)
{
uintptr_t reg;
reg = ((pgbase << TTBR_BADDR_SHIFT) & TTBR_BADDR_MASK);
reg |= (((uintptr_t)asid << TTBR_ASID_SHIFT) & TTBR_ASID_MASK);
return reg;
}
/****************************************************************************
* 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__
(
"dsb ishst\n"
"tlbi vale1is, %0\n"
"dsb ish\n"
"isb"
:
: "r" (vaddr)
: "memory"
);
}
/****************************************************************************
* Name: mmu_invalidate_tlbs
*
* Description:
* Flush the entire TLB
*
****************************************************************************/
static inline void mmu_invalidate_tlbs(void)
{
__asm__ __volatile__
(
"dsb nshst\n"
"tlbi vmalle1\n"
"dsb nsh\n"
"isb"
:
:
: "memory"
);
}
/****************************************************************************
* Name: mmu_write_ttbr0
*
* Description:
* Write ttbr0
*
* Input Parameters:
* reg - ttbr0 value
*
****************************************************************************/
static inline void mmu_write_ttbr0(uintptr_t reg)
{
write_sysreg(reg, ttbr0_el1);
mmu_invalidate_tlbs();
}
/****************************************************************************
* Name: mmu_read_ttbr0
*
* Description:
* Read ttbr0
*
* Returned Value:
* ttbr0 register value
*
****************************************************************************/
static inline uintptr_t mmu_read_ttbr0(void)
{
return read_sysreg(ttbr0_el1);
}
/****************************************************************************
* 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.
*
****************************************************************************/
static inline void mmu_enable(uintptr_t pgbase, uint16_t asid)
{
uintptr_t reg = mmu_ttbr_reg(pgbase, asid);
/* Commit to ttbr0 and synchronize */
mmu_write_ttbr0(reg);
}
/****************************************************************************
* Name: mmu_pte_to_paddr
*
* Description:
* Extract physical address from PTE
*
* Input Parameters:
* pte - Page table entry
*
* Returned Value:
* Physical address from PTE
*
****************************************************************************/
static inline uintptr_t mmu_pte_to_paddr(uintptr_t pte)
{
uintptr_t paddr = pte;
paddr &= PTE_PADDR_MASK; /* Remove flags */
return paddr;
}
/****************************************************************************
* Name: mmu_ttbr_to_paddr
*
* Description:
* Extract physical address from TTBR0/1
*
* Returned Value:
* Physical address from TTBR0/1 value
*
****************************************************************************/
static inline uintptr_t mmu_ttbr_to_paddr(uintptr_t ttbr)
{
uintptr_t baddr;
baddr = ttbr;
baddr = ((baddr >> TTBR_BADDR_SHIFT) & TTBR_BADDR_MASK);
return baddr;
}
/****************************************************************************
* Name: mmu_get_ttbr0_pgbase
*
* Description:
* Utility function to read the current base page table physical address
*
* Returned Value:
* Physical address of the current base page table
*
****************************************************************************/
static inline uintptr_t mmu_get_ttbr0_pgbase(void)
{
return mmu_ttbr_to_paddr(read_sysreg(ttbr0_el1));
}
/****************************************************************************
* Public Function Prototypes
****************************************************************************/
@ -249,6 +503,103 @@ extern const struct arm_mmu_config g_mmu_config;
int arm64_mmu_init(bool is_primary_core);
int arm64_mmu_set_memregion(const struct arm_mmu_region *region);
/****************************************************************************
* 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 - The virtual address to be mapped. 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, uint64_t mmuflags);
/****************************************************************************
* Name: mmu_ln_getentry
*
* Description:
* Get 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
*
****************************************************************************/
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_clear
*
* Description:
* Unmap 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
*
****************************************************************************/
#define mmu_ln_clear(ptlevel, lnvaddr, vaddr) \
mmu_ln_restore(ptlevel, lnvaddr, vaddr, 0)
/****************************************************************************
* Name: mmu_get_region_size
*
* Description:
* Get (giga/mega) page size for level n.
*
* Input Parameters:
* ptlevel - The translation table level, amount of levels is
* MMU implementation specific
*
* Returned Value:
* Region size for one page at level n.
*
****************************************************************************/
size_t mmu_get_region_size(uint32_t ptlevel);
#endif /* __ASSEMBLY__ */
#endif /* __ARCH_ARM64_SRC_COMMON_ARM64_MMU_H */

View file

@ -0,0 +1,173 @@
/****************************************************************************
* arch/arm64/src/common/arm64_pgalloc.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 <nuttx/config.h>
#include <string.h>
#include <assert.h>
#include <errno.h>
#include <debug.h>
#include <nuttx/arch.h>
#include <nuttx/addrenv.h>
#include <nuttx/irq.h>
#include <nuttx/pgalloc.h>
#include <nuttx/sched.h>
#include "addrenv.h"
#include "barriers.h"
#include "pgalloc.h"
#include "arm64_mmu.h"
#ifdef CONFIG_BUILD_KERNEL
/****************************************************************************
* Pre-processor Definitions
****************************************************************************/
/****************************************************************************
* Private Functions
****************************************************************************/
/****************************************************************************
* 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.
*
* Input Parameters:
* brkaddr - The heap break address. The next page will be allocated and
* mapped to this address. Must be page aligned. If the memory manager
* has not yet been initialized and this is the first block requested for
* the heap, then brkaddr should be zero. pgalloc will then assigned the
* well-known virtual address of the beginning of the heap.
* npages - The number of pages to allocate and map. Mapping of pages
* will be contiguous beginning beginning at 'brkaddr'
*
* Returned Value:
* The (virtual) base address of the mapped page will returned on success.
* Normally this will be the same as the 'brkaddr' input. However, if
* the 'brkaddr' input was zero, this will be the virtual address of the
* beginning of the heap. Zero is returned on any failure.
*
****************************************************************************/
uintptr_t pgalloc(uintptr_t brkaddr, unsigned int npages)
{
struct tcb_s *tcb = nxsched_self();
struct arch_addrenv_s *addrenv;
uintptr_t ptlast;
uintptr_t paddr;
uintptr_t vaddr;
DEBUGASSERT(tcb && tcb->addrenv_own);
addrenv = &tcb->addrenv_own->addrenv;
/* 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.
*/
/* brkaddr = 0 means that no heap has yet been allocated */
if (!brkaddr)
{
brkaddr = addrenv->heapvbase;
}
/* Start mapping from the old heap break address */
vaddr = brkaddr;
/* Sanity checks */
DEBUGASSERT(brkaddr >= addrenv->heapvbase);
DEBUGASSERT(MM_ISALIGNED(brkaddr));
for (; npages > 0; npages--)
{
/* Get the address of the last level page table */
ptlast = arm64_pgvaddr(arm64_get_pgtable(addrenv, vaddr));
if (!ptlast)
{
return 0;
}
/* Allocate physical memory for the new heap */
paddr = mm_pgalloc(1);
if (!paddr)
{
return 0;
}
/* Wipe the memory */
arm64_pgwipe(paddr);
/* Then add the reference */
mmu_ln_setentry(MMU_PGT_LEVELS, ptlast, paddr, vaddr, MMU_UDATA_FLAGS);
vaddr += MM_PGSIZE;
}
return brkaddr;
}
/****************************************************************************
* Name: up_allocate_pgheap
*
* Description:
* If there is a page allocator in the configuration, then this function
* must be provided by the platform-specific code. The OS initialization
* logic will call this function early in the initialization sequence to
* get the page heap information needed to configure the page allocator.
*
****************************************************************************/
void up_allocate_pgheap(void **heap_start, size_t *heap_size)
{
DEBUGASSERT(heap_start && heap_size);
*heap_start = (void *)CONFIG_ARCH_PGPOOL_PBASE;
*heap_size = (size_t)CONFIG_ARCH_PGPOOL_SIZE;
}
#endif /* CONFIG_BUILD_KERNEL */

View file

@ -0,0 +1,104 @@
/****************************************************************************
* arch/arm64/src/common/arm64_pthread_start.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 <nuttx/config.h>
#include <pthread.h>
#include <nuttx/arch.h>
#include <assert.h>
#include <arch/syscall.h>
#include "arm64_internal.h"
#include "sched/sched.h"
#if !defined(CONFIG_BUILD_FLAT) && defined(__KERNEL__) && \
!defined(CONFIG_DISABLE_PTHREAD)
/****************************************************************************
* Public Functions
****************************************************************************/
/****************************************************************************
* Name: up_pthread_start
*
* Description:
* In this kernel mode build, this function will be called to execute a
* pthread in user-space. When the pthread is first started, a kernel-mode
* stub will first run to perform some housekeeping functions. This
* kernel-mode stub will then be called transfer control to the user-mode
* pthread.
*
* Normally the a user-mode start-up stub will also execute before the
* pthread actually starts. See libc/pthread/pthread_create.c
*
* Input Parameters:
* startup - The user-space pthread startup function
* entrypt - The user-space address of the pthread entry point
* arg - Standard argument for the pthread entry point
*
* Returned Value:
* This function should not return. It should call the user-mode start-up
* stub and that stub should call pthread_exit if/when the user pthread
* terminates.
*
****************************************************************************/
void up_pthread_start(pthread_trampoline_t startup,
pthread_startroutine_t entrypt, pthread_addr_t arg)
{
struct tcb_s *tcb = this_task();
uint64_t spsr;
/* Set up to enter the user-space pthread start-up function in
* unprivileged mode. We need:
*
* R0 = entrypt
* R1 = arg
* ELR = startup
* SPSR = user mode
*/
tcb->xcp.regs[REG_ELR] = (uint64_t)startup;
tcb->xcp.regs[REG_X0] = (uint64_t)entrypt;
tcb->xcp.regs[REG_X1] = (uint64_t)arg;
spsr = tcb->xcp.regs[REG_SPSR] & ~SPSR_MODE_MASK;
tcb->xcp.regs[REG_SPSR] = spsr | SPSR_MODE_EL0T;
/* Fully unwind the kernel stack and drop to user space */
asm
(
"mov x0, %0\n" /* Get context registers */
"mov sp, x0\n" /* Stack pointer = context */
"b arm64_exit_exception\n"
:
: "r" (tcb->xcp.regs)
: "memory"
);
PANIC();
}
#endif /* !CONFIG_BUILD_FLAT && __KERNEL__ && !CONFIG_DISABLE_PTHREAD */

View file

@ -56,21 +56,21 @@ void arm64_init_signal_process(struct tcb_s *tcb, struct regs_context *regs)
struct regs_context *pctx = (regs != NULL) ? regs :
(struct regs_context *)tcb->xcp.regs;
struct regs_context *psigctx;
char *stack_ptr = (char *)pctx->sp_elx - sizeof(struct regs_context);
char *stack_ptr = (char *)pctx->sp_elx - sizeof(struct regs_context);
#ifdef CONFIG_ARCH_FPU
struct fpu_reg *pfpuctx;
pfpuctx = STACK_PTR_TO_FRAME(struct fpu_reg, stack_ptr);
tcb->xcp.fpu_regs = (uint64_t *)pfpuctx;
pfpuctx = STACK_PTR_TO_FRAME(struct fpu_reg, stack_ptr);
tcb->xcp.fpu_regs = (uint64_t *)pfpuctx;
/* set fpu context */
arm64_init_fpu(tcb);
stack_ptr = (char *)pfpuctx;
stack_ptr = (char *)pfpuctx;
#endif
psigctx = STACK_PTR_TO_FRAME(struct regs_context, stack_ptr);
psigctx = STACK_PTR_TO_FRAME(struct regs_context, stack_ptr);
memset(psigctx, 0, sizeof(struct regs_context));
psigctx->elr = (uint64_t)arm64_sigdeliver;
psigctx->elr = (uint64_t)arm64_sigdeliver;
/* Keep using SP_EL1 */
@ -80,7 +80,11 @@ void arm64_init_signal_process(struct tcb_s *tcb, struct regs_context *regs)
psigctx->spsr = SPSR_MODE_EL1H | DAIF_FIQ_BIT | DAIF_IRQ_BIT;
#endif
psigctx->sp_elx = (uint64_t)stack_ptr;
#ifdef CONFIG_ARCH_KERNEL_STACK
psigctx->sp_el0 = (uint64_t)pctx->sp_el0;
#else
psigctx->sp_el0 = (uint64_t)psigctx;
#endif
psigctx->exe_depth = 1;
psigctx->tpidr_el0 = (uint64_t)tcb;
psigctx->tpidr_el1 = (uint64_t)tcb;

View file

@ -0,0 +1,76 @@
/****************************************************************************
* arch/arm64/src/common/arm64_signal_dispatch.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 <nuttx/config.h>
#include <nuttx/arch.h>
#include <arch/syscall.h>
#include "arm64_internal.h"
#if !defined(CONFIG_BUILD_FLAT) && defined(__KERNEL__)
/****************************************************************************
* Public Functions
****************************************************************************/
/****************************************************************************
* Name: up_signal_dispatch
*
* Description:
* In the kernel mode build, this function will be called to execute a
* a signal handler in user-space. When the signal is delivered, a
* kernel-mode stub will first run to perform some housekeeping functions.
* This kernel-mode stub will then be called transfer control to the user
* mode signal handler by calling this function.
*
* Normally the user-mode signaling handling stub will also execute
* before the ultimate signal handler is called. See
* arch/arm64/src/common/arm64_signal_handler.S. This function is the
* user-space, signal handler trampoline function. It is called from
* up_signal_dispatch() in user-mode.
*
* Input Parameters:
* sighand - The address user-space signal handling function
* signo, info, and ucontext - Standard arguments to be passed to the
* signal handling function.
*
* Returned Value:
* None. This function does not return in the normal sense. It returns
* via an architecture specific system call made by up_signal_handler().
* However, this will look like a normal return by the caller of
* up_signal_dispatch.
*
****************************************************************************/
void up_signal_dispatch(_sa_sigaction_t sighand, int signo,
siginfo_t *info, void *ucontext)
{
/* Let sys_call4() do all of the work */
sys_call4(SYS_signal_handler, (uintptr_t)sighand, (uintptr_t)signo,
(uintptr_t)info, (uintptr_t)ucontext);
}
#endif /* !CONFIG_BUILD_FLAT && __KERNEL__ */

View file

@ -43,54 +43,10 @@
#include "signal/signal.h"
/****************************************************************************
* Private Functions
* Private Types
****************************************************************************/
/****************************************************************************
* Name: dispatch_syscall
*
* Description:
* Call the stub function corresponding to the system call. NOTE the non-
* standard parameter passing:
*
* x0 = SYS_ call number
* x1 = parm0
* x2 = parm1
* x3 = parm2
* x4 = parm3
* x5 = parm4
* x6 = parm5
*
* The values of X4-X5 may be preserved in the proxy called by the user
* code if they are used (but otherwise will not be).
*
* WARNING: There are hard-coded values in this logic!
*
****************************************************************************/
#ifdef CONFIG_LIB_SYSCALL
static void dispatch_syscall(void) naked_function;
static void dispatch_syscall(void)
{
__asm__ __volatile__
(
" sub sp, sp, #16\n" /* Create a stack frame to hold 3 parms + lr */
" str r4, [sp, #0]\n" /* Move parameter 4 (if any) into position */
" str r5, [sp, #4]\n" /* Move parameter 5 (if any) into position */
" str r6, [sp, #8]\n" /* Move parameter 6 (if any) into position */
" str lr, [sp, #12]\n" /* Save lr in the stack frame */
" ldr ip, =g_stublookup\n" /* R12=The base of the stub lookup table */
" ldr ip, [ip, r0, lsl #2]\n" /* R12=The address of the stub for this SYSCALL */
" blx ip\n" /* Call the stub (modifies lr) */
" ldr lr, [sp, #12]\n" /* Restore lr */
" add sp, sp, #16\n" /* Destroy the stack frame */
" mov r2, r0\n" /* R2=Save return value in R2 */
" mov r0, %0\n" /* R0=SYS_syscall_return */
" svc %1\n"::"i"(SYS_syscall_return),
"i"(SYS_syscall) /* Return from the SYSCALL */
);
}
#endif
typedef uintptr_t (*syscall_t)(unsigned int, ...);
/****************************************************************************
* Private Functions
@ -111,10 +67,87 @@ static void arm64_dump_syscall(const char *tag, uint64_t cmd,
f_regs->regs[REG_X6], f_regs->regs[REG_X7]);
}
#ifdef CONFIG_LIB_SYSCALL
/****************************************************************************
* Public Functions
****************************************************************************/
/****************************************************************************
* Name: dispatch_syscall
*
* Description:
* Call the stub function corresponding to the system call. NOTE the non-
* standard parameter passing:
*
* R0 = SYS_ call number
* R1 = parm0
* R2 = parm1
* R3 = parm2
* R4 = parm3
* R5 = parm4
* R6 = parm5
* R7 = context (aka SP)
*
****************************************************************************/
uintptr_t dispatch_syscall(unsigned int nbr, uintptr_t parm1,
uintptr_t parm2, uintptr_t parm3,
uintptr_t parm4, uintptr_t parm5,
uintptr_t parm6, void *context)
{
struct tcb_s *rtcb = this_task();
register long x0 asm("x0") = (long)(nbr);
register long x1 asm("x1") = (long)(parm1);
register long x2 asm("x2") = (long)(parm2);
register long x3 asm("x3") = (long)(parm3);
register long x4 asm("x4") = (long)(parm4);
register long x5 asm("x5") = (long)(parm5);
register long x6 asm("x6") = (long)(parm6);
syscall_t do_syscall;
uintptr_t ret;
/* Valid system call ? */
if (x0 > SYS_maxsyscall)
{
/* Nope, get out */
return -ENOSYS;
}
/* Set the user register context to TCB */
rtcb->xcp.regs = context;
/* Indicate that we are in a syscall handler */
rtcb->flags |= TCB_FLAG_SYSCALL;
/* Offset a0 to account for the reserved syscalls */
x0 -= CONFIG_SYS_RESERVED;
/* Find the system call from the lookup table */
do_syscall = (syscall_t)g_stublookup[x0];
/* Run the system call, save return value locally */
ret = do_syscall(x0, x1, x2, x3, x4, x5, x6);
/* System call is now done */
rtcb->flags &= ~TCB_FLAG_SYSCALL;
/* Unmask any pending signals now */
nxsig_unmask_pendingsignal();
return ret;
}
#endif
/****************************************************************************
* Name: arm64_syscall_switch
*
@ -245,6 +278,9 @@ int arm64_syscall(uint64_t *regs)
{
uint64_t cmd;
struct regs_context *f_regs;
#ifdef CONFIG_BUILD_KERNEL
uint64_t spsr;
#endif
/* Nested interrupts are not supported */
@ -260,138 +296,6 @@ int arm64_syscall(uint64_t *regs)
switch (cmd)
{
/* R0=SYS_syscall_return: This a SYSCALL return command:
*
* void arm_syscall_return(void);
*
* At this point, the following values are saved in context:
*
* R0 = SYS_syscall_return
*
* We need to restore the saved return address and return in
* unprivileged thread mode.
*/
#ifdef CONFIG_LIB_SYSCALL
case SYS_syscall_return:
{
struct tcb_s *rtcb = nxsched_self();
int index = (int)rtcb->xcp.nsyscalls - 1;
/* Make sure that there is a saved SYSCALL return address. */
DEBUGASSERT(index >= 0);
/* Setup to return to the saved SYSCALL return address in
* the original mode.
*/
regs[REG_PC] = rtcb->xcp.syscall[index].sysreturn;
#ifdef CONFIG_BUILD_KERNEL
regs[REG_CPSR] = rtcb->xcp.syscall[index].cpsr;
#endif
/* The return value must be in R0-R1. dispatch_syscall()
* temporarily moved the value for R0 into R2.
*/
regs[REG_R0] = regs[REG_R2];
#ifdef CONFIG_ARCH_KERNEL_STACK
/* If this is the outermost SYSCALL and if there is a saved user
* stack pointer, then restore the user stack pointer on this
* final return to user code.
*/
if (index == 0 && rtcb->xcp.ustkptr != NULL)
{
regs[REG_SP] = (uint64_t)rtcb->xcp.ustkptr;
rtcb->xcp.ustkptr = NULL;
}
#endif
/* Save the new SYSCALL nesting level */
rtcb->xcp.nsyscalls = index;
/* Handle any signal actions that were deferred while processing
* the system call.
*/
rtcb->flags &= ~TCB_FLAG_SYSCALL;
(void)nxsig_unmask_pendingsignal();
}
break;
#endif
/* R0=SYS_task_start: This a user task start
*
* void up_task_start(main_t taskentry, int argc, char *argv[])
* noreturn_function;
*
* At this point, the following values are saved in context:
*
* R0 = SYS_task_start
* R1 = taskentry
* R2 = argc
* R3 = argv
*/
#ifdef CONFIG_BUILD_KERNEL
case SYS_task_start:
{
/* Set up to return to the user-space _start function in
* unprivileged mode. We need:
*
* R0 = argc
* R1 = argv
* PC = taskentry
* CSPR = user mode
*/
regs[REG_PC] = regs[REG_R1];
regs[REG_R0] = regs[REG_R2];
regs[REG_R1] = regs[REG_R3];
cpsr = regs[REG_CPSR] & ~PSR_MODE_MASK;
regs[REG_CPSR] = cpsr | PSR_MODE_USR;
}
break;
#endif
/* R0=SYS_pthread_start: This a user pthread start
*
* void up_pthread_start(pthread_startroutine_t entrypt,
* pthread_addr_t arg) noreturn_function;
*
* At this point, the following values are saved in context:
*
* R0 = SYS_pthread_start
* R1 = entrypt
* R2 = arg
*/
#if !defined(CONFIG_BUILD_FLAT) && !defined(CONFIG_DISABLE_PTHREAD)
case SYS_pthread_start:
{
/* Set up to enter the user-space pthread start-up function in
* unprivileged mode. We need:
*
* R0 = entrypt
* R1 = arg
* PC = startup
* CSPR = user mode
*/
regs[REG_PC] = regs[REG_R1];
regs[REG_R0] = regs[REG_R2];
regs[REG_R1] = regs[REG_R3];
cpsr = regs[REG_CPSR] & ~PSR_MODE_MASK;
regs[REG_CPSR] = cpsr | PSR_MODE_USR;
}
break;
#endif
#ifdef CONFIG_BUILD_KERNEL
/* R0=SYS_signal_handler: This a user signal handler callback
*
@ -414,24 +318,24 @@ int arm64_syscall(uint64_t *regs)
/* Remember the caller's return address */
DEBUGASSERT(rtcb->xcp.sigreturn == 0);
rtcb->xcp.sigreturn = regs[REG_PC];
rtcb->xcp.sigreturn = regs[REG_ELR];
/* Set up to return to the user-space trampoline function in
* unprivileged mode.
*/
regs[REG_PC] = (uint64_t)ARCH_DATA_RESERVE->ar_sigtramp;
cpsr = regs[REG_CPSR] & ~PSR_MODE_MASK;
regs[REG_CPSR] = cpsr | PSR_MODE_USR;
regs[REG_ELR] = (uint64_t)ARCH_DATA_RESERVE->ar_sigtramp;
spsr = regs[REG_SPSR] & ~SPSR_MODE_MASK;
regs[REG_SPSR] = spsr | SPSR_MODE_EL0T;
/* Change the parameter ordering to match the expectation of struct
* userpace_s signal_handler.
*/
regs[REG_R0] = regs[REG_R1]; /* sighand */
regs[REG_R1] = regs[REG_R2]; /* signal */
regs[REG_R2] = regs[REG_R3]; /* info */
regs[REG_R3] = regs[REG_R4]; /* ucontext */
regs[REG_X0] = regs[REG_X1]; /* sighand */
regs[REG_X1] = regs[REG_X2]; /* signal */
regs[REG_X2] = regs[REG_X3]; /* info */
regs[REG_X3] = regs[REG_X4]; /* ucontext */
#ifdef CONFIG_ARCH_KERNEL_STACK
/* If we are signalling a user process, then we must be operating
@ -445,29 +349,16 @@ int arm64_syscall(uint64_t *regs)
{
uint64_t usp;
DEBUGASSERT(rtcb->xcp.kstkptr == NULL);
/* Copy "info" into user stack */
if (rtcb->xcp.sigdeliver)
{
usp = rtcb->xcp.saved_regs[REG_SP];
}
else
{
usp = rtcb->xcp.regs[REG_SP];
}
/* Create a frame for info and copy the kernel info */
usp = usp - sizeof(siginfo_t);
memcpy((void *)usp, (void *)regs[REG_R2], sizeof(siginfo_t));
rtcb->xcp.ustkptr = (uintptr_t *)read_sysreg(sp_el0);
usp = (uintptr_t)rtcb->xcp.ustkptr - sizeof(siginfo_t);
memcpy((void *)usp, (void *)regs[REG_X2], sizeof(siginfo_t));
/* Now set the updated SP and user copy of "info" to R2 */
rtcb->xcp.kstkptr = (uint64_t *)regs[REG_SP];
regs[REG_SP] = usp;
regs[REG_R2] = usp;
write_sysreg(usp, sp_el0);
regs[REG_X2] = usp;
}
#endif
}
@ -492,98 +383,24 @@ int arm64_syscall(uint64_t *regs)
DEBUGASSERT(rtcb->xcp.sigreturn != 0);
regs[REG_PC] = rtcb->xcp.sigreturn;
cpsr = regs[REG_CPSR] & ~PSR_MODE_MASK;
regs[REG_CPSR] = cpsr | PSR_MODE_SVC;
regs[REG_ELR] = rtcb->xcp.sigreturn;
spsr = regs[REG_SPSR] & ~SPSR_MODE_MASK;
regs[REG_SPSR] = spsr | SPSR_MODE_EL1H;
rtcb->xcp.sigreturn = 0;
#ifdef CONFIG_ARCH_KERNEL_STACK
/* We must enter here be using the user stack. We need to switch
* to back to the kernel user stack before returning to the kernel
* mode signal trampoline.
*/
/* Restore the original user SP, destroying the signal frame */
if (rtcb->xcp.kstack != NULL)
{
DEBUGASSERT(rtcb->xcp.kstkptr != NULL);
regs[REG_SP] = (uint64_t)rtcb->xcp.kstkptr;
rtcb->xcp.kstkptr = NULL;
}
write_sysreg(rtcb->xcp.ustkptr, sp_el0);
#endif
}
break;
#endif
/* This is not an architecture-specific system call. If NuttX is built
* as a standalone kernel with a system call interface, then all of the
* additional system calls must be handled as in the default case.
*/
default:
{
#ifdef CONFIG_LIB_SYSCALL
struct tcb_s *rtcb = nxsched_self();
int index = rtcb->xcp.nsyscalls;
/* Verify that the SYS call number is within range */
DEBUGASSERT(cmd >= CONFIG_SYS_RESERVED && cmd < SYS_maxsyscall);
/* Make sure that there is a no saved SYSCALL return address. We
* cannot yet handle nested system calls.
*/
DEBUGASSERT(index < CONFIG_SYS_NNEST);
/* Setup to return to dispatch_syscall in privileged mode. */
rtcb->xcp.syscall[index].sysreturn = regs[REG_PC];
#ifdef CONFIG_BUILD_KERNEL
rtcb->xcp.syscall[index].cpsr = regs[REG_CPSR];
#endif
regs[REG_PC] = (uint64_t)dispatch_syscall;
#ifdef CONFIG_BUILD_KERNEL
cpsr = regs[REG_CPSR] & ~PSR_MODE_MASK;
regs[REG_CPSR] = cpsr | PSR_MODE_SVC;
#endif
/* Offset R0 to account for the reserved values */
regs[REG_R0] -= CONFIG_SYS_RESERVED;
/* Indicate that we are in a syscall handler. */
rtcb->flags |= TCB_FLAG_SYSCALL;
#ifdef CONFIG_ARCH_KERNEL_STACK
/* If this is the first SYSCALL and if there is an allocated
* kernel stack, then switch to the kernel stack.
*/
if (index == 0 && rtcb->xcp.kstack != NULL)
{
rtcb->xcp.ustkptr = (uint64_t *)regs[REG_SP];
if (rtcb->xcp.kstkptr != NULL)
{
regs[REG_SP] = (uint64_t)rtcb->xcp.kstkptr;
}
else
{
regs[REG_SP] = (uint64_t)rtcb->xcp.kstack +
ARCH_KERNEL_STACKSIZE;
}
}
#endif
/* Save the new SYSCALL nesting level */
rtcb->xcp.nsyscalls = index + 1;
#else
svcerr("ERROR: Bad SYS call: 0x%" PRIx64 "\n", cmd);
#endif
}
break;
DEBUGPANIC();
break;
}
return 0;

View file

@ -0,0 +1,101 @@
/****************************************************************************
* arch/arm64/src/common/arm64_task_start.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 <nuttx/config.h>
#include <nuttx/arch.h>
#include <assert.h>
#include <arch/syscall.h>
#include "arm64_internal.h"
#include "sched/sched.h"
#ifndef CONFIG_BUILD_FLAT
/****************************************************************************
* Public Functions
****************************************************************************/
/****************************************************************************
* Name: up_task_start
*
* Description:
* In this kernel mode build, this function will be called to execute a
* task in user-space. When the task is first started, a kernel-mode
* stub will first run to perform some housekeeping functions. This
* kernel-mode stub will then be called transfer control to the user-mode
* task.
*
* Normally the a user-mode start-up stub will also execute before the
* task actually starts. See libc/sched/task_startup.c
*
* Input Parameters:
* taskentry - The user-space entry point of the task.
* argc - The number of parameters being passed.
* argv - The parameters being passed. These lie in kernel-space memory
* and will have to be reallocated in user-space memory.
*
* Returned Value:
* This function should not return. It should call the user-mode start-up
* stub and that stub should call exit if/when the user task terminates.
*
****************************************************************************/
void up_task_start(main_t taskentry, int argc, char *argv[])
{
struct tcb_s *tcb = this_task();
uint64_t spsr;
/* Set up to return to the user-space _start function in
* unprivileged mode. We need:
*
* R0 = argc
* R1 = argv
* ELR = taskentry
* SPSR = user mode
*/
tcb->xcp.regs[REG_ELR] = (uint64_t)taskentry;
tcb->xcp.regs[REG_X0] = (uint64_t)argc;
tcb->xcp.regs[REG_X1] = (uint64_t)argv;
spsr = tcb->xcp.regs[REG_SPSR] & ~SPSR_MODE_MASK;
tcb->xcp.regs[REG_SPSR] = spsr | SPSR_MODE_EL0T;
/* Fully unwind the kernel stack and drop to user space */
asm
(
"mov x0, %0\n" /* Get context registers */
"mov sp, x0\n" /* Stack pointer = context */
"b arm64_exit_exception\n"
:
: "r" (tcb->xcp.regs)
: "memory"
);
PANIC();
}
#endif /* !CONFIG_BUILD_FLAT */

View file

@ -56,6 +56,22 @@
.endm
.macro enable_irq
#ifdef CONFIG_ARM64_DECODEFIQ
msr daifclr, #3
#else
msr daifclr, #2
#endif
.endm
.macro disable_irq
#ifdef CONFIG_ARM64_DECODEFIQ
msr daifset, #3
#else
msr daifset, #2
#endif
.endm
/****************************************************************************
* Public Functions
****************************************************************************/
@ -132,7 +148,7 @@ SECTION_FUNC(text, arm64_context_switch)
ldp x0, x1, [sp], #16
#endif
/* Save the current SP_EL0 */
/* Save the current SP_ELx */
add x4, sp, #8 * XCPTCONTEXT_GP_REGS
str x4, [x1, #8 * REG_SP_ELX]
@ -214,6 +230,30 @@ SECTION_FUNC(text, arm64_sync_exc)
bne exc_handle
#ifdef CONFIG_LIB_SYSCALL
/* Handle user system calls separately */
cmp x0, #CONFIG_SYS_RESERVED
blt reserved_syscall
/* Call dispatch_syscall() on the kernel stack with interrupts enabled */
enable_irq
mov x7, sp /* x7 = context */
bl dispatch_syscall
disable_irq
/* Save the return value into the user context */
str x0, [sp, #8 * REG_X0]
/* Return from exception */
b arm64_exit_exception
reserved_syscall:
#endif
/* x0 = syscall_cmd
* if ( x0 <= SYS_switch_context ) {
* call context_switch
@ -253,10 +293,6 @@ SECTION_FUNC(text, arm64_sync_exc)
bl arm64_syscall /* Call the handler */
/* Save the return value into the */
str x0, [sp, #8 * REG_X0]
/* Return from exception */
b arm64_exit_exception

View file

@ -27,6 +27,7 @@
#include <sys/types.h>
#include <stdlib.h>
#include <nuttx/arch.h>
#include <nuttx/addrenv.h>
#include <arch/syscall.h>
@ -71,21 +72,75 @@ static void sig_trampoline(void)
{
__asm__ __volatile__
(
" push {lr}\n" /* Save LR on the stack */
" mov ip, r0\n" /* IP=sighand */
" mov r0, r1\n" /* R0=signo */
" mov r1, r2\n" /* R1=info */
" mov r2, r3\n" /* R2=ucontext */
" blx ip\n" /* Call the signal handler */
" pop {r2}\n" /* Recover LR in R2 */
" mov lr, r2\n" /* Restore LR */
" mov r0, %0\n" /* SYS_signal_handler_return */
" svc %1\n" /* Return from the SYSCALL */
::"i"(SYS_signal_handler_return),
" sub sp, sp, #8\n" /* Create a stack frame to hold LR */
" str lr, [sp, #0]\n" /* Save LR on the stack */
" mov ip0, x0\n" /* IP=sighand */
" mov x0, x1\n" /* R0=signo */
" mov x1, x2\n" /* R1=info */
" mov x2, x3\n" /* R2=ucontext */
" blr ip0\n" /* Call the signal handler */
" ldr lr, [sp, #0]\n" /* Recover LR in R2 */
" add sp, sp, #8\n" /* Destroy the stack frame */
" mov x0, %0\n" /* SYS_signal_handler_return */
" svc %1\n" /* Return from the SYSCALL */
:
: "i"(SYS_signal_handler_return),
"i"(SYS_syscall)
:
);
}
/****************************************************************************
* Public Data
****************************************************************************/
/* Linker defined symbols to .ctors and .dtors */
extern initializer_t _sctors[];
extern initializer_t _ectors[];
extern initializer_t _sdtors[];
extern initializer_t _edtors[];
/****************************************************************************
* Private Functions
****************************************************************************/
#ifdef CONFIG_HAVE_CXX
/****************************************************************************
* Name: exec_ctors
*
* Description:
* Call static constructors
*
****************************************************************************/
static void exec_ctors(void)
{
for (initializer_t *ctor = _sctors; ctor != _ectors; ctor++)
{
(*ctor)();
}
}
/****************************************************************************
* Name: exec_dtors
*
* Description:
* Call static destructors
*
****************************************************************************/
static void exec_dtors(void)
{
for (initializer_t *dtor = _sdtors; dtor != _edtors; dtor++)
{
(*dtor)();
}
}
#endif
/****************************************************************************
* Public Functions
****************************************************************************/
@ -120,11 +175,15 @@ void __start(int argc, char *argv[])
ARCH_DATA_RESERVE->ar_sigtramp = (addrenv_sigtramp_t)sig_trampoline;
#ifdef CONFIG_HAVE_CXX
/* Call C++ constructors */
exec_ctors();
/* Setup so that C++ destructors called on task exit */
/* REVISIT: Missing logic */
atexit(exec_dtors);
#endif
/* Call the main() entry point passing argc and argv. */

View file

@ -0,0 +1,127 @@
/****************************************************************************
* arch/arm64/src/common/pgalloc.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_ARM64_SRC_COMMON_PGALLOC_H
#define __ARCH_ARM64_SRC_COMMON_PGALLOC_H
#ifdef CONFIG_MM_PGALLOC
/****************************************************************************
* Included Files
****************************************************************************/
#include <nuttx/config.h>
#include <stdint.h>
#include <string.h>
#include "addrenv.h"
/****************************************************************************
* Pre-processor Definitions
****************************************************************************/
#ifndef CONFIG_ARCH_PGPOOL_MAPPING
# error "ARM64 needs CONFIG_ARCH_PGPOOL_MAPPING"
#endif
/****************************************************************************
* Public Functions Prototypes
****************************************************************************/
/****************************************************************************
* Name: arm64_pgvaddr
*
* Description:
* Get virtual address for pgpool physical address. Note: this function
* is minimalistic and is only usable for kernel mappings and only tests
* if the paddr is in the pgpool. For user mapped addresses this does not
* work.
*
* Note:
* To get it to work with user addresses, a manual table walk needs to be
* implemented. Not too complex, but not needed for anything -> not
* implemented.
*
* Input Parameters:
* paddr - Physical pgpool address
*
* Return:
* vaddr - Virtual address for physical address
*
****************************************************************************/
#ifdef CONFIG_ARCH_PGPOOL_MAPPING
static inline uintptr_t arm64_pgvaddr(uintptr_t paddr)
{
if (paddr >= CONFIG_ARCH_PGPOOL_PBASE && paddr < CONFIG_ARCH_PGPOOL_PEND)
{
return paddr - CONFIG_ARCH_PGPOOL_PBASE + CONFIG_ARCH_PGPOOL_VBASE;
}
else if (paddr >= CONFIG_RAM_START && paddr < CONFIG_RAM_END)
{
return paddr - CONFIG_RAM_START + CONFIG_RAM_VSTART;
}
return 0;
}
#endif /* CONFIG_ARCH_PGPOOL_MAPPING */
/****************************************************************************
* Name: arm64_uservaddr
*
* Description:
* Return true if the virtual address, vaddr, lies in the user address
* space.
*
****************************************************************************/
static inline bool arm64_uservaddr(uintptr_t vaddr)
{
/* Check if this address is within the range of the virtualized .bss/.data,
* heap, or stack regions.
*/
return ((vaddr >= ARCH_ADDRENV_VBASE && vaddr < ARCH_ADDRENV_VEND)
#ifdef CONFIG_ARCH_VMA_MAPPING
|| (vaddr >= CONFIG_ARCH_SHM_VBASE && vaddr < ARCH_SHM_VEND)
#endif
);
}
/****************************************************************************
* Name: arm64_pgwipe
*
* Description:
* Wipe a page of physical memory, first mapping it into virtual memory.
*
* Input Parameters:
* paddr - Physical address of page
*
****************************************************************************/
static inline void arm64_pgwipe(uintptr_t paddr)
{
uintptr_t vaddr = arm64_pgvaddr(paddr);
memset((void *)vaddr, 0, MM_PGSIZE);
}
#endif /* CONFIG_MM_PGALLOC */
#endif /* __ARCH_ARM64_SRC_COMMON_PGALLOC_H */