241 lines
8.1 KiB
C
241 lines
8.1 KiB
C
|
/* Copyright (C) 2005 David Decotigny
|
||
|
Copyright (C) 2000-2004, The KOS team
|
||
|
|
||
|
Initially taken from SOS
|
||
|
*/
|
||
|
|
||
|
#include "assert.h"
|
||
|
#include "klibc.h"
|
||
|
#include "segment.h"
|
||
|
|
||
|
#include "cpu_context.h"
|
||
|
|
||
|
/**
|
||
|
* Here is the definition of a CPU context for IA32 processors. This
|
||
|
* is a Matos/SOS convention, not a specification given by the IA32
|
||
|
* spec. However there is a strong constraint related to the x86
|
||
|
* interrupt handling specification: the top of the stack MUST be
|
||
|
* compatible with the 'iret' instruction, ie there must be the
|
||
|
* err_code (might be 0), eip, cs and eflags of the destination
|
||
|
* context in that order (see Intel x86 specs vol 3, figure 5-4).
|
||
|
*
|
||
|
* @note IMPORTANT: This definition MUST be consistent with the way
|
||
|
* the registers are stored on the stack in
|
||
|
* irq_wrappers.S/exception_wrappers.S !!! Hence the constraint above.
|
||
|
*/
|
||
|
struct cpu_state {
|
||
|
/* (Lower addresses) */
|
||
|
|
||
|
/* These are Matos/SOS convention */
|
||
|
uint16_t gs;
|
||
|
uint16_t fs;
|
||
|
uint16_t es;
|
||
|
uint16_t ds;
|
||
|
uint16_t cpl0_ss; /* This is ALWAYS the Stack Segment of the
|
||
|
Kernel context (CPL0) of the interrupted
|
||
|
thread, even for a user thread */
|
||
|
uint16_t alignment_padding; /* unused */
|
||
|
uint32_t eax;
|
||
|
uint32_t ebx;
|
||
|
uint32_t ecx;
|
||
|
uint32_t edx;
|
||
|
uint32_t esi;
|
||
|
uint32_t edi;
|
||
|
uint32_t ebp;
|
||
|
|
||
|
/* MUST NEVER CHANGE (dependent on the IA32 iret instruction) */
|
||
|
uint32_t error_code;
|
||
|
vaddr_t eip;
|
||
|
uint32_t cs; /* 32bits according to the specs ! However, the CS
|
||
|
register is really 16bits long */
|
||
|
uint32_t eflags;
|
||
|
|
||
|
/* (Higher addresses) */
|
||
|
} __attribute__((packed));
|
||
|
|
||
|
/**
|
||
|
* The CS value pushed on the stack by the CPU upon interrupt, and
|
||
|
* needed by the iret instruction, is 32bits long while the real CPU
|
||
|
* CS register is 16bits only: this macro simply retrieves the CPU
|
||
|
* "CS" register value from the CS value pushed on the stack by the
|
||
|
* CPU upon interrupt.
|
||
|
*
|
||
|
* The remaining 16bits pushed by the CPU should be considered
|
||
|
* "reserved" and architecture dependent. IMHO, the specs don't say
|
||
|
* anything about them. Considering that some architectures generate
|
||
|
* non-zero values for these 16bits (at least Cyrix), we'd better
|
||
|
* ignore them.
|
||
|
*/
|
||
|
#define GET_CPU_CS_REGISTER_VALUE(pushed_ui32_cs_value) ((pushed_ui32_cs_value)&0xffff)
|
||
|
|
||
|
/**
|
||
|
* Structure of an interrupted Kernel thread's context
|
||
|
*/
|
||
|
struct cpu_kstate {
|
||
|
struct cpu_state regs;
|
||
|
} __attribute__((packed));
|
||
|
|
||
|
/**
|
||
|
* THE main operation of a kernel thread. This routine calls the
|
||
|
* kernel thread function start_func and calls exit_func when
|
||
|
* start_func returns.
|
||
|
*/
|
||
|
static void core_routine(cpu_kstate_function_arg1_t *start_func, void *start_arg,
|
||
|
cpu_kstate_function_arg1_t *exit_func, void *exit_arg)
|
||
|
__attribute__((noreturn));
|
||
|
|
||
|
static void core_routine(cpu_kstate_function_arg1_t *start_func, void *start_arg,
|
||
|
cpu_kstate_function_arg1_t *exit_func, void *exit_arg)
|
||
|
{
|
||
|
start_func(start_arg);
|
||
|
exit_func(exit_arg);
|
||
|
|
||
|
assert(!"The exit function of the thread should NOT return !");
|
||
|
for (;;)
|
||
|
;
|
||
|
}
|
||
|
|
||
|
int cpu_kstate_init(struct cpu_state **ctxt, cpu_kstate_function_arg1_t *start_func,
|
||
|
uint32_t start_arg, vaddr_t stack_bottom, size_t stack_size,
|
||
|
cpu_kstate_function_arg1_t *exit_func, uint32_t exit_arg)
|
||
|
{
|
||
|
/* We are initializing a Kernel thread's context */
|
||
|
struct cpu_kstate *kctxt;
|
||
|
|
||
|
/* This is a critical internal function, so that it is assumed that
|
||
|
the caller knows what he does: we legitimally assume that values
|
||
|
for ctxt, start_func, stack_* and exit_func are allways VALID ! */
|
||
|
|
||
|
/* Setup the stack.
|
||
|
*
|
||
|
* On x86, the stack goes downward. Each frame is configured this
|
||
|
* way (higher addresses first):
|
||
|
*
|
||
|
* - (optional unused space. As of gcc 3.3, this space is 24 bytes)
|
||
|
* - arg n
|
||
|
* - arg n-1
|
||
|
* - ...
|
||
|
* - arg 1
|
||
|
* - return instruction address: The address the function returns to
|
||
|
* once finished
|
||
|
* - local variables
|
||
|
*
|
||
|
* The remaining of the code should be read from the end upward to
|
||
|
* understand how the processor will handle it.
|
||
|
*/
|
||
|
|
||
|
vaddr_t tmp_vaddr = stack_bottom + stack_size;
|
||
|
uint32_t *stack = (uint32_t *)tmp_vaddr;
|
||
|
|
||
|
/* If needed, poison the stack */
|
||
|
#ifdef CPU_STATE_DETECT_UNINIT_KERNEL_VARS
|
||
|
memset((void *)stack_bottom, CPU_STATE_STACK_POISON, stack_size);
|
||
|
#elif defined(CPU_STATE_DETECT_KERNEL_STACK_OVERFLOW)
|
||
|
cpu_state_prepare_detect_kernel_stack_overflow(stack_bottom, stack_size);
|
||
|
#endif
|
||
|
|
||
|
/* Simulate a call to the core_routine() function: prepare its
|
||
|
arguments */
|
||
|
*(--stack) = exit_arg;
|
||
|
*(--stack) = (uint32_t)exit_func;
|
||
|
*(--stack) = start_arg;
|
||
|
*(--stack) = (uint32_t)start_func;
|
||
|
*(--stack) = 0; /* Return address of core_routine => force page fault */
|
||
|
|
||
|
/*
|
||
|
* Setup the initial context structure, so that the CPU will execute
|
||
|
* the function core_routine() once this new context has been
|
||
|
* restored on CPU
|
||
|
*/
|
||
|
|
||
|
/* Compute the base address of the structure, which must be located
|
||
|
below the previous elements */
|
||
|
tmp_vaddr = ((vaddr_t)stack) - sizeof(struct cpu_kstate);
|
||
|
kctxt = (struct cpu_kstate *)tmp_vaddr;
|
||
|
|
||
|
/* Initialize the CPU context structure */
|
||
|
memset(kctxt, 0x0, sizeof(struct cpu_kstate));
|
||
|
|
||
|
/* Tell the CPU context structure that the first instruction to
|
||
|
execute will be that of the core_routine() function */
|
||
|
kctxt->regs.eip = (uint32_t)core_routine;
|
||
|
|
||
|
/* Setup the segment registers */
|
||
|
kctxt->regs.cs = BUILD_SEGMENT_REG_VALUE(0, FALSE, SEG_KCODE); /* Code */
|
||
|
kctxt->regs.ds = BUILD_SEGMENT_REG_VALUE(0, FALSE, SEG_KDATA); /* Data */
|
||
|
kctxt->regs.es = BUILD_SEGMENT_REG_VALUE(0, FALSE, SEG_KDATA); /* Data */
|
||
|
kctxt->regs.cpl0_ss = BUILD_SEGMENT_REG_VALUE(0, FALSE, SEG_KDATA); /* Stack */
|
||
|
/* fs and gs unused for the moment. */
|
||
|
|
||
|
/* The newly created context is initially interruptible */
|
||
|
kctxt->regs.eflags = (1 << 9); /* set IF bit */
|
||
|
|
||
|
/* Finally, update the generic kernel/user thread context */
|
||
|
*ctxt = (struct cpu_state *)kctxt;
|
||
|
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
#if defined(CPU_STATE_DETECT_KERNEL_STACK_OVERFLOW)
|
||
|
void cpu_state_prepare_detect_kernel_stack_overflow(const struct cpu_state *ctxt,
|
||
|
vaddr_t stack_bottom, size_t stack_size)
|
||
|
{
|
||
|
(void)ctxt;
|
||
|
size_t poison_size = CPU_STATE_DETECT_KERNEL_STACK_OVERFLOW;
|
||
|
if (poison_size > stack_size)
|
||
|
poison_size = stack_size;
|
||
|
|
||
|
memset((void *)stack_bottom, CPU_STATE_STACK_POISON, poison_size);
|
||
|
}
|
||
|
|
||
|
void cpu_state_detect_kernel_stack_overflow(const struct cpu_state *ctxt, vaddr_t stack_bottom,
|
||
|
size_t stack_size)
|
||
|
{
|
||
|
unsigned char *c;
|
||
|
size_t i;
|
||
|
|
||
|
/* On Matos/SOS, "ctxt" corresponds to the address of the esp register of
|
||
|
the saved context in Kernel mode (always, even for the interrupted
|
||
|
context of a user thread). Here we make sure that this stack
|
||
|
pointer is within the allowed stack area */
|
||
|
assert(((vaddr_t)ctxt) >= stack_bottom);
|
||
|
assert(((vaddr_t)ctxt) + sizeof(struct cpu_kstate) <= stack_bottom + stack_size);
|
||
|
|
||
|
/* Check that the bottom of the stack has not been altered */
|
||
|
for (c = (unsigned char *)stack_bottom, i = 0;
|
||
|
(i < CPU_STATE_DETECT_KERNEL_STACK_OVERFLOW) && (i < stack_size); c++, i++) {
|
||
|
assert(CPU_STATE_STACK_POISON == *c);
|
||
|
}
|
||
|
}
|
||
|
#endif
|
||
|
|
||
|
/* =======================================================================
|
||
|
* Public Accessor functions
|
||
|
*/
|
||
|
|
||
|
vaddr_t cpu_context_get_PC(const struct cpu_state *ctxt)
|
||
|
{
|
||
|
assert(NULL != ctxt);
|
||
|
|
||
|
/* This is the PC of the interrupted context (ie kernel or user
|
||
|
context). */
|
||
|
return ctxt->eip;
|
||
|
}
|
||
|
|
||
|
vaddr_t cpu_context_get_SP(const struct cpu_state *ctxt)
|
||
|
{
|
||
|
assert(NULL != ctxt);
|
||
|
|
||
|
/* On Matos/SOS, "ctxt" corresponds to the address of the esp register of
|
||
|
the saved context in Kernel mode (always, even for the interrupted
|
||
|
context of a user thread). */
|
||
|
return (vaddr_t)ctxt;
|
||
|
}
|
||
|
|
||
|
void cpu_context_dump(const struct cpu_state *ctxt)
|
||
|
{
|
||
|
printf("CPU: eip=%x esp=%x eflags=%x cs=%x ds=%x ss=%x err=%x", (unsigned)ctxt->eip,
|
||
|
(unsigned)ctxt, (unsigned)ctxt->eflags, (unsigned)GET_CPU_CS_REGISTER_VALUE(ctxt->cs),
|
||
|
(unsigned)ctxt->ds, (unsigned)ctxt->cpl0_ss, (unsigned)ctxt->error_code);
|
||
|
}
|