mirror of
https://github.com/apache/nuttx.git
synced 2026-08-27 04:10:40 +00:00
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:
parent
7f228b1554
commit
29f8648ecc
25 changed files with 3146 additions and 436 deletions
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
||||
|
|
|
|||
|
|
@ -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 */
|
||||
|
|
|
|||
818
arch/arm64/src/common/arm64_addrenv.c
Normal file
818
arch/arm64/src/common/arm64_addrenv.c
Normal 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;
|
||||
}
|
||||
110
arch/arm64/src/common/arm64_addrenv_kstack.c
Normal file
110
arch/arm64/src/common/arm64_addrenv_kstack.c
Normal 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 */
|
||||
148
arch/arm64/src/common/arm64_addrenv_perms.c
Normal file
148
arch/arm64/src/common/arm64_addrenv_perms.c
Normal 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);
|
||||
}
|
||||
312
arch/arm64/src/common/arm64_addrenv_pgmap.c
Normal file
312
arch/arm64/src/common/arm64_addrenv_pgmap.c
Normal 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 */
|
||||
117
arch/arm64/src/common/arm64_addrenv_shm.c
Normal file
117
arch/arm64/src/common/arm64_addrenv_shm.c
Normal 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 */
|
||||
219
arch/arm64/src/common/arm64_addrenv_utils.c
Normal file
219
arch/arm64/src/common/arm64_addrenv_utils.c
Normal 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 */
|
||||
|
|
@ -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);
|
||||
|
||||
|
|
|
|||
|
|
@ -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 */
|
||||
|
|
|
|||
|
|
@ -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];
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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 */
|
||||
|
|
|
|||
173
arch/arm64/src/common/arm64_pgalloc.c
Normal file
173
arch/arm64/src/common/arm64_pgalloc.c
Normal 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 */
|
||||
104
arch/arm64/src/common/arm64_pthread_start.c
Normal file
104
arch/arm64/src/common/arm64_pthread_start.c
Normal 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 */
|
||||
|
|
@ -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;
|
||||
|
|
|
|||
76
arch/arm64/src/common/arm64_signal_dispatch.c
Normal file
76
arch/arm64/src/common/arm64_signal_dispatch.c
Normal 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__ */
|
||||
|
|
@ -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;
|
||||
|
|
|
|||
101
arch/arm64/src/common/arm64_task_start.c
Normal file
101
arch/arm64/src/common/arm64_task_start.c
Normal 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 */
|
||||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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. */
|
||||
|
||||
|
|
|
|||
127
arch/arm64/src/common/pgalloc.h
Normal file
127
arch/arm64/src/common/pgalloc.h
Normal 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 */
|
||||
Loading…
Add table
Add a link
Reference in a new issue