| /* |
| * Copyright (c) 2010-2015 Wind River Systems, Inc. |
| * |
| * SPDX-License-Identifier: Apache-2.0 |
| */ |
| |
| /** |
| * @file |
| * @brief Thread support primitives |
| * |
| * This module provides core thread related primitives for the IA-32 |
| * processor architecture. |
| */ |
| |
| #ifdef CONFIG_INIT_STACKS |
| #include <string.h> |
| #endif /* CONFIG_INIT_STACKS */ |
| |
| #include <toolchain.h> |
| #include <linker/sections.h> |
| #include <kernel_structs.h> |
| #include <wait_q.h> |
| #include <mmustructs.h> |
| #include <misc/printk.h> |
| |
| /* forward declaration */ |
| |
| /* Some configurations require that the stack/registers be adjusted before |
| * _thread_entry. See discussion in swap.S for _x86_thread_entry_wrapper() |
| */ |
| #if defined(CONFIG_X86_IAMCU) |
| #define WRAPPER_REQUIRED |
| #endif |
| |
| #ifdef WRAPPER_REQUIRED |
| extern void _x86_thread_entry_wrapper(k_thread_entry_t entry, |
| void *p1, void *p2, void *p3); |
| #endif /* WRAPPER_REQUIRED */ |
| |
| |
| /* Initial thread stack frame, such that everything is laid out as expected |
| * for when _Swap() switches to it for the first time. |
| */ |
| struct _x86_initial_frame { |
| u32_t swap_retval; |
| u32_t ebp; |
| u32_t ebx; |
| u32_t esi; |
| u32_t edi; |
| void *_thread_entry; |
| u32_t eflags; |
| k_thread_entry_t entry; |
| void *p1; |
| void *p2; |
| void *p3; |
| }; |
| |
| /** |
| * @brief Create a new kernel execution thread |
| * |
| * Initializes the k_thread object and sets up initial stack frame. |
| * |
| * @param thread pointer to thread struct memory, including any space needed |
| * for extra coprocessor context |
| * @param stack the pointer to aligned stack memory |
| * @param stack_size the stack size in bytes |
| * @param entry thread entry point routine |
| * @param parameter1 first param to entry point |
| * @param parameter2 second param to entry point |
| * @param parameter3 third param to entry point |
| * @param priority thread priority |
| * @param options thread options: K_ESSENTIAL, K_FP_REGS, K_SSE_REGS |
| */ |
| void _new_thread(struct k_thread *thread, k_thread_stack_t *stack, |
| size_t stack_size, k_thread_entry_t entry, |
| void *parameter1, void *parameter2, void *parameter3, |
| int priority, unsigned int options) |
| { |
| char *stack_buf; |
| char *stack_high; |
| struct _x86_initial_frame *initial_frame; |
| |
| _ASSERT_VALID_PRIO(priority, entry); |
| stack_buf = K_THREAD_STACK_BUFFER(stack); |
| _new_thread_init(thread, stack_buf, stack_size, priority, options); |
| |
| #if CONFIG_X86_USERSPACE |
| if (!(options & K_USER)) { |
| /* Running in kernel mode, kernel stack region is also a guard |
| * page */ |
| _x86_mmu_set_flags((void *)(stack_buf - MMU_PAGE_SIZE), |
| MMU_PAGE_SIZE, MMU_ENTRY_NOT_PRESENT, |
| MMU_PTE_P_MASK); |
| } |
| #endif /* CONFIG_X86_USERSPACE */ |
| |
| #if CONFIG_X86_STACK_PROTECTION |
| _x86_mmu_set_flags(stack, MMU_PAGE_SIZE, MMU_ENTRY_NOT_PRESENT, |
| MMU_PTE_P_MASK); |
| #endif |
| |
| stack_high = (char *)STACK_ROUND_DOWN(stack_buf + stack_size); |
| |
| /* Create an initial context on the stack expected by _Swap() */ |
| initial_frame = (struct _x86_initial_frame *) |
| (stack_high - sizeof(struct _x86_initial_frame)); |
| /* _thread_entry() arguments */ |
| initial_frame->entry = entry; |
| initial_frame->p1 = parameter1; |
| initial_frame->p2 = parameter2; |
| initial_frame->p3 = parameter3; |
| /* initial EFLAGS; only modify IF and IOPL bits */ |
| initial_frame->eflags = (EflagsGet() & ~EFLAGS_MASK) | EFLAGS_INITIAL; |
| #ifdef CONFIG_X86_USERSPACE |
| if (options & K_USER) { |
| #ifdef WRAPPER_REQUIRED |
| initial_frame->edi = (u32_t)_arch_user_mode_enter; |
| initial_frame->_thread_entry = _x86_thread_entry_wrapper; |
| #else |
| initial_frame->_thread_entry = _arch_user_mode_enter; |
| #endif /* WRAPPER_REQUIRED */ |
| } else |
| #endif /* CONFIG_X86_USERSPACE */ |
| { |
| #ifdef WRAPPER_REQUIRED |
| initial_frame->edi = (u32_t)_thread_entry; |
| initial_frame->_thread_entry = _x86_thread_entry_wrapper; |
| #else |
| initial_frame->_thread_entry = _thread_entry; |
| #endif |
| } |
| /* Remaining _x86_initial_frame members can be garbage, _thread_entry() |
| * doesn't care about their state when execution begins |
| */ |
| thread->callee_saved.esp = (unsigned long)initial_frame; |
| |
| #if defined(CONFIG_FP_SHARING) |
| thread->arch.excNestCount = 0; |
| #endif /* CONFIG_FP_SHARING */ |
| #ifdef CONFIG_THREAD_MONITOR |
| thread->entry = (struct __thread_entry *)&initial_frame->entry; |
| thread_monitor_init(thread); |
| #endif |
| } |
| |
| #ifdef CONFIG_X86_USERSPACE |
| void _x86_swap_update_page_tables(struct k_thread *incoming, |
| struct k_thread *outgoing) |
| { |
| /* Outgoing thread stack no longer accessible */ |
| _x86_mmu_set_flags((void *)outgoing->stack_info.start, |
| ROUND_UP(outgoing->stack_info.size, MMU_PAGE_SIZE), |
| MMU_ENTRY_SUPERVISOR, MMU_PTE_US_MASK); |
| |
| |
| /* Userspace can now access the incoming thread's stack */ |
| _x86_mmu_set_flags((void *)incoming->stack_info.start, |
| ROUND_UP(incoming->stack_info.size, MMU_PAGE_SIZE), |
| MMU_ENTRY_USER, MMU_PTE_US_MASK); |
| |
| /* In case of privilege elevation, use the incoming thread's kernel |
| * stack, the top of the thread stack is the bottom of the kernel stack |
| */ |
| _main_tss.esp0 = incoming->stack_info.start; |
| |
| /* If either thread defines different memory domains, efficiently |
| * switch between them |
| */ |
| if (incoming->mem_domain_info.mem_domain != |
| outgoing->mem_domain_info.mem_domain){ |
| |
| /* Ensure that the outgoing mem domain configuration |
| * is set back to default state. |
| */ |
| _arch_mem_domain_destroy(outgoing->mem_domain_info.mem_domain); |
| _arch_mem_domain_configure(incoming); |
| } |
| } |
| |
| |
| FUNC_NORETURN void _arch_user_mode_enter(k_thread_entry_t user_entry, |
| void *p1, void *p2, void *p3) |
| { |
| u32_t stack_end; |
| |
| /* Transition will reset stack pointer to initial, discarding |
| * any old context since this is a one-way operation |
| */ |
| stack_end = STACK_ROUND_DOWN(_current->stack_info.start + |
| _current->stack_info.size); |
| |
| /* Set up the kernel stack used during privilege elevation */ |
| _x86_mmu_set_flags((void *)(_current->stack_info.start - MMU_PAGE_SIZE), |
| MMU_PAGE_SIZE, |
| (MMU_ENTRY_PRESENT | MMU_ENTRY_WRITE | |
| MMU_ENTRY_SUPERVISOR), |
| (MMU_PTE_P_MASK | MMU_PTE_RW_MASK | |
| MMU_PTE_US_MASK)); |
| |
| _x86_userspace_enter(user_entry, p1, p2, p3, stack_end, |
| _current->stack_info.start); |
| CODE_UNREACHABLE; |
| } |
| |
| |
| /* Implemented in userspace.S */ |
| extern void _x86_syscall_entry_stub(void); |
| |
| /* Syscalls invoked by 'int 0x80'. Installed in the IDT at DPL=3 so that |
| * userspace can invoke it. |
| */ |
| NANO_CPU_INT_REGISTER(_x86_syscall_entry_stub, -1, -1, 0x80, 3); |
| |
| #endif /* CONFIG_X86_USERSPACE */ |