This is an automated email from the ASF dual-hosted git repository. acassis pushed a commit to branch master in repository https://gitbox.apache.org/repos/asf/nuttx.git
commit 712e8d9cc7e158faa177fb84e2507a96e2013b75 Author: p-szafonimateusz <[email protected]> AuthorDate: Sat Jul 20 11:59:00 2024 +0200 arch/x86_64: add kernel build support arch/x86_64: add kernel build support Signed-off-by: p-szafonimateusz <[email protected]> --- arch/Kconfig | 2 +- arch/x86_64/include/intel64/irq.h | 8 + arch/x86_64/include/syscall.h | 44 +++- arch/x86_64/src/common/CMakeLists.txt | 7 +- arch/x86_64/src/common/Make.defs | 7 + arch/x86_64/src/common/Toolchain.defs | 2 +- arch/x86_64/src/common/crt0.c | 121 +++++++++++ arch/x86_64/src/common/pgalloc.h | 22 ++ arch/x86_64/src/common/x86_64_allocateheap.c | 4 + arch/x86_64/src/common/x86_64_pgalloc.c | 232 +++++++++++++++++++++ arch/x86_64/src/common/x86_64_pthread_start.c | 77 +++++++ arch/x86_64/src/common/x86_64_signal_dispatch.c | 76 +++++++ arch/x86_64/src/common/x86_64_syscall.c | 138 ++++++++++++ arch/x86_64/src/common/x86_64_task_start.c | 74 +++++++ arch/x86_64/src/intel64/intel64_head.S | 10 + arch/x86_64/src/intel64/intel64_initialstate.c | 11 - arch/x86_64/src/intel64/intel64_irq.c | 7 +- .../x86_64/intel64/qemu-intel64/scripts/Make.defs | 6 +- .../intel64/qemu-intel64/scripts/qemu-kernel.ld | 130 ++++++++++++ 19 files changed, 959 insertions(+), 19 deletions(-) diff --git a/arch/Kconfig b/arch/Kconfig index 3f805ab1dd..978a2e2901 100644 --- a/arch/Kconfig +++ b/arch/Kconfig @@ -145,7 +145,7 @@ config ARCH_X86_64 select LIBC_ARCH_ELF_64BIT if LIBC_ARCH_ELF select ARCH_TOOLCHAIN_GNU select ARCH_HAVE_BACKTRACE - select ARCH_HAVE_FORK + select ARCH_HAVE_FORK if !BUILD_KERNEL select ARCH_HAVE_SETJMP select ARCH_HAVE_PERF_EVENTS ---help--- diff --git a/arch/x86_64/include/intel64/irq.h b/arch/x86_64/include/intel64/irq.h index 7dcb0e102e..c149604749 100644 --- a/arch/x86_64/include/intel64/irq.h +++ b/arch/x86_64/include/intel64/irq.h @@ -512,6 +512,14 @@ struct xcpt_syscall_s struct xcptcontext { +#ifdef CONFIG_BUILD_KERNEL + /* This is the saved address to use when returning from a user-space + * signal handler. + */ + + uintptr_t sigreturn; +#endif + /* These are saved copies of instruction pointer and EFLAGS used during * signal processing. */ diff --git a/arch/x86_64/include/syscall.h b/arch/x86_64/include/syscall.h index 5e27629991..7fc567b105 100644 --- a/arch/x86_64/include/syscall.h +++ b/arch/x86_64/include/syscall.h @@ -38,7 +38,49 @@ /* Configuration ************************************************************/ -#define CONFIG_SYS_RESERVED 0 +#ifndef CONFIG_BUILD_FLAT +# define CONFIG_SYS_RESERVED 4 +#else +# define CONFIG_SYS_RESERVED 0 +#endif + +/* system calls */ + +#ifndef CONFIG_BUILD_FLAT +/* SYS call 0: + * + * void up_task_start(main_t taskentry, int argc, char *argv[]) + * noreturn_function; + */ + +# define SYS_task_start (0) + +/* SYS call 1: + * + * void up_pthread_start((pthread_startroutine_t startup, + * pthread_startroutine_t entrypt, pthread_addr_t arg) + * noreturn_function + */ + +# define SYS_pthread_start (1) + +/* SYS call 2: + * + * void signal_handler(_sa_sigaction_t sighand, + * int signo, siginfo_t *info, + * void *ucontext); + */ + +# define SYS_signal_handler (2) + +/* SYS call 3: + * + * void signal_handler_return(void); + */ + +# define SYS_signal_handler_return (3) + +#endif /* !CONFIG_BUILD_FLAT */ /**************************************************************************** * Public Types diff --git a/arch/x86_64/src/common/CMakeLists.txt b/arch/x86_64/src/common/CMakeLists.txt index 8e11fb3fa3..5a4334fbe3 100644 --- a/arch/x86_64/src/common/CMakeLists.txt +++ b/arch/x86_64/src/common/CMakeLists.txt @@ -56,7 +56,12 @@ if(CONFIG_ARCH_USE_MMU) endif() if(CONFIG_ARCH_ADDRENV) - list(APPEND SRCS x86_64_addrenv.c x86_64_addrenv_perms.c) + list(APPEND SRCS x86_64_addrenv.c x86_64_pgalloc.c x86_64_addrenv_perms.c) +endif() + +if(NOT CONFIG_BUILD_FLAT) + list(APPEND SRCS x86_64_task_start.c x86_64_pthread_start.c + x86_64_signal_dispatch.c) endif() if(NOT CONFIG_ALARM_ARCH) diff --git a/arch/x86_64/src/common/Make.defs b/arch/x86_64/src/common/Make.defs index a13057704e..bb18944627 100644 --- a/arch/x86_64/src/common/Make.defs +++ b/arch/x86_64/src/common/Make.defs @@ -18,6 +18,13 @@ # ############################################################################ +ifeq ($(CONFIG_BUILD_KERNEL),y) +crt0$(OBJEXT): crt0.c + $(CC) $(CFLAGS) -c common$(DELIM)crt0.c -o crt0$(OBJEXT) + +STARTUP_OBJS = crt0$(OBJEXT) +endif + # Common x86_64 files CMN_CSRCS += x86_64_allocateheap.c x86_64_copystate.c x86_64_exit.c diff --git a/arch/x86_64/src/common/Toolchain.defs b/arch/x86_64/src/common/Toolchain.defs index 77ecc2825c..5a57c77529 100644 --- a/arch/x86_64/src/common/Toolchain.defs +++ b/arch/x86_64/src/common/Toolchain.defs @@ -241,7 +241,7 @@ LDMODULEFLAGS = -r -T $(call CONVERT_PATH,$(TOPDIR)/libs/libc/modlib/gnu-elf.ld) CELFFLAGS = $(CFLAGS) -fvisibility=hidden CXXELFFLAGS = $(CXXFLAGS) -fvisibility=hidden -LDELFFLAGS = -r -e main --gc-sections +LDELFFLAGS = -r -e main LDELFFLAGS += -T $(call CONVERT_PATH,$(TOPDIR)$(DELIM)libs$(DELIM)libc$(DELIM)modlib$(DELIM)gnu-elf.ld) # -fno-pic to avoid GOT relocations diff --git a/arch/x86_64/src/common/crt0.c b/arch/x86_64/src/common/crt0.c new file mode 100644 index 0000000000..cdd3321e76 --- /dev/null +++ b/arch/x86_64/src/common/crt0.c @@ -0,0 +1,121 @@ +/**************************************************************************** + * arch/x86_64/src/common/crt0.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 <sys/types.h> +#include <stdlib.h> + +#include <nuttx/addrenv.h> + +#include <arch/syscall.h> + +#ifdef CONFIG_BUILD_KERNEL + +/**************************************************************************** + * Public Function Prototypes + ****************************************************************************/ + +int main(int argc, char *argv[]); + +/**************************************************************************** + * Private Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: up_signal_handler + * + * Description: + * This function is the user-space, signal handler trampoline function. It + * is called from up_signal_dispatch() in user-mode. + * + * Returned Value: + * None. This function does not return in the normal sense. It returns + * via the SYS_signal_handler_return (see syscall.h) + * + ****************************************************************************/ + +static void sig_trampoline(void) naked_function; +static void sig_trampoline(void) +{ + /* RDI = signal + * RSI = info + * RDX = ucontext + * R10 = sighand + * RAX = syscall ID + */ + + __asm__ volatile + ( + "call *%%r10\n" + "movq %0, %%rax\n" + "syscall\n" + :: "i"(SYS_signal_handler_return) + ); +} + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: __start + * + * Description: + * This function is the low level entry point into the main thread of + * execution of a task. It receives initial control when the task is + * started and calls main entry point of the newly started task. + * + * Input Parameters: + * 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 + * main() function. If that function returns, this function will call + * exit. + * + ****************************************************************************/ + +void __start(int argc, char *argv[]) +{ + int ret; + + /* Initialize the reserved area at the beginning of the .bss/.data region + * that is visible to the RTOS. + */ + + ARCH_DATA_RESERVE->ar_sigtramp = (addrenv_sigtramp_t)sig_trampoline; + + /* Call the main() entry point passing argc and argv. */ + + ret = main(argc, argv); + + /* Call exit() if/when the main() returns */ + + exit(ret); +} + +#endif /* CONFIG_BUILD_KERNEL */ diff --git a/arch/x86_64/src/common/pgalloc.h b/arch/x86_64/src/common/pgalloc.h index e85d4ee8b5..e1f7531b5a 100644 --- a/arch/x86_64/src/common/pgalloc.h +++ b/arch/x86_64/src/common/pgalloc.h @@ -93,6 +93,28 @@ static inline uintptr_t x86_64_pgpaddr(uintptr_t vaddr) return 0; } +/**************************************************************************** + * Name: x86_64_uservaddr + * + * Description: + * Return true if the virtual address, vaddr, lies in the user address + * space. + * + ****************************************************************************/ + +static inline bool x86_64_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: x86_64_pgwipe * diff --git a/arch/x86_64/src/common/x86_64_allocateheap.c b/arch/x86_64/src/common/x86_64_allocateheap.c index 0dbfc9a7f9..0623f8fe39 100644 --- a/arch/x86_64/src/common/x86_64_allocateheap.c +++ b/arch/x86_64/src/common/x86_64_allocateheap.c @@ -93,7 +93,11 @@ const uintptr_t g_idle_topstack[CONFIG_SMP_NCPUS] = * ****************************************************************************/ +#ifdef CONFIG_BUILD_KERNEL +void up_allocate_kheap(void **heap_start, size_t *heap_size) +#else void up_allocate_heap(void **heap_start, size_t *heap_size) +#endif /* CONFIG_BUILD_KERNEL */ { uintptr_t hstart; uintptr_t topstack; diff --git a/arch/x86_64/src/common/x86_64_pgalloc.c b/arch/x86_64/src/common/x86_64_pgalloc.c new file mode 100644 index 0000000000..75db0cbe70 --- /dev/null +++ b/arch/x86_64/src/common/x86_64_pgalloc.c @@ -0,0 +1,232 @@ +/**************************************************************************** + * arch/x86_64/src/common/x86_64_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/irq.h> +#include <nuttx/sched.h> +#include <nuttx/arch.h> +#include <nuttx/addrenv.h> +#include <nuttx/pgalloc.h> +#include <nuttx/spinlock.h> + +#include "addrenv.h" +#include "pgalloc.h" +#include "x86_64_mmu.h" + +#ifdef CONFIG_BUILD_KERNEL + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +/* Last PGT level */ + +#define PGT_LAST (X86_MMU_PT_LEVELS) + +/**************************************************************************** + * Private Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: x86_64_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 x86_64_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 = x86_64_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 (x86_64_uservaddr(vaddr)) + { + flags = MMU_UPGT_FLAGS; + } + else + { + flags = MMU_KPGT_FLAGS; + } + + /* Wipe the page and assign it */ + + x86_64_pgwipe(paddr); + mmu_ln_setentry(ptlevel, ptprev, paddr, vaddr, flags); + } + } + + /* Flush the data cache, so the changes are committed to memory */ + + SP_DMB(); + + return paddr; +} + +/**************************************************************************** + * 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 = x86_64_pgvaddr(x86_64_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 */ + + x86_64_pgwipe(paddr); + + /* Then add the reference */ + + mmu_ln_setentry(PGT_LAST, ptlast, paddr, vaddr, MMU_UDATA_FLAGS); + vaddr += MM_PGSIZE; + } + + /* Flush the data cache, so the changes are committed to memory */ + + SP_DMB(); + + return brkaddr; +} + +#endif /* CONFIG_BUILD_KERNEL */ diff --git a/arch/x86_64/src/common/x86_64_pthread_start.c b/arch/x86_64/src/common/x86_64_pthread_start.c new file mode 100644 index 0000000000..e24d06efcc --- /dev/null +++ b/arch/x86_64/src/common/x86_64_pthread_start.c @@ -0,0 +1,77 @@ +/**************************************************************************** + * arch/x86_64/src/common/x86_64_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 "x86_64_internal.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) +{ + /* Let sys_call3() do all of the work */ + + sys_call3(SYS_pthread_start, (uintptr_t)startup, (uintptr_t)entrypt, + (uintptr_t)arg); + + PANIC(); +} + +#endif /* !CONFIG_BUILD_FLAT && __KERNEL__ && !CONFIG_DISABLE_PTHREAD */ diff --git a/arch/x86_64/src/common/x86_64_signal_dispatch.c b/arch/x86_64/src/common/x86_64_signal_dispatch.c new file mode 100644 index 0000000000..3a5bcc12bd --- /dev/null +++ b/arch/x86_64/src/common/x86_64_signal_dispatch.c @@ -0,0 +1,76 @@ +/**************************************************************************** + * arch/x86_64/src/common/x86_64_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 "x86_64_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/x86_64/src/common/x86_64_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__ */ diff --git a/arch/x86_64/src/common/x86_64_syscall.c b/arch/x86_64/src/common/x86_64_syscall.c index 5e52b5347c..cea1ad977d 100644 --- a/arch/x86_64/src/common/x86_64_syscall.c +++ b/arch/x86_64/src/common/x86_64_syscall.c @@ -118,6 +118,144 @@ uint64_t *x86_64_syscall(uint64_t *regs) switch (cmd) { +#ifdef CONFIG_BUILD_KERNEL + /* cmd=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: + * + * cmd = SYS_task_start + * arg1 = taskentry + * arg2 = argc + * arg3 = argv + */ + + case SYS_task_start: + { + /* Set up to return to the user-space _start function in + * unprivileged mode. We need: + * + * RDI = argc + * RSI = argv + * RCX = taskentry (SYSRETQ return address) + * + */ + + regs[REG_RDI] = arg2; + regs[REG_RSI] = arg3; + regs[REG_RCX] = arg1; + + break; + } + + /* cmd=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: + * + * cmd = SYS_pthread_start + * arg1 = startup + * arg2 = entrypt + * arg3 = arg + */ + + case SYS_pthread_start: + { + /* Set up to enter the user-space pthread start-up function in + * unprivileged mode. We need: + * + * RDI = entrypt + * RSI = arg + * RCX = startup (SYSRETQ return address) + */ + + regs[REG_RDI] = arg2; + regs[REG_RSI] = arg3; + regs[REG_RCX] = arg1; + + break; + } + + /* cmd=SYS_signal_handler: This a user signal handler callback + * + * void signal_handler(_sa_sigaction_t sighand, int signo, + * siginfo_t *info, void *ucontext); + * + * At this point, the following values are saved in context: + * + * cmd = SYS_signal_handler + * arg1 = sighand + * arg2 = signo + * arg3 = info + * arg4 = ucontext (on the stack) + */ + + case SYS_signal_handler: + { + struct tcb_s *rtcb = nxsched_self(); + + /* Remember the caller's return address */ + + DEBUGASSERT(rtcb->xcp.sigreturn == 0); + rtcb->xcp.sigreturn = regs[REG_RCX]; + + /* Set up to return to the user-space trampoline function in + * unprivileged mode. + */ + + regs[REG_RCX] = (uint64_t)ARCH_DATA_RESERVE->ar_sigtramp; + + /* Change the parameter ordering to match the expectation of struct + * userpace_s signal_handler. + */ + + regs[REG_RDI] = arg2; /* signal */ + regs[REG_RSI] = arg3; /* info */ + regs[REG_RDX] = arg4; /* ucontext */ + regs[REG_R10] = arg1; /* sighand */ + + break; + } + + /* cmd=SYS_signal_handler_return: This a user signal handler callback + * + * void signal_handler_return(void); + * + * At this point, the following values are saved in context: + * + * cmd = SYS_signal_handler_return + */ + + case SYS_signal_handler_return: + { + /* Set up to return to the user-space. We need: + * + * RCX = taskentry (SYSRETQ return address) + * + */ + + struct tcb_s *rtcb = nxsched_self(); + + /* Set up to return to the kernel-mode signal dispatching logic. */ + + DEBUGASSERT(rtcb->xcp.sigreturn != 0); + + regs[REG_RCX] = rtcb->xcp.sigreturn; + regs[REG_RSP] = rtcb->xcp.saved_rsp; + rtcb->xcp.sigreturn = 0; + + /* For kernel mode, we should be already on a correct kernel stack + * which was recovered in x86_64_syscall_entry. + */ + + break; + } +#endif /* CONFIG_BUILD_KERNEL */ + /* 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 diff --git a/arch/x86_64/src/common/x86_64_task_start.c b/arch/x86_64/src/common/x86_64_task_start.c new file mode 100644 index 0000000000..c70e999040 --- /dev/null +++ b/arch/x86_64/src/common/x86_64_task_start.c @@ -0,0 +1,74 @@ +/**************************************************************************** + * arch/x86_64/src/common/x86_64_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 "x86_64_internal.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[]) +{ + /* Let sys_call3() do all of the work */ + + sys_call3(SYS_task_start, (uintptr_t)taskentry, (uintptr_t)argc, + (uintptr_t)argv); + + PANIC(); +} + +#endif /* !CONFIG_BUILD_FLAT */ diff --git a/arch/x86_64/src/intel64/intel64_head.S b/arch/x86_64/src/intel64/intel64_head.S index d07bcdd482..b8174b503c 100644 --- a/arch/x86_64/src/intel64/intel64_head.S +++ b/arch/x86_64/src/intel64/intel64_head.S @@ -647,6 +647,16 @@ x86_64_syscall_entry: movq (8*REG_R11)(%rdi), %r11 movq (8*REG_RDI)(%rdi), %rdi +# ifdef CONFIG_BUILD_KERNEL + /* Do not return to RING3 if this is nested syscall */ + cmp %gs:X86_64_CPUPRIV_UVBASE_OFFSET, %rcx + jb syscall_no_ring3 + + /* Return to user code pointed in RCX */ + sysretq + +syscall_no_ring3: +# endif /* Return to address pointed in RCX - must be on stack */ pushq %rcx ret diff --git a/arch/x86_64/src/intel64/intel64_initialstate.c b/arch/x86_64/src/intel64/intel64_initialstate.c index 0b0ad138f5..b020174e03 100644 --- a/arch/x86_64/src/intel64/intel64_initialstate.c +++ b/arch/x86_64/src/intel64/intel64_initialstate.c @@ -156,17 +156,6 @@ void up_initial_state(struct tcb_s *tcb) xcp->regs[REG_GS] = 0; - /* Set supervisor- or user-mode, depending on how NuttX is configured and - * what kind of thread is being started. Disable FIQs in any event - * - * If the kernel build is not selected, then all threads run in - * supervisor-mode. - */ - -#ifdef CONFIG_BUILD_KERNEL -# error "Missing logic for the CONFIG_BUILD_KERNEL build" -#endif - /* Enable or disable interrupts, based on user configuration. If the IF * bit is set, maskable interrupts will be enabled. */ diff --git a/arch/x86_64/src/intel64/intel64_irq.c b/arch/x86_64/src/intel64/intel64_irq.c index a8e8cd345d..657dec1cd6 100644 --- a/arch/x86_64/src/intel64/intel64_irq.c +++ b/arch/x86_64/src/intel64/intel64_irq.c @@ -344,11 +344,12 @@ static void up_idtentry(unsigned int index, uint64_t base, uint16_t sel, entry->sel = sel; entry->zero = 0; - /* We must uncomment the OR below when we get to using user-mode. It sets - * the interrupt gate's privilege level to 3. + /* We don't use software interrupts from user-space (INT) so DPL level + * can be set to privilage level 0. DPL bits have no effect on hardware + * interrupts. */ - entry->flags = flags; /* | 0x60 */ + entry->flags = flags; } /**************************************************************************** diff --git a/boards/x86_64/intel64/qemu-intel64/scripts/Make.defs b/boards/x86_64/intel64/qemu-intel64/scripts/Make.defs index 2b2d675e37..ed40ccf546 100644 --- a/boards/x86_64/intel64/qemu-intel64/scripts/Make.defs +++ b/boards/x86_64/intel64/qemu-intel64/scripts/Make.defs @@ -24,4 +24,8 @@ include $(TOPDIR)/.config include $(TOPDIR)/tools/Config.mk include $(TOPDIR)/arch/x86_64/src/intel64/Toolchain.defs -ARCHSCRIPT += $(BOARD_DIR)$(DELIM)scripts$(DELIM)qemu.ld \ No newline at end of file +ifeq ($(CONFIG_BUILD_KERNEL),y) +ARCHSCRIPT += $(BOARD_DIR)$(DELIM)scripts$(DELIM)qemu-kernel.ld +else +ARCHSCRIPT += $(BOARD_DIR)$(DELIM)scripts$(DELIM)qemu.ld +endif diff --git a/boards/x86_64/intel64/qemu-intel64/scripts/qemu-kernel.ld b/boards/x86_64/intel64/qemu-intel64/scripts/qemu-kernel.ld new file mode 100644 index 0000000000..341bc0c44e --- /dev/null +++ b/boards/x86_64/intel64/qemu-intel64/scripts/qemu-kernel.ld @@ -0,0 +1,130 @@ +/**************************************************************************** + * boards/x86_64/intel64/qemu-intel64/scripts/qemu-kernel.ld + * + * 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. + * + ****************************************************************************/ + +OUTPUT_ARCH(i386:x86-64) +ENTRY(__pmode_entry) +SECTIONS +{ + + . = 0x0; + + .realmode : { + . = ALIGN(8); + KEEP(*(.multiboot)) + *(.realmode) + } + + . = 0x1M; + _kernel_physical_start = .; + + .loader.text : { + . = ALIGN(8); + *(.loader.text) + } + + .loader.rodata : { + *(.loader.rodata) + } + + .loader.data : { + *(.loader.data) + } + + .loader.bss : { + *(.loader.bss) + } + + . = ALIGN(0x1000); + _boot_end = .; + + . += 0x100000000; + _kernel_virtual_start = .; + + .text : AT(_boot_end) + { + _stext = ABSOLUTE(.); + . = ALIGN(8); + KEEP(*(.multiboot)) + *(.text .text.*) + *(.gnu.linkonce.t.*) + _etext = ABSOLUTE(.); + } + + .rodata ALIGN(0x1000) : + { + _srodata = ABSOLUTE(.); + *(.rodata .rodata.*) + *(.fixup) + *(.gnu.warning) + *(.glue_7) + *(.glue_7t) + *(.got) + *(.gcc_except_table) + *(.gnu.linkonce.r.*) + *(.eh_frame) + *(.note.gnu.*) + _erodata = ABSOLUTE(.); + } + + .data ALIGN(0x1000) : + { + _sdata = ABSOLUTE(.); + *(.data .data.*) + *(.gnu.linkonce.d.*) + CONSTRUCTORS + . = ALIGN(4); + _edata = ABSOLUTE(.); + } + + .pgheap ALIGN(0x1000) : + { + __pgheap_start = ABSOLUTE(.); + . = ALIGN(0x1000); + __pgheap_size = ABSOLUTE(.); + } + + .bss ALIGN(0x1000) : + { + _sbss = ABSOLUTE(.); + *(.bss .bss.*) + *(.gnu.linkonce.b.*) + *(COMMON) + . = ALIGN(16); + _ebss = ABSOLUTE(.); + } + + _kernel_virtual_end = .; + + _kernel_physical_end = (LOADADDR (.bss) + SIZEOF (.bss) + 0xFFF) & 0xFFFFFFFFFFFFF000; + + /* Stabs debugging sections */ + .stab 0 : { *(.stab) } + .stabstr 0 : { *(.stabstr) } + .stab.excl 0 : { *(.stab.excl) } + .stab.exclstr 0 : { *(.stab.exclstr) } + .stab.index 0 : { *(.stab.index) } + .stab.indexstr 0 : { *(.stab.indexstr) } + .comment 0 : { *(.comment) } + .debug_abbrev 0 : { *(.debug_abbrev) } + .debug_info 0 : { *(.debug_info) } + .debug_line 0 : { *(.debug_line) } + .debug_pubnames 0 : { *(.debug_pubnames) } + .debug_aranges 0 : { *(.debug_aranges) } +}
