zephyr/arch/x86/core/thread.c

208 lines
6.1 KiB
C
Raw Normal View History

/*
* 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>
kernel/arch: consolidate tTCS and TNANO definitions There was a lot of duplication between architectures for the definition of threads and the "nanokernel" guts. These have been consolidated. Now, a common file kernel/unified/include/kernel_structs.h holds the common definitions. Architectures provide two files to complement it: kernel_arch_data.h and kernel_arch_func.h. The first one contains at least the struct _thread_arch and struct _kernel_arch data structures, as well as the struct _callee_saved and struct _caller_saved register layouts. The second file contains anything that needs what is provided by the common stuff in kernel_structs.h. Those two files are only meant to be included in kernel_structs.h in very specific locations. The thread data structure has been separated into three major parts: common struct _thread_base and struct k_thread, and arch-specific struct _thread_arch. The first and third ones are included in the second. The struct s_NANO data structure has been split into two: common struct _kernel and arch-specific struct _kernel_arch. The latter is included in the former. Offsets files have also changed: nano_offsets.h has been renamed kernel_offsets.h and is still included by the arch-specific offsets.c. Also, since the thread and kernel data structures are now made of sub-structures, offsets have to be added to make up the full offset. Some of these additions have been consolidated in shorter symbols, available from kernel/unified/include/offsets_short.h, which includes an arch-specific offsets_arch_short.h. Most of the code include offsets_short.h now instead of offsets.h. Change-Id: I084645cb7e6db8db69aeaaf162963fe157045d5a Signed-off-by: Benjamin Walsh <benjamin.walsh@windriver.com>
2016-11-08 10:36:50 -05:00
#include <kernel_structs.h>
#include <wait_q.h>
#include <mmustructs.h>
#include <misc/printk.h>
/* forward declaration */
/* Initial thread stack frame, such that everything is laid out as expected
* for when z_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
unified/x86: add unified kernel support for x86 arch The x86 architecture port is fitted with support for the unified kernel, namely: - the interrupt exit code now calls _Swap() if the current thread is not a coop thread and if the scheduler is not locked - there is no 'task' fields in the _nanokernel anymore: _Swap() now calls _get_next_ready_thread instead - the _nanokernel.fiber field is replaced by a more sophisticated ready_q, based on the microkernel's priority-bitmap-based one - nano_private includes nano_internal.h from the unified directory - the FIBER, TASK and PREEMPTIBLE flags do not exist anymore: the thread priority drives the behaviour - the tcs uses a dlist for queuing in both ready and wait queues instead of a custom singly-linked list - other new fields in the tcs include a schedule-lock count, a back-pointer to init data (when the task is static) and a pointer to swap data, needed when a thread pending on _Swap() must be passed more then just one value (e.g. k_stack_pop() needs an error code and data) - fiberRtnValueSet() is aliased to _set_thread_return_value since it also operates on preempt threads now - _set_thread_return_value_with_data() sets the swap_data field in addition to a return value from _Swap() - convenience aliases are created for shorter names: - _current is defined as _nanokernel.current - _ready_q is defined as _nanokernel.ready_q - _Swap() sets the threads's return code to -EAGAIN before swapping out to prevent timeouts to have to set it (solves hard issues in some kernel objects). - Floating point support. Note that, in _Swap(), the register holding the thread to be swapped in has been changed from %ecx to %eax in both the legacy kernel and the unified kernel to take advantage of the fact that the return value of _get_next_ready_thread() is stored in %eax, and this avoids moving it to %ecx. Work by: Dmitriy Korovkin <dmitriy.korovkin@windriver.com> Allan Stephens <allan.stephens@windriver.com> Benjamin Walsh <benjamin.walsh@windriver.com> Change-Id: I4ce2bd47bcdc62034c669b5e889fc0f29480c43b Signed-off-by: Benjamin Walsh <benjamin.walsh@windriver.com>
2016-09-02 16:34:35 -04:00
* @param priority thread priority
* @param options thread options: K_ESSENTIAL, K_FP_REGS, K_SSE_REGS
*/
void z_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;
Z_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) == 0) {
/* Running in kernel mode, kernel stack region is also a guard
* page */
z_x86_mmu_set_flags(&z_x86_kernel_pdpt,
(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
z_x86_mmu_set_flags(&z_x86_kernel_pdpt, 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 z_swap() */
initial_frame = (struct _x86_initial_frame *)
(stack_high - sizeof(struct _x86_initial_frame));
/* z_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) != 0) {
#ifdef _THREAD_WRAPPER_REQUIRED
initial_frame->edi = (u32_t)z_arch_user_mode_enter;
initial_frame->thread_entry = _x86_thread_entry_wrapper;
#else
initial_frame->thread_entry = z_arch_user_mode_enter;
#endif /* _THREAD_WRAPPER_REQUIRED */
} else
#endif /* CONFIG_X86_USERSPACE */
{
#ifdef _THREAD_WRAPPER_REQUIRED
initial_frame->edi = (u32_t)z_thread_entry;
initial_frame->thread_entry = _x86_thread_entry_wrapper;
#else
initial_frame->thread_entry = z_thread_entry;
#endif
}
/* Remaining _x86_initial_frame members can be garbage, z_thread_entry()
* doesn't care about their state when execution begins
*/
thread->callee_saved.esp = (unsigned long)initial_frame;
#if defined(CONFIG_LAZY_FP_SHARING)
thread->arch.excNestCount = 0;
#endif /* CONFIG_LAZY_FP_SHARING */
}
#ifdef CONFIG_X86_USERSPACE
void _x86_swap_update_page_tables(struct k_thread *incoming,
struct k_thread *outgoing)
{
/* Outgoing thread stack no longer accessible */
z_x86_reset_pages((void *)outgoing->stack_info.start,
ROUND_UP(outgoing->stack_info.size, MMU_PAGE_SIZE));
/* Userspace can now access the incoming thread's stack */
z_x86_mmu_set_flags(&USER_PDPT,
(void *)incoming->stack_info.start,
ROUND_UP(incoming->stack_info.size, MMU_PAGE_SIZE),
MMU_ENTRY_PRESENT | K_MEM_PARTITION_P_RW_U_RW,
K_MEM_PARTITION_PERM_MASK | MMU_PTE_P_MASK);
#ifndef CONFIG_X86_KPTI
/* 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.
*
* If KPTI is enabled, then privilege elevation always lands on the
* trampoline stack and the irq/sycall code has to manually transition
* off of it to the thread's kernel stack after switching page
* tables.
*/
_main_tss.esp0 = incoming->stack_info.start;
#endif
/* 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.
*/
z_arch_mem_domain_destroy(outgoing->mem_domain_info.mem_domain);
z_arch_mem_domain_configure(incoming);
}
}
FUNC_NORETURN void z_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 */
z_x86_mmu_set_flags(&z_x86_kernel_pdpt,
(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 */