matos/core/cpu_context.h
2024-01-29 23:27:10 +01:00

232 lines
8.5 KiB
C

/* Copyright (C) 2021 Mathieu Maret
Copyright (C) 2005 David Decotigny
Copyright (C) 2000-2004, The KOS team
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
USA.
*/
#pragma once
/**
* @file cpu_context.h
*
* Low level API to manage kernel and user thread CPU contexts. Should
* be some kind of architecture-independent.
*/
#include "errno.h"
#include "stddef.h"
#include "stdint.h"
#include "types.h"
/**
* Opaque structure storing the CPU context of an inactive kernel or
* user thread, as saved by the low level primitives below or by the
* interrupt/exception handlers.
*
* @note This is an (architecture-independent) forward declaration:
* see cpu_context.c and the *.S files for its
* (architecture-dependent) definition.
*/
struct cpu_state;
/**
* The type of the functions passed as arguments to the Kernel thread
* related functions.
*/
typedef void(cpu_kstate_function_arg1_t(void *arg1));
/**
* Function to create an initial context for a kernel thread starting
* its execution at function start_func with the argument initial_arg,
* and having the stack defined by stack_bottom/stack_size. When the
* start_func function returns, the function exit_func is called with
* argument exit_arg.
*
* @param kctxt The kernel thread CPU context to initialize. The
* address of the newly-initialized struct cpu_state will be
* stored in this variable. The contents of this struct cpu_state
* are actually located /inside/ the stack.
*
* @param start_func The address of the first instruction that will be
* executed when this context will be first transferred on
* CPU. Practically speaking, this is the address of a function that
* is assumed to take 1 argument.
*
* @param start_arg The value that will be passed as the argument to
* start_func when the thread starts. The stack will be setup
* accordingly to simulate a real call to the function and really
* passing this arguement.
*
* @param stack_bottom The lowest address of the stack.
*
* @param stack_size The size of the stack.
*
* @param exit_func The address of the instruction executed after the
* function start_func has returned. This function takes 1 parameter
* as argument: exit_arg.
*
* @param exit_arg The argument passed to the function exit_func.
*
* @note the newly created context is INTERRUPTIBLE by default !
*/
int cpu_kstate_init(struct cpu_state **kctxt, cpu_kstate_function_arg1_t *start_func,
vaddr_t start_arg, vaddr_t stack_bottom, size_t stack_size,
cpu_kstate_function_arg1_t *exit_func, vaddr_t exit_arg);
/**
* Prepare the system to deal with multiple CPU execution contexts
*/
int cpu_context_subsystem_setup();
/**
* Function that performs an immediate context-switch from one
* kernel/user thread to another one. It stores the current executing
* context in from_ctxt, and restores to_context on CPU.
*
* @param from_ctxt The address of the struct cpu_state will be
* stored in this variable. Must NOT be NULL.
*
* @param to_ctxt The CPU will resume its execution with the struct
* cpu_state located at this address. Must NOT be NULL.
*/
void cpu_context_switch(struct cpu_state **from_ctxt, struct cpu_state *to_ctxt);
/*
* Switch to the new given context (of a kernel/user thread) without
* saving the old context (of another kernel/user thread), and call
* the function reclaiming_func passing it the recalining_arg
* argument. The reclaining function is called from within the stack
* of the new context, so that it can (among other things) safely
* destroy the stack of the former context.
*
* @param switch_to_ctxt The context that will be restored on the CPU
*
* @param reclaiming_func The address of the function that will be
* called after having changed the stack, but before restoring the CPU
* context to switch_to_ctxt.
*/
void cpu_context_exit_to(struct cpu_state *switch_to_ctxt,
cpu_kstate_function_arg1_t *reclaiming_func, uint32_t reclaiming_arg)
__attribute__((noreturn));
/* =======================================================================
* Public Accessor functions
*/
/**
* Return whether the saved context was in kernel or user context
*
* @return TRUE when context was interrupted when in user mode, FALSE
* when in kernel mode, < 0 on error.
*/
int cpu_context_is_in_user_mode(const struct cpu_state *ctxt);
/**
* Return Program Counter stored in the saved kernel/user context
*/
vaddr_t cpu_context_get_PC(const struct cpu_state *ctxt);
/**
* Return Stack Pointer stored in the saved kernel/user context
*/
vaddr_t cpu_context_get_SP(const struct cpu_state *ctxt);
/**
* Dump the contents of the CPU context (bochs + x86_videomem)
*/
void cpu_context_dump(const struct cpu_state *ctxt);
/* =======================================================================
* Public Accessor functions TO BE USED ONLY BY Exception handlers
*/
/**
* Return the argument passed by the CPU upon exception, as stored in the
* saved context
*/
uint32_t cpu_context_get_EX_err(const struct cpu_state *ctxt);
/**
* Return the faulting address of the exception
*/
vaddr_t cpu_context_get_EX_faulting_vaddr(const struct cpu_state *ctxt);
/* =======================================================================
* Macros controlling stack poisoning.
* Stack poisoning can be used to detect:
* - unitialized local variables
* - when the thread might have gone too deep in the stack
*/
/** The signature of the poison */
#define CPU_STATE_STACK_POISON 0xa5
/**
* When set, mean that the whole stack is poisoned to detect use of
* unititialized variables
*/
#define CPU_STATE_DETECT_UNINIT_KERNEL_VARS
/* #undef CPU_STATE_DETECT_UNINIT_KERNEL_VARS */
/**
* When set, mean that the bottom of the stack is poisoned to detect
* probable stack overflow. Its value indicates the number of bytes
* used for this detection.
*/
#define CPU_STATE_DETECT_KERNEL_STACK_OVERFLOW 64
/* #undef CPU_STATE_DETECT_KERNEL_STACK_OVERFLOW */
#if defined(CPU_STATE_DETECT_KERNEL_STACK_OVERFLOW)
void cpu_state_prepare_detect_kernel_stack_overflow(const struct cpu_state *ctxt,
vaddr_t kernel_stack_bottom,
size_t kernel_stack_size);
void cpu_state_detect_kernel_stack_overflow(const struct cpu_state *ctxt,
vaddr_t kernel_stack_bottom,
size_t kernel_stack_size);
#else
#define cpu_state_prepare_detect_kernel_stack_overflow(ctxt, stkbottom, stksize) \
({/* nop \
*/})
#define cpu_state_detect_kernel_stack_overflow(ctxt, stkbottom, stksize) ({/* nop */})
#endif
int cpu_ustate_init(struct cpu_state **ctx, uaddr_t startPC, uint32_t arg1, uint32_t arg2,
uaddr_t startSP, vaddr_t kernelStackBottom, size_t kernelStackSize);
int syscallGet1arg(const struct cpu_state *user_ctxt,
/* out */ unsigned int *arg1);
int syscallGet2args(const struct cpu_state *user_ctxt,
/* out */ unsigned int *arg1,
/* out */ unsigned int *arg2);
int syscallGet3args(const struct cpu_state *user_ctxt,
/* out */ unsigned int *arg1,
/* out */ unsigned int *arg2,
/* out */ unsigned int *arg3);
int syscallGet4args(const struct cpu_state *user_ctxt,
/* out */ unsigned int *arg1,
/* out */ unsigned int *arg2,
/* out */ unsigned int *arg3,
/* out */ unsigned int *arg4);
int syscallGet5args(const struct cpu_state *user_ctxt,
/* out */ unsigned int *arg1,
/* out */ unsigned int *arg2,
/* out */ unsigned int *arg3,
/* out */ unsigned int *arg4,
/* out */ unsigned int *arg5);