Linux preempt-rt

Check our new training course

Real-Time Linux with PREEMPT_RT

Check our new training course
with Creative Commons CC-BY-SA
lecture and lab materials

Bootlin logo

Elixir Cross Referencer

 * 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.

#include <string.h>

#include <toolchain.h>
#include <linker/sections.h>
#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 _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 (!(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),
#endif /* CONFIG_X86_USERSPACE */

	_x86_mmu_set_flags(stack, MMU_PAGE_SIZE, MMU_ENTRY_NOT_PRESENT,

	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;
	if (options & K_USER) {
		initial_frame->edi = (u32_t)_arch_user_mode_enter;
		initial_frame->_thread_entry = _x86_thread_entry_wrapper;
		initial_frame->_thread_entry = _arch_user_mode_enter;
	} else
#endif /* CONFIG_X86_USERSPACE */
		initial_frame->edi = (u32_t)_thread_entry;
		initial_frame->_thread_entry = _x86_thread_entry_wrapper;
		initial_frame->_thread_entry = _thread_entry;
	/* 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 */

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),

	/* 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),

	/* 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 !=

		 /* Ensure that the outgoing mem domain configuration
		  * is set back to default state.

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 +

	/* Set up the kernel stack used during privilege elevation */
	_x86_mmu_set_flags((void *)(_current->stack_info.start - MMU_PAGE_SIZE),

	_x86_userspace_enter(user_entry, p1, p2, p3, stack_end,

/* 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 */