|
| ||
|
| ||
hwcore/irq.o hwcore/irq_wrappers.o hwcore/i8259.o \ | hwcore/irq.o hwcore/irq_wrappers.o hwcore/i8259.o \ | ||
hwcore/paging.o \ | hwcore/paging.o \ | ||
hwcore/i8254.o drivers/x86_videomem.o drivers/bochs.o \ | hwcore/i8254.o drivers/x86_videomem.o drivers/bochs.o \ | ||
hwcore/cpu_context.o hwcore/cpu_context_switch.o \ | |||
sos/kmem_vmm.o sos/kmem_slab.o sos/kmalloc.o \ | sos/kmem_vmm.o sos/kmem_slab.o sos/kmalloc.o \ | ||
sos/physmem.o sos/klibc.o sos/main.o | sos/physmem.o sos/klibc.o \ | ||
sos/assert.o sos/main.o | |||
KERNEL_OBJ = sos.elf | KERNEL_OBJ = sos.elf | ||
MULTIBOOT_IMAGE = fd.img | MULTIBOOT_IMAGE = fd.img | ||
|
| ||
$(KERNEL_OBJ): $(OBJECTS) ./support/sos.lds | $(KERNEL_OBJ): $(OBJECTS) ./support/sos.lds | ||
$(LD) $(LDFLAGS) -T ./support/sos.lds -o $@ $(OBJECTS) | $(LD) $(LDFLAGS) -T ./support/sos.lds -o $@ $(OBJECTS) | ||
-nm -C $@ | cut -d ' ' -f 1,3 > sos.map | -nm -C $@ | cut -d ' ' -f 1,3 > sos.map | ||
size $@ | |||
-include .mkvars | -include .mkvars | ||
|
| ||
|
| ||
SOS -- Simple OS | SOS -- Simple OS | ||
Copyright (C) 2003,2004 The SOS Team (David Decotigny & Thomas Petazzoni) | Copyright (C) 2003,2004,2005 The SOS Team (David Decotigny & Thomas Petazzoni) | ||
Version "Article 5" -- Kernel space virtual memory allocator (Slab system) | Version "Article 6 (1st part)" -- Low-level kernel thread management | ||
This program is free software; you can redistribute it and/or | This program is free software; you can redistribute it and/or | ||
modify it under the terms of the GNU General Public License | modify it under the terms of the GNU General Public License | ||
|
| ||
|
| ||
/* checksum= */ .long -(MULTIBOOT_HEADER_MAGIC \ | /* checksum= */ .long -(MULTIBOOT_HEADER_MAGIC \ | ||
+MULTIBOOT_HEADER_FLAGS) | +MULTIBOOT_HEADER_FLAGS) | ||
/* header_addr= */ .long multiboot_header | /* header_addr= */ .long multiboot_header | ||
/* load_addr= */ .long __b_kernel | /* load_addr= */ .long __b_load | ||
/* bss_end_addr= */ .long __e_kernel | /* bss_end_addr= */ .long __e_kernel | ||
/* entry_addr= */ .long multiboot_entry | /* entry_addr= */ .long multiboot_entry | ||
|
| ||
hlt | hlt | ||
jmp loop | jmp loop | ||
/* Here is the stack */ | /* Here is the stack */ | ||
.comm stack, MULTIBOOT_STACK_SIZE | .section ".init_stack", "aw", @nobits | ||
.size stack, MULTIBOOT_STACK_SIZE | |||
stack: | |||
.space MULTIBOOT_STACK_SIZE | |||
/* Some data characterizing the stack addresses */ | |||
.data | |||
.globl bootstrap_stack_bottom | |||
bootstrap_stack_bottom: .long stack | |||
.globl bootstrap_stack_size | |||
bootstrap_stack_size: .long MULTIBOOT_STACK_SIZE |
|
| ||
|
| ||
#endif | #endif | ||
#ifndef ASM | #ifndef ASM | ||
/* Do not include here in boot.S. */ | /* Do not include here in the assembler sources. */ | ||
#include <sos/types.h> | |||
/* The address of the stack of the bootstrap thread */ | |||
extern sos_vaddr_t bootstrap_stack_bottom; | |||
extern sos_size_t bootstrap_stack_size; | |||
/* Types. */ | /* Types. */ | ||
|
| ||
|
| ||
return SOS_OK; | return SOS_OK; | ||
} | } | ||
#define _putchar(chr) \ | |||
#define sos_bochs_putchar(chr) \ | |||
sos_ret_t sos_bochs_putchar(char c) | |||
{ | |||
_putchar(c); | |||
return SOS_OK; | |||
} | |||
sos_ret_t sos_bochs_putstring(const char* str) | sos_ret_t sos_bochs_putstring(const char* str) | ||
{ | { | ||
for ( ; str && (*str != '\0') ; str++) | for ( ; str && (*str != '\0') ; str++) | ||
sos_bochs_putchar(*str); | _putchar(*str); | ||
return SOS_OK; | return SOS_OK; | ||
} | } | ||
{ | { | ||
unsigned c; | unsigned c; | ||
#define BOCHS_PRTHEX(q) \ | #define BOCHS_PRTHEX(q) \ | ||
({ unsigned char r; if ((q) >= 10) r='a'+(q)-10; \ | ({ unsigned char r; if ((q) >= 10) r='a'+(q)-10; \ | ||
else r='0'+(q); sos_bochs_putchar(r); }) | else r='0'+(q); _putchar(r); }) | ||
switch (nbytes) | switch (nbytes) | ||
{ | { | ||
|
| ||
|
| ||
sos_ret_t sos_bochs_setup(void); | sos_ret_t sos_bochs_setup(void); | ||
sos_ret_t sos_bochs_putchar(char c); | |||
sos_ret_t sos_bochs_putstring(const char* str); | sos_ret_t sos_bochs_putstring(const char* str); | ||
/** Print the least signficant 32 (nbytes == 4), 24 (nbytes == 3), 16 | /** Print the least signficant 32 (nbytes == 4), 24 (nbytes == 3), 16 | ||
|
| ||
|
| ||
/* | /* | ||
* @(#) $Id: bootsect.S,v 1.6 2004/06/18 07:43:51 d2 Exp $ | * @(#) $Id: bootsect.S,v 1.8 2004/11/20 16:00:11 d2 Exp $ | ||
* Auteurs : Thomas Petazzoni & Fabrice Gautier & Emmanuel Marty | * Auteurs : Thomas Petazzoni & Fabrice Gautier & Emmanuel Marty | ||
* Jerome Petazzoni & Bernard Cassagne & coffeeman | * Jerome Petazzoni & Bernard Cassagne & coffeeman | ||
|
| ||
* sos_bsect.lds ***/ | * sos_bsect.lds ***/ | ||
/* La pile de 16k qu'on utilise au niveau de LaunchKernel se trouve | /* La pile de 16k qu'on utilise au niveau de LaunchKernel se trouve | ||
declaree avec le noyau, dans sa section ".bss", cad HORS du boot | declaree avec le noyau, dans sa section ".init_stack", cad HORS du boot | ||
définir directement dans le sos_bsect.lds, ou dans un fichier .c | définir directement dans le sos_bsect.lds, ou dans un fichier .c | ||
auxiliaire pour plus de clarté */ | auxiliaire pour plus de clarté */ | ||
.comm stack, BOOT_STACK_SIZE | /* Here is the stack */ | ||
.section ".init_stack", "aw", @nobits | |||
.p2align 4 | |||
.size stack, BOOT_STACK_SIZE | |||
stack: | |||
.space BOOT_STACK_SIZE | |||
/* Some data characterizing the stack addresses */ | |||
.data | |||
.globl bootstrap_stack_bottom | |||
bootstrap_stack_bottom: .long stack | |||
.globl bootstrap_stack_size | |||
bootstrap_stack_size: .long BOOT_STACK_SIZE |
|
| ||
|
| ||
/* Copyright (C) 2000-2004, The KOS team | |||
Copyright (C) 1999 Free Software Foundation, Inc. | |||
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. | |||
*/ | |||
#include <sos/assert.h> | |||
#include <sos/klibc.h> | |||
#include <drivers/bochs.h> | |||
#include <drivers/x86_videomem.h> | |||
#include <hwcore/segment.h> | |||
#include "cpu_context.h" | |||
/** | |||
* Here is the definition of a CPU context for IA32 processors. This | |||
* is a 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 sos_cpu_kstate { | |||
/* (Lower addresses) */ | |||
/* These are SOS convention */ | |||
sos_ui16_t gs; | |||
sos_ui16_t fs; | |||
sos_ui16_t es; | |||
sos_ui16_t ds; | |||
sos_ui16_t ss; | |||
sos_ui16_t alignment_padding; /* unused */ | |||
sos_ui32_t eax; | |||
sos_ui32_t ebx; | |||
sos_ui32_t ecx; | |||
sos_ui32_t edx; | |||
sos_ui32_t esi; | |||
sos_ui32_t edi; | |||
sos_ui32_t ebp; | |||
/* MUST NEVER CHANGE (dependent on the IA32 iret instruction) */ | |||
sos_ui32_t error_code; | |||
sos_vaddr_t eip; | |||
sos_ui32_t cs; | |||
sos_ui32_t eflags; | |||
/* (Higher addresses) */ | |||
} __attribute__((packed)); | |||
static void core_routine (sos_cpu_kstate_function_arg1_t *start_func, | |||
sos_ui32_t start_arg, | |||
sos_cpu_kstate_function_arg1_t *exit_func, | |||
sos_ui32_t exit_arg) | |||
__attribute__((noreturn)); | |||
static void core_routine (sos_cpu_kstate_function_arg1_t *start_func, | |||
sos_ui32_t start_arg, | |||
sos_cpu_kstate_function_arg1_t *exit_func, | |||
sos_ui32_t exit_arg) | |||
{ | |||
start_func(start_arg); | |||
exit_func(exit_arg); | |||
SOS_ASSERT_FATAL(! "The exit function of the thread should NOT return !"); | |||
for(;;); | |||
} | |||
sos_ret_t sos_cpu_kstate_init(struct sos_cpu_kstate **ctxt, | |||
sos_cpu_kstate_function_arg1_t *start_func, | |||
sos_ui32_t start_arg, | |||
sos_vaddr_t stack_bottom, | |||
sos_size_t stack_size, | |||
sos_cpu_kstate_function_arg1_t *exit_func, | |||
sos_ui32_t exit_arg) | |||
{ | |||
/* 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. | |||
*/ | |||
sos_vaddr_t tmp_vaddr = stack_bottom + stack_size; | |||
sos_ui32_t *stack = (sos_ui32_t*)tmp_vaddr; | |||
/* If needed, poison the stack */ | |||
#ifdef SOS_CPU_KSTATE_DETECT_UNINIT_VARS | |||
memset((void*)stack_bottom, SOS_CPU_KSTATE_STACK_POISON, stack_size); | |||
#elif defined(SOS_CPU_KSTATE_DETECT_STACK_OVERFLOW) | |||
sos_cpu_kstate_prepare_detect_stack_overflow(stack_bottom, stack_size); | |||
#endif | |||
/* Simulate a call to the core_routine() function: prepare its | |||
arguments */ | |||
*(--stack) = exit_arg; | |||
*(--stack) = (sos_ui32_t)exit_func; | |||
*(--stack) = start_arg; | |||
*(--stack) = (sos_ui32_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 = ((sos_vaddr_t)stack) - sizeof(struct sos_cpu_kstate); | |||
*ctxt = (struct sos_cpu_kstate*)tmp_vaddr; | |||
/* Initialize the CPU context structure */ | |||
memset(*ctxt, 0x0, sizeof(struct sos_cpu_kstate)); | |||
/* Tell the CPU context structure that the first instruction to | |||
execute will be that of the core_routine() function */ | |||
(*ctxt)->eip = (sos_ui32_t)core_routine; | |||
/* Setup the segment registers */ | |||
(*ctxt)->cs = SOS_BUILD_SEGMENT_REG_VALUE(0, 0, SOS_SEG_KCODE); /* Code */ | |||
(*ctxt)->ds = SOS_BUILD_SEGMENT_REG_VALUE(0, 0, SOS_SEG_KDATA); /* Data */ | |||
(*ctxt)->es = SOS_BUILD_SEGMENT_REG_VALUE(0, 0, SOS_SEG_KDATA); /* Data */ | |||
(*ctxt)->ss = SOS_BUILD_SEGMENT_REG_VALUE(0, 0, SOS_SEG_KDATA); /* Stack */ | |||
/* fs and gs unused for the moment. */ | |||
/* The newly created context is initially interruptible */ | |||
(*ctxt)->eflags = (1 << 9); /* set IF bit */ | |||
return SOS_OK; | |||
} | |||
#if defined(SOS_CPU_KSTATE_DETECT_STACK_OVERFLOW) | |||
void | |||
sos_cpu_kstate_prepare_detect_stack_overflow(const struct sos_cpu_kstate *ctxt, | |||
sos_vaddr_t stack_bottom, | |||
sos_size_t stack_size) | |||
{ | |||
sos_size_t poison_size = SOS_CPU_KSTATE_DETECT_STACK_OVERFLOW; | |||
if (poison_size > stack_size) | |||
poison_size = stack_size; | |||
memset((void*)stack_bottom, SOS_CPU_KSTATE_STACK_POISON, poison_size); | |||
} | |||
void | |||
sos_cpu_kstate_detect_stack_overflow(const struct sos_cpu_kstate *ctxt, | |||
sos_vaddr_t stack_bottom, | |||
sos_size_t stack_size) | |||
{ | |||
unsigned char *c; | |||
int i; | |||
SOS_ASSERT_FATAL(((sos_vaddr_t)ctxt) >= stack_bottom); | |||
SOS_ASSERT_FATAL(((sos_vaddr_t)ctxt) + sizeof(struct sos_cpu_kstate) | |||
<= stack_bottom + stack_size); | |||
for (c = (unsigned char*) stack_bottom, i = 0 ; | |||
(i < SOS_CPU_KSTATE_DETECT_STACK_OVERFLOW) && (i < stack_size) ; | |||
c++, i++) | |||
{ | |||
SOS_ASSERT_FATAL(SOS_CPU_KSTATE_STACK_POISON == *c); | |||
} | |||
} | |||
#endif | |||
sos_vaddr_t sos_cpu_kstate_get_PC(const struct sos_cpu_kstate *ctxt) | |||
{ | |||
SOS_ASSERT_FATAL(NULL != ctxt); | |||
return ctxt->eip; | |||
} | |||
sos_vaddr_t sos_cpu_kstate_get_SP(const struct sos_cpu_kstate *ctxt) | |||
{ | |||
SOS_ASSERT_FATAL(NULL != ctxt); | |||
return (sos_vaddr_t)ctxt; | |||
} | |||
void sos_cpu_kstate_dump(const struct sos_cpu_kstate *ctxt) | |||
{ | |||
char buf[128]; | |||
snprintf(buf, sizeof(buf), | |||
"CPU: eip=%x esp=%x eflags=%x cs=%x ds=%x ss=%x err=%x", | |||
(unsigned)ctxt->eip, (unsigned)ctxt, (unsigned)ctxt->eflags, | |||
(unsigned)ctxt->cs, (unsigned)ctxt->ds, (unsigned)ctxt->ss, | |||
(unsigned)ctxt->error_code); | |||
sos_bochs_putstring(buf); sos_bochs_putstring("\n"); | |||
sos_x86_videomem_putstring(23, 0, | |||
SOS_X86_VIDEO_FG_BLACK | SOS_X86_VIDEO_BG_LTGRAY, | |||
buf); | |||
} | |||
sos_ui32_t sos_cpu_kstate_get_EX_info(const struct sos_cpu_kstate *ctxt) | |||
{ | |||
SOS_ASSERT_FATAL(NULL != ctxt); | |||
return ctxt->error_code; | |||
} | |||
sos_vaddr_t | |||
sos_cpu_kstate_get_EX_faulting_vaddr(const struct sos_cpu_kstate *ctxt) | |||
{ | |||
sos_ui32_t cr2; | |||
/* See Intel Vol 3 (section 5.14): the address of the faulting | |||
virtual address of a page fault is stored in the cr2 register */ | |||
asm volatile ("movl %%cr2, %0" | |||
:"=r"(cr2) | |||
: ); | |||
return cr2; | |||
} | |||
sos_ui32_t sos_backtrace(const struct sos_cpu_kstate *cpu_kstate, | |||
sos_ui32_t max_depth, | |||
sos_vaddr_t stack_bottom, | |||
sos_size_t stack_size, | |||
sos_backtrace_callback_t * backtracer, | |||
void *custom_arg) | |||
{ | |||
int depth; | |||
sos_vaddr_t callee_PC, caller_frame; | |||
/* | |||
* Layout of a frame on the x86 (compiler=gcc): | |||
* | |||
* funcA calls funcB calls funcC | |||
* | |||
* .... | |||
* funcB Argument 2 | |||
* funcB Argument 1 | |||
* funcA Return eip | |||
* frameB: funcA ebp (ie previous stack frame) | |||
* .... | |||
* (funcB local variables) | |||
* .... | |||
* funcC Argument 2 | |||
* funcC Argument 1 | |||
* funcB Return eip | |||
* frameC: funcB ebp (ie previous stack frame == A0) <---- a frame address | |||
* .... | |||
* (funcC local variables) | |||
* .... | |||
* | |||
* The presence of "ebp" on the stack depends on 2 things: | |||
* + the compiler is gcc | |||
* + the source is compiled WITHOUT the -fomit-frame-pointer option | |||
* In the absence of "ebp", chances are high that the value pushed | |||
* at that address is outside the stack boundaries, meaning that the | |||
* function will return -SOS_ENOSUP. | |||
*/ | |||
if (cpu_kstate) | |||
{ | |||
callee_PC = cpu_kstate->eip; | |||
caller_frame = cpu_kstate->ebp; | |||
} | |||
else | |||
{ | |||
/* Skip the sos_backtrace() frame */ | |||
callee_PC = (sos_vaddr_t)__builtin_return_address(0); | |||
caller_frame = (sos_vaddr_t)__builtin_frame_address(1); | |||
} | |||
for(depth=0 ; depth < max_depth ; depth ++) | |||
{ | |||
/* Call the callback */ | |||
backtracer(callee_PC, caller_frame + 8, depth, custom_arg); | |||
/* If the frame address is funky, don't go further */ | |||
if ( (caller_frame < stack_bottom) | |||
|| (caller_frame + 4 >= stack_bottom + stack_size) ) | |||
return depth; | |||
/* Go to caller frame */ | |||
callee_PC = *((sos_vaddr_t*) (caller_frame + 4)); | |||
caller_frame = *((sos_vaddr_t*) caller_frame); | |||
} | |||
return depth; | |||
} | |||
|
| ||
|
| ||
/* Copyright (C) 2000-2004, The KOS team | |||
Copyright (C) 1999 Free Software Foundation, Inc. | |||
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. | |||
*/ | |||
#ifndef _SOS_CPUCTXT_H_ | |||
#define _SOS_CPUCTXT_H_ | |||
/** | |||
* @file cpu_context.h | |||
* | |||
* Low level API to manage kernel and user thread CPU contexts. Should | |||
* be some kind of architecture-independent. | |||
*/ | |||
#include <sos/types.h> | |||
#include <sos/errno.h> | |||
/** | |||
* Opaque structure storing the CPU context of an inactive kernel | |||
* 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 sos_cpu_kstate; | |||
/** | |||
* The type of the functions passed as arguments below | |||
*/ | |||
typedef void (sos_cpu_kstate_function_arg1_t(sos_ui32_t 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 ctxt The kernel thread CPU context to initialize. The | |||
* address of the newly-initialized struct sos_cpu_kstate will be | |||
* stored in this variable. The contents of this struct sos_cpu_kstate | |||
* 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 ! | |||
*/ | |||
sos_ret_t sos_cpu_kstate_init(struct sos_cpu_kstate **ctxt, | |||
sos_cpu_kstate_function_arg1_t *start_func, | |||
sos_ui32_t start_arg, | |||
sos_vaddr_t stack_bottom, | |||
sos_size_t stack_size, | |||
sos_cpu_kstate_function_arg1_t *exit_func, | |||
sos_ui32_t exit_arg); | |||
/** | |||
* Function that performs an immediate context-switch from one kernel | |||
* 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 sos_cpu_kstate will be | |||
* stored in this variable. Must NOT be NULL. | |||
* | |||
* @param to_ctxt The CPU will resume its execution with the struct | |||
* sos_cpu_kstate located at this address. Must NOT be NULL. | |||
*/ | |||
void sos_cpu_kstate_switch(struct sos_cpu_kstate **from_ctxt, | |||
struct sos_cpu_kstate *to_ctxt); | |||
/* | |||
* Switch to the new given context (of a kernel thread) without saving | |||
* the old context (of another kernel 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 | |||
sos_cpu_kstate_exit_to(struct sos_cpu_kstate *switch_to_ctxt, | |||
sos_cpu_kstate_function_arg1_t *reclaiming_func, | |||
sos_ui32_t reclaiming_arg) __attribute__((noreturn)); | |||
/* ======================================================================= | |||
* Public Accessor functions | |||
*/ | |||
/** | |||
* Return Program Counter stored in the saved context | |||
*/ | |||
sos_vaddr_t sos_cpu_kstate_get_PC(const struct sos_cpu_kstate *ctxt); | |||
/** | |||
* Return Stack Pointer stored in the saved context | |||
*/ | |||
sos_vaddr_t sos_cpu_kstate_get_SP(const struct sos_cpu_kstate *ctxt); | |||
/** | |||
* Dump the contents of the CPU context (bochs + x86_videomem) | |||
*/ | |||
void sos_cpu_kstate_dump(const struct sos_cpu_kstate *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 | |||
*/ | |||
sos_ui32_t sos_cpu_kstate_get_EX_info(const struct sos_cpu_kstate *ctxt); | |||
/** | |||
* Return the faulting address of the exception | |||
*/ | |||
sos_vaddr_t | |||
sos_cpu_kstate_get_EX_faulting_vaddr(const struct sos_cpu_kstate *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 SOS_CPU_KSTATE_STACK_POISON 0xa5 | |||
/** | |||
* When set, mean that the whole stack is poisoned to detect use of | |||
* unititialized variables | |||
*/ | |||
#define SOS_CPU_KSTATE_DETECT_UNINIT_VARS | |||
/* #undef SOS_CPU_KSTATE_DETECT_UNINIT_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 SOS_CPU_KSTATE_DETECT_STACK_OVERFLOW 64 | |||
/* #undef SOS_CPU_KSTATE_DETECT_STACK_OVERFLOW */ | |||
#if defined(SOS_CPU_KSTATE_DETECT_STACK_OVERFLOW) | |||
void | |||
sos_cpu_kstate_prepare_detect_stack_overflow(const struct sos_cpu_kstate *ctxt, | |||
sos_vaddr_t stack_bottom, | |||
sos_size_t stack_size); | |||
void sos_cpu_kstate_detect_stack_overflow(const struct sos_cpu_kstate *ctxt, | |||
sos_vaddr_t stack_bottom, | |||
sos_size_t stack_size); | |||
#else | |||
# define sos_cpu_kstate_prepare_detect_stack_overflow(ctxt,stkbottom,stksize) \ | |||
({ /* nop */ }) | |||
# define sos_cpu_kstate_detect_stack_overflow(ctxt,stkbottom,stksize) \ | |||
({ /* nop */ }) | |||
#endif | |||
/* ======================================================================= | |||
* Backtrace facility. To be used for DEBUGging purpose ONLY. | |||
*/ | |||
/** | |||
* The function called at each step of the backtrace iterations | |||
* | |||
* @param PC The address of the next instruction of the function that | |||
* will be executed | |||
* | |||
* @param params The address of the array of the parameteres that have | |||
* been passed to the function considered | |||
* | |||
* @param depth The index of the iteration (ie the depth of the | |||
* current frame into the stack) | |||
* | |||
* @param custom_arg Whatever you want: this is the argument passed as | |||
* custom_arg to sos_backtrace() | |||
*/ | |||
typedef void (sos_backtrace_callback_t)(sos_vaddr_t PC, | |||
sos_vaddr_t params, | |||
sos_ui32_t depth, | |||
void *custom_arg); | |||
/** | |||
* Call the backtracer callback on each frame stored in the cpu_kstate | |||
* | |||
* @param cpu_kstate The CPU context we want to explore. NULL to | |||
* backtrace the current CPU context. | |||
* | |||
* @param max_depth The maximum number of frames to explore | |||
* | |||
* @param stack_bottom The lower boundary of the stack. This is used | |||
* to make sure that the frame addresses fit inside the stack | |||
* boudaries (ie are potentially correct). | |||
* | |||
* @param stack_size The size of the stack. Same comment. | |||
* | |||
* @param backtracer The function to call to handle the frame for each | |||
* iteration | |||
* | |||
* @param custom_arg The arg passed as custom_arg to the backtracer | |||
* | |||
* @return The number of frames explored. | |||
* | |||
* @note Might be inaccurate when gcc's -fomit-frame-pointer has been | |||
* used. | |||
*/ | |||
sos_ui32_t sos_backtrace(const struct sos_cpu_kstate *cpu_kstate, | |||
sos_ui32_t max_depth, | |||
sos_vaddr_t stack_bottom, | |||
sos_size_t stack_size, | |||
sos_backtrace_callback_t * backtracer, | |||
void *custom_arg); | |||
#endif /* _SOS_CPUCTXT_H_ */ | |||
|
| ||
|
| ||
/* Copyright (C) 2000-2004, The KOS team | |||
Copyright (C) 1999 Free Software Foundation, Inc. | |||
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. | |||
*/ | |||
#define ASM_SOURCE 1 | |||
.file "cpu_context_switch.S" | |||
.text | |||
.globl sos_cpu_kstate_switch | |||
.type sos_cpu_kstate_switch, @function | |||
sos_cpu_kstate_switch: | |||
// arg2= to_context -- esp+64 | |||
// arg1= from_context -- esp+60 | |||
// caller ip -- esp+56 | |||
pushf // (eflags) esp+52 | |||
pushl %cs // (cs) esp+48 | |||
pushl $resume_pc // (ip) esp+44 | |||
pushl $0 // (error code) esp+40 | |||
pushl %ebp // esp+36 | |||
pushl %edi // esp+32 | |||
pushl %esi // esp+28 | |||
pushl %edx // esp+24 | |||
pushl %ecx // esp+20 | |||
pushl %ebx // esp+16 | |||
pushl %eax // esp+12 | |||
subl $2, %esp // (alignment) esp+10 | |||
pushw %ss // esp+8 | |||
pushw %ds // esp+6 | |||
pushw %es // esp+4 | |||
pushw %fs // esp+2 | |||
pushw %gs // esp | |||
/* | |||
* Now that the original eax/ebx are store, we can use them safely | |||
*/ | |||
/* Store the address of the saved context */ | |||
movl 60(%esp), %ebx | |||
movl %esp, (%ebx) | |||
/* This is the proper context switch ! We change the stack here */ | |||
movl 64(%esp), %esp | |||
/* Restore the CPU context */ | |||
popw %gs | |||
popw %fs | |||
popw %es | |||
popw %ds | |||
popw %ss | |||
addl $2,%esp | |||
popl %eax | |||
popl %ebx | |||
popl %ecx | |||
popl %edx | |||
popl %esi | |||
popl %edi | |||
popl %ebp | |||
addl $4, %esp /* Ignore "error code" */ | |||
/* This restores the eflags, the cs and the eip registers */ | |||
iret /* equivalent to: popfl ; ret */ | |||
resume_pc: | |||
// Same context as that when sos_cpu_kstate_switch got called | |||
// arg2= to_context -- esp+8 | |||
// arg1= from_context -- esp+4 | |||
// caller ip -- esp | |||
ret | |||
/* ------------------------- */ | |||
.globl sos_cpu_kstate_exit_to | |||
.type sos_cpu_kstate_exit_to, @function | |||
sos_cpu_kstate_exit_to: | |||
// arg3= reclaiming_arg -- esp+12 | |||
// arg2= reclaiming_func -- esp+8 | |||
// arg1= to_context -- esp+4 | |||
// caller ip -- esp | |||
/* Store the current SP in a temporary register */ | |||
movl %esp, %eax | |||
/* This is the proper context switch ! We change the stack here */ | |||
movl 4(%eax), %esp | |||
/* Call the reclaiming function (remember: the old frame address | |||
is stored in eax) */ | |||
pushl 12(%eax) | |||
call *8(%eax) | |||
addl $4, %esp | |||
/* Restore the CPU context */ | |||
popw %gs | |||
popw %fs | |||
popw %es | |||
popw %ds | |||
popw %ss | |||
addl $2,%esp | |||
popl %eax | |||
popl %ebx | |||
popl %ecx | |||
popl %edx | |||
popl %esi | |||
popl %edi | |||
popl %ebp | |||
addl $4, %esp /* Ignore "error code" */ | |||
/* This restores the eflags, the cs and the eip registers */ | |||
iret /* equivalent to: popfl ; ret */ | |||
|
| ||
|
| ||
sos_exception_handler_t sos_exception_handler_array[SOS_EXCEPT_NUM] = | sos_exception_handler_t sos_exception_handler_array[SOS_EXCEPT_NUM] = | ||
{ NULL, }; | { NULL, }; | ||
sos_ret_t sos_exceptions_setup(void) | sos_ret_t sos_exception_subsystem_setup(void) | ||
/* We inidicate that the double fault exception handler is defined, | /* We inidicate that the double fault exception handler is defined, | ||
and give its address. this handler is a do-nothing handler (see | and give its address. this handler is a do-nothing handler (see | ||
|
| ||
|
| ||
#ifndef ASM_SOURCE | #ifndef ASM_SOURCE | ||
# include <sos/errno.h> | # include <sos/errno.h> | ||
# include "cpu_context.h" | |||
#endif | #endif | ||
/** | /** | ||
|
| ||
#ifndef ASM_SOURCE | #ifndef ASM_SOURCE | ||
typedef void (*sos_exception_handler_t)(int exception_number); | typedef void (*sos_exception_handler_t)(int exception_number, | ||
const struct sos_cpu_kstate *cpu_kstate); | |||
sos_ret_t sos_exceptions_setup(void); | sos_ret_t sos_exception_subsystem_setup(void); | ||
sos_exception_handler_t routine); | sos_exception_handler_t routine); | ||
sos_exception_handler_t sos_exception_get_routine(int exception_number); | sos_exception_handler_t sos_exception_get_routine(int exception_number); | ||
|
| ||
|
| ||
pushw %fs | pushw %fs | ||
pushw %gs | pushw %gs | ||
/* Call the handler with exception number as | /* | ||
* argument */ | * Call the handler with the exception number and the | ||
* address of the stored CPU context as arguments | |||
*/ | |||
pushl %esp | |||
leal sos_exception_handler_array,%edi | leal sos_exception_handler_array,%edi | ||
call *\id*4(%edi) | call *\id*4(%edi) | ||
addl $4, %esp | /* Unallocate the arguments passed to the handler */ | ||
addl $8, %esp | |||
/* Restore the context */ | /* Restore the context */ | ||
popw %gs | popw %gs | ||
|
| ||
pushw %fs | pushw %fs | ||
pushw %gs | pushw %gs | ||
/* Call the handler with exception number as | /* | ||
* argument */ | * Call the handler with the exception number and the | ||
* address of the stored CPU context as arguments | |||
*/ | |||
pushl %esp | |||
leal sos_exception_handler_array,%edi | leal sos_exception_handler_array,%edi | ||
call *\id*4(%edi) | call *\id*4(%edi) | ||
addl $4, %esp | /* Unallocate the arguments passed to the handler */ | ||
addl $8, %esp | |||
/* Restore the context */ | /* Restore the context */ | ||
popw %gs | popw %gs | ||
|
| ||
/* Double fault handler not supported. We must define it since we | /* Double fault handler not supported. We must define it since we | ||
define an entry for it in the sos_exception_wrapper_array. */ | define an entry for it in the sos_exception_wrapper_array. It | ||
simply uses an alternate stack to display a message and stop the | |||
system. qemu won't handle it correctly (see comment in qemu's | |||
sources). */ | |||
#define ALTERNATE_DOUBLE_FAULT_STACK_SIZE 512 | |||
.p2align 2, 0x90 | .p2align 2, 0x90 | ||
sos_exception_wrapper_\id: | sos_exception_wrapper_\id: | ||
.type sos_exception_wrapper_\id,@function | .type sos_exception_wrapper_\id,@function | ||
1: hlt | 1: cli /* Not necessary */ | ||
jmp 1b /* Machine halting */ | movl $double_fault_alternate_stack, %eax | ||
addl $ALTERNATE_DOUBLE_FAULT_STACK_SIZE, %eax | |||
movl %eax, %esp | |||
pushl $msg_double_fault_not_supported | |||
call sos_display_fatal_error ; jmp 1b /* Not necessary */ | |||
/* Build the sos_irq_wrapper_array, shared with interrupt.c */ | |||
msg_double_fault_not_supported: | |||
.string "exception_wrappers.S: Double fault detected ! NOT SUPPORTED yet. System Halted." | |||
/* Build the sos_irq_wrapper_array, shared with interrupt.c */ | |||
.p2align 5, 0x0 | .p2align 5, 0x0 | ||
sos_exception_wrapper_array: | sos_exception_wrapper_array: | ||
.irp id, 0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15, \ | .irp id, 0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15, \ | ||
16,17,18,19,20,21,22,23,24,25,26,27,29,30,31 | 16,17,18,19,20,21,22,23,24,25,26,27,29,30,31 | ||
.long (sos_exception_wrapper_\id) | .long (sos_exception_wrapper_\id) | ||
.endr | .endr | ||
/* Alternate stack for double fault handler */ | |||
.bss | |||
.p2align 2, 0x0 | |||
.size double_fault_alternate_stack, ALTERNATE_DOUBLE_FAULT_STACK_SIZE | |||
double_fault_alternate_stack: | |||
.fill ALTERNATE_DOUBLE_FAULT_STACK_SIZE, 1, 0x0 | |||
|
| ||
|
| ||
[SOS_SEG_KDATA] = BUILD_GDTE(0, 0), | [SOS_SEG_KDATA] = BUILD_GDTE(0, 0), | ||
}; | }; | ||
sos_ret_t sos_gdt_setup(void) | sos_ret_t sos_gdt_subsystem_setup(void) | ||
struct x86_gdt_register gdtr; | struct x86_gdt_register gdtr; | ||
|
| ||
|
| ||
* Configure the virtual space as a direct mapping to the linear | * Configure the virtual space as a direct mapping to the linear | ||
* address space (ie "flat" virtual space). | * address space (ie "flat" virtual space). | ||
*/ | */ | ||
sos_ret_t sos_gdt_setup(void); | sos_ret_t sos_gdt_subsystem_setup(void); | ||
#endif /* _SOS_GDT_H_ */ | #endif /* _SOS_GDT_H_ */ | ||
|
| ||
|
| ||
#define PIC_SLAVE 0xa0 | #define PIC_SLAVE 0xa0 | ||
/** Setup the 8259 PIC */ | /** Setup the 8259 PIC */ | ||
sos_ret_t sos_i8259_setup(void) | sos_ret_t sos_i8259_subsystem_setup(void) | ||
/* Send ICW1: 8086 mode + NOT Single ctrl + call address | /* Send ICW1: 8086 mode + NOT Single ctrl + call address | ||
interval=8 */ | interval=8 */ | ||
|
| ||
|
| ||
*/ | */ | ||
/** Setup PIC and Disable all IRQ lines */ | /** Setup PIC and Disable all IRQ lines */ | ||
sos_ret_t sos_i8259_setup(void); | sos_ret_t sos_i8259_subsystem_setup(void); | ||
sos_ret_t sos_i8259_enable_irq_line(int numirq); | sos_ret_t sos_i8259_enable_irq_line(int numirq); | ||
|
| ||
|
| ||
static struct x86_idt_entry idt[SOS_IDTE_NUM]; | static struct x86_idt_entry idt[SOS_IDTE_NUM]; | ||
sos_ret_t sos_idt_setup() | sos_ret_t sos_idt_subsystem_setup() | ||
struct x86_idt_register idtr; | struct x86_idt_register idtr; | ||
int i; | int i; | ||
|
| ||
|
| ||
/** Initialization routine: all the IDT entries (or "IDTE") are marked | /** Initialization routine: all the IDT entries (or "IDTE") are marked | ||
"not present". */ | "not present". */ | ||
sos_ret_t sos_idt_setup(void); | sos_ret_t sos_idt_subsystem_setup(void); | ||
/** | /** | ||
* Enable the IDT entry if handler_address != NULL, with the given | * Enable the IDT entry if handler_address != NULL, with the given | ||
|
| ||
|
| ||
#include "irq.h" | #include "irq.h" | ||
/* array of IRQ wrappers, defined in irq_wrappers.S */ | /** array of IRQ wrappers, defined in irq_wrappers.S */ | ||
/* arrays of IRQ handlers, shared with irq_wrappers.S */ | /** arrays of IRQ handlers, shared with irq_wrappers.S */ | ||
/** Number of interrupt handlers that are currently executing */ | |||
sos_ui32_t sos_irq_nested_level_counter; | |||
sos_ret_t sos_irq_setup(void) | sos_ret_t sos_irq_subsystem_setup(void) | ||
return sos_i8259_setup(); | sos_irq_nested_level_counter = 0; | ||
return sos_i8259_subsystem_setup(); | |||
|
| ||
/* Expected to be atomic */ | /* Expected to be atomic */ | ||
return sos_irq_handler_array[irq_level]; | return sos_irq_handler_array[irq_level]; | ||
} | } | ||
sos_ui32_t sos_irq_get_nested_level() | |||
{ | |||
/* No need to disable interrupts here */ | |||
return sos_irq_nested_level_counter; | |||
} | |||
|
| ||
|
| ||
#ifndef _SOS_HWINTR_H_ | #ifndef _SOS_HWINTR_H_ | ||
#define _SOS_HWINTR_H_ | #define _SOS_HWINTR_H_ | ||
/** | /** | ||
* @file irq.c | * @file irq.c | ||
* | * | ||
* Hardware interrupts routines management. | * Hardware interrupts routines management. | ||
*/ | */ | ||
#include <sos/errno.h> | #include <sos/errno.h> | ||
#include "cpu_context.h" | |||
#define sos_save_flags(flags) \ | #define sos_save_flags(flags) \ | ||
asm volatile("pushfl ; popl %0":"=g"(flags)::"memory") | asm volatile("pushfl ; popl %0":"=g"(flags)::"memory") | ||
#define sos_restore_flags(flags) \ | #define sos_restore_flags(flags) \ | ||
asm volatile("push %0; popfl"::"g"(flags):"memory") | asm volatile("push %0; popfl"::"g"(flags):"memory") | ||
#define sos_disable_IRQs(flags) \ | #define sos_disable_IRQs(flags) \ | ||
({ sos_save_flags(flags); asm("cli\n"); }) | ({ sos_save_flags(flags); asm("cli\n"); }) | ||
#define sos_restore_IRQs(flags) \ | #define sos_restore_IRQs(flags) \ | ||
sos_restore_flags(flags) | sos_restore_flags(flags) | ||
/* Usual IRQ levels */ | /* Usual IRQ levels */ | ||
#define SOS_IRQ_TIMER 0 | #define SOS_IRQ_TIMER 0 | ||
#define SOS_IRQ_KEYBOARD 1 | #define SOS_IRQ_KEYBOARD 1 | ||
|
| ||
#define SOS_IRQ_HARDDISK 14 | #define SOS_IRQ_HARDDISK 14 | ||
#define SOS_IRQ_RESERVED_5 15 | #define SOS_IRQ_RESERVED_5 15 | ||
typedef void (*sos_irq_handler_t)(int irq_level); | |||
/** Definition of an hardware IRQ handler */ | |||
typedef void (*sos_irq_handler_t)(int irq_level, | |||
const struct sos_cpu_kstate *cpu_kstate); | |||
/** Setup the PIC */ | /** Setup the PIC */ | ||
sos_ret_t sos_irq_setup(void); | sos_ret_t sos_irq_subsystem_setup(void); | ||
/** | /** | ||
* If the routine is not NULL, the IDT is setup to call an IRQ | * If the routine is not NULL, the IDT is setup to call an IRQ | ||
|
| ||
sos_irq_handler_t sos_irq_get_routine(int irq_level); | sos_irq_handler_t sos_irq_get_routine(int irq_level); | ||
/** | |||
* Tell how many nested IRQ handler have been fired | |||
*/ | |||
sos_ui32_t sos_irq_get_nested_level(); | |||
/** | |||
* Return TRUE when we are currently executing in interrupt context | |||
*/ | |||
#define sos_servicing_irq() \ | |||
(sos_irq_get_nested_level() > 0) | |||
#endif /* _SOS_HWINTR_H_ */ | #endif /* _SOS_HWINTR_H_ */ | ||
|
| ||
|
| ||
.text | .text | ||
/* The address of the table of handlers (defined in irq.c) */ | /** The address of the table of handlers (defined in irq.c) */ | ||
/* The address of the table of wrappers (defined below, and shared | /** The address of the table of wrappers (defined below, and shared | ||
.globl sos_irq_wrapper_array | .globl sos_irq_wrapper_array | ||
/** The variable holding the nested level of the IRQ handlers */ | |||
.extern sos_irq_nested_level_counter | |||
/* These pre-handlers are for IRQ (Master PIC) */ | /* These pre-handlers are for IRQ (Master PIC) */ | ||
.irp id, 0,1,2,3,4,5,6,7 | .irp id, 0,1,2,3,4,5,6,7 | ||
|
| ||
pushw %fs | pushw %fs | ||
pushw %gs | pushw %gs | ||
/* | |||
* Increment IRQ nested level | |||
*/ | |||
incl sos_irq_nested_level_counter | |||
/* Send EOI to PIC. See Intel 8259 datasheet | /* Send EOI to PIC. See Intel 8259 datasheet | ||
available on Kos website */ | available on Kos website */ | ||
movb $0x20, %al | movb $0x20, %al | ||
outb %al, $0x20 | outb %al, $0x20 | ||
/* | /* | ||
* Call the handler with IRQ number as argument | * Call the handler with the IRQ number and the | ||
* address of the stored CPU context as arguments | |||
pushl %esp | |||
pushl $\id | pushl $\id | ||
leal sos_irq_handler_array,%edi | leal sos_irq_handler_array,%edi | ||
call *\id*4(%edi) | call *\id*4(%edi) | ||
addl $4, %esp | /* Unallocate the arguments passed to the handler */ | ||
addl $8, %esp | |||
/* | |||
* Decrement IRQ nested level | |||
*/ | |||
cli /* Just in case we messed up everything in the handler */ | |||
subl $1, sos_irq_nested_level_counter | |||
/* sos_irq_nested_level_counter went below 0 ?! */ | |||
jnc 2f | |||
1: /* Yes: Print fatal error message */ | |||
pushl $msg_nested_level_overflow | |||
call sos_display_fatal_error | |||
addl $4, %esp ; jmp 1b | |||
/* Never returns */ | |||
2: /* No: all right ! */ | |||
/* Restore the context */ | /* Restore the context */ | ||
popw %gs | popw %gs | ||
|
| ||
pushw %fs | pushw %fs | ||
pushw %gs | pushw %gs | ||
/* | |||
* Increment IRQ nested level | |||
*/ | |||
incl sos_irq_nested_level_counter | |||
/* Send EOI to PIC. See Intel 8259 datasheet | /* Send EOI to PIC. See Intel 8259 datasheet | ||
available on Kos website */ | available on Kos website */ | ||
movb $0x20, %al | movb $0x20, %al | ||
|
| ||
outb %al, $0x20 | outb %al, $0x20 | ||
/* | /* | ||
* Call the handler with IRQ number as argument | * Call the handler with the IRQ number and the | ||
* address of the stored CPU context as arguments | |||
pushl %esp | |||
pushl $\id | pushl $\id | ||
leal sos_irq_handler_array,%edi | leal sos_irq_handler_array,%edi | ||
call *\id*4(%edi) | call *\id*4(%edi) | ||
addl $4, %esp | /* Unallocate the arguments passed to the handler */ | ||
addl $8, %esp | |||
/* | |||
* Decrement IRQ nested level | |||
*/ | |||
cli /* Just in case we messed up everything in the handler */ | |||
subl $1, sos_irq_nested_level_counter | |||
/* sos_irq_nested_level_counter went below 0 ?! */ | |||
jnc 2f | |||
1: /* Yes: Print fatal error message */ | |||
pushl $msg_nested_level_overflow | |||
call sos_display_fatal_error | |||
addl $4, %esp ; jmp 1b | |||
/* Never returns */ | |||
2: /* No: all right ! */ | |||
/* Restore the context */ | /* Restore the context */ | ||
popw %gs | popw %gs | ||
|
| ||
iret | iret | ||
.endr | .endr | ||
/* Build the sos_irq_wrapper_array, shared with irq.c */ | |||
msg_nested_level_overflow: | |||
.string "irq_wrappers.S: IRQ Nested level overflow ! System halted." | |||
/* Build the sos_irq_wrapper_array, shared with irq.c */ | |||
.p2align 5, 0x0 | .p2align 5, 0x0 | ||
sos_irq_wrapper_array: | sos_irq_wrapper_array: | ||
.irp id, 0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15 | .irp id, 0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15 | ||
|
| ||
|
| ||
} | } | ||
sos_ret_t sos_paging_setup(sos_paddr_t identity_mapping_base, | sos_ret_t sos_paging_subsystem_setup(sos_paddr_t identity_mapping_base, | ||
sos_paddr_t identity_mapping_top) | sos_paddr_t identity_mapping_top) | ||
/* The PDBR we will setup below */ | /* The PDBR we will setup below */ | ||
struct x86_pdbr cr3; | struct x86_pdbr cr3; | ||
|
| ||
sos_ret_t sos_paging_map(sos_paddr_t ppage_paddr, | sos_ret_t sos_paging_map(sos_paddr_t ppage_paddr, | ||
sos_vaddr_t vpage_vaddr, | sos_vaddr_t vpage_vaddr, | ||
sos_bool_t is_user_page, | sos_bool_t is_user_page, | ||
int flags) | sos_ui32_t flags) | ||
/* Get the page directory entry and table entry index for this | /* Get the page directory entry and table entry index for this | ||
address */ | address */ | ||
|
| ||
|
| ||
* text to be printed to the console. Finally, this routine installs | * text to be printed to the console. Finally, this routine installs | ||
* the whole configuration into the MMU. | * the whole configuration into the MMU. | ||
*/ | */ | ||
sos_ret_t sos_paging_setup(sos_paddr_t identity_mapping_base, | sos_ret_t sos_paging_subsystem_setup(sos_paddr_t identity_mapping_base, | ||
sos_paddr_t identity_mapping_top); | sos_paddr_t identity_mapping_top); | ||
/** | /** | ||
* Map the given physical page at the given virtual address in the | * Map the given physical page at the given virtual address in the | ||
|
| ||
sos_ret_t sos_paging_map(sos_paddr_t ppage_paddr, | sos_ret_t sos_paging_map(sos_paddr_t ppage_paddr, | ||
sos_vaddr_t vpage_vaddr, | sos_vaddr_t vpage_vaddr, | ||
sos_bool_t is_user_page, | sos_bool_t is_user_page, | ||
int flags); | sos_ui32_t flags); | ||
/** | /** | ||
* Undo the mapping from vaddr to the underlying physical page (if any) | * Undo the mapping from vaddr to the underlying physical page (if any) | ||
|
| ||
|
| ||
* | * | ||
* @see gdt.h | * @see gdt.h | ||
*/ | */ | ||
#define SOS_SEG_NULL 0 /* NULL segment, unused by the procesor */ | #define SOS_SEG_NULL 0 /* NULL segment, unused by the procesor */ | ||
#define SOS_SEG_KCODE 1 /* Kernel code segment */ | #define SOS_SEG_KCODE 1 /* Kernel code segment */ | ||
#define SOS_SEG_KDATA 2 /* Kernel data segment */ | #define SOS_SEG_KDATA 2 /* Kernel data segment */ | ||
/** | /** | ||
|
| ||
|
| ||
/* Copyright (C) 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. | |||
*/ | |||
#include <sos/klibc.h> | |||
#include <drivers/bochs.h> | |||
#include <drivers/x86_videomem.h> | |||
#include "assert.h" | |||
void sos_display_fatal_error(const char *format, /* args */...) | |||
{ | |||
char buff[256]; | |||
va_list ap; | |||
asm("cli\n"); /* disable interrupts -- x86 only */ \ | |||
va_start(ap, format); | |||
vsnprintf(buff, sizeof(buff), format, ap); | |||
va_end(ap); | |||
sos_bochs_putstring(buff); sos_bochs_putstring("\n"); | |||
sos_x86_videomem_putstring(24, 0, | |||
SOS_X86_VIDEO_BG_BLACK | |||
| SOS_X86_VIDEO_FG_LTRED , buff); | |||
/* Infinite loop: processor halted */ | |||
for ( ; ; ) | |||
asm("hlt\n"); | |||
} | |||
|
| ||
|
| ||
#ifndef _SOS_ASSERT_H_ | #ifndef _SOS_ASSERT_H_ | ||
#define _SOS_ASSERT_H_ | #define _SOS_ASSERT_H_ | ||
#include <drivers/bochs.h> | |||
#include <drivers/x86_videomem.h> | void sos_display_fatal_error(const char *format, /* args */...) | ||
__attribute__ ((format (printf, 1, 2), noreturn)); | |||
/** | /** | ||
* If the expr is FALSE, print a message and halt the machine | * If the expr is FALSE, print a message and halt the machine | ||
|
| ||
#define SOS_ASSERT_FATAL(expr) \ | #define SOS_ASSERT_FATAL(expr) \ | ||
({ \ | ({ \ | ||
int __res=(int)(expr); \ | int __res=(int)(expr); \ | ||
if (! __res) { \ | if (! __res) \ | ||
asm("cli\n"); /* disable interrupts -- x86 only */ \ | sos_display_fatal_error("%s@%s:%d Assertion " # expr " failed", \ | ||
sos_bochs_printf("%s@%s:%d Assertion " # expr " failed\n", \ | |||
__PRETTY_FUNCTION__, __FILE__, __LINE__); \ | |||
sos_x86_videomem_printf(24, 0, 12, \ | |||
"%s@%s:%d Assertion " # expr " failed", \ | |||
for (;;) asm("hlt;") ; /* Infinite loop, ie simple system halt */ \ | |||
} \ | |||
#define SOS_FATAL_ERROR(fmt,args...) \ | |||
({ \ | |||
sos_display_fatal_error("%s@%s:%d FATAL: " fmt, \ | |||
__PRETTY_FUNCTION__, __FILE__, __LINE__, \ | |||
##args); \ | |||
}) | |||
#endif /* _SOS_ASSERT_H_ */ | #endif /* _SOS_ASSERT_H_ */ | ||
|
| ||
|
| ||
} | } | ||
static unsigned long int _random_seed = 93186752; | |||
/** | |||
* The following code is borrowed from Glenn Rhoads. | |||
* http://remus.rutgers.edu/~rhoads/Code/code.html | |||
* License to be defined... | |||
*/ | |||
unsigned long int random (void) | |||
{ | |||
/* The following parameters are recommended settings based on research | |||
uncomment the one you want. */ | |||
/* For RAND_MAX == 4294967291 */ | |||
static unsigned int a = 1588635695, q = 2, r = 1117695901; | |||
/* static unsigned int a = 1223106847, m = 4294967291U, q = 3, r = 625646750;*/ | |||
/* static unsigned int a = 279470273, m = 4294967291U, q = 15, r = 102913196;*/ | |||
/* For RAND_MAX == 2147483647 */ | |||
/* static unsigned int a = 1583458089, m = 2147483647, q = 1, r = 564025558; */ | |||
/* static unsigned int a = 784588716, m = 2147483647, q = 2, r = 578306215; */ | |||
/* static unsigned int a = 16807, m = 2147483647, q = 127773, r = 2836; */ | |||
/* static unsigned int a = 950706376, m = 2147483647, q = 2, r = 246070895; */ | |||
_random_seed = a*(_random_seed % q) - r*(_random_seed / q); | |||
return _random_seed; | |||
} | |||
void srandom (unsigned long int seed) | |||
{ | |||
_random_seed = seed; | |||
} | |||
/* I (d2) borrowed and rewrote this for Nachos/INSA Rennes. Thanks to | /* I (d2) borrowed and rewrote this for Nachos/INSA Rennes. Thanks to | ||
them for having kindly allowed me to do so. */ | them for having kindly allowed me to do so. */ | ||
int vsnprintf(char *buff, sos_size_t len, const char * format, va_list ap) | int vsnprintf(char *buff, sos_size_t len, const char * format, va_list ap) | ||
|
| ||
break; | break; | ||
} | } | ||
case 'p': | |||
PUTCHAR('0'); | |||
PUTCHAR('x'); | |||
case 'x': | case 'x': | ||
{ | { | ||
unsigned int hexa = va_arg(ap,int); | unsigned int hexa = va_arg(ap,int); | ||
|
| ||
|
| ||
* | * | ||
* Basic libc-style support for common useful functions (string.h, | * Basic libc-style support for common useful functions (string.h, | ||
* stdarg.h), some with slight non-standard behavior (see comments). | * stdarg.h), some with slight non-standard behavior (see comments). | ||
* | |||
* Most of the prototypes of these functions are borrowed from | |||
* FreeBSD, but their implementation (in klibc.c) come either from Kos | |||
* (GPL v2) or from David Decotigny (SOS). | |||
*/ | */ | ||
#include <sos/types.h> | #include <sos/types.h> | ||
|
| ||
int snprintf(char *, sos_size_t, const char *, /*args*/ ...) | int snprintf(char *, sos_size_t, const char *, /*args*/ ...) | ||
__attribute__ ((format (printf, 3, 4))); | __attribute__ ((format (printf, 3, 4))); | ||
/* | |||
* Pseudo-random generation functions. Useful to do some coverage | |||
* tests. | |||
*/ | |||
/* Amplitude of the random number generation */ | |||
#define RAND_MAX 4294967291U | |||
/* Pseudo-random number generation (MT unsafe) */ | |||
unsigned long int random (void); | |||
/* Set random seed (MT unsafe) */ | |||
void srandom (unsigned long int seed); | |||
#endif /* _SOS_KLIBC_H_ */ | #endif /* _SOS_KLIBC_H_ */ | ||
|
| ||
|
| ||
}; | }; | ||
sos_ret_t sos_kmalloc_setup() | sos_ret_t sos_kmalloc_subsystem_setup() | ||
int i; | int i; | ||
for (i = 0 ; kmalloc_cache[i].object_size != 0 ; i ++) | for (i = 0 ; kmalloc_cache[i].object_size != 0 ; i ++) | ||
|
| ||
|
| ||
/** | /** | ||
* Iniatilize the kmalloc subsystem, ie pre-allocate a series of caches. | * Iniatilize the kmalloc subsystem, ie pre-allocate a series of caches. | ||
*/ | */ | ||
sos_ret_t sos_kmalloc_setup(void); | sos_ret_t sos_kmalloc_subsystem_setup(void); | ||
/* | /* | ||
* sos_kmalloc flags | * sos_kmalloc flags | ||
|
| ||
/** | /** | ||
* Allocate a kernel object of the given size in the most suited slab | * Allocate a kernel object of the given size in the most suited slab | ||
* cache if size can be handled by one of the pre-allocated caches, or | * cache if size can be handled by one of the pre-allocated caches, or | ||
* using directly the range allocator otherwise. | * using directly the range allocator otherwise. The object will | ||
* allways be mapped in physical memory (ie implies | |||
* SOS_KSLAB_CREATE_MAP and SOS_KMEM_VMM_MAP). | |||
* @param size The size of the object | * @param size The size of the object | ||
* @param flags The allocation flags (SOS_KMALLOC_* flags) | * @param flags The allocation flags (SOS_KMALLOC_* flags) | ||
|
| ||
|
| ||
struct sos_kslab_cache * | struct sos_kslab_cache * | ||
sos_kmem_cache_setup_prepare(sos_vaddr_t kernel_core_base, | sos_kmem_cache_subsystem_setup_prepare(sos_vaddr_t kernel_core_base, | ||
sos_vaddr_t kernel_core_top, | sos_vaddr_t kernel_core_top, | ||
sos_size_t sizeof_struct_range, | sos_size_t sizeof_struct_range, | ||
/* results */ | /* results */ | ||
struct sos_kslab **first_struct_slab_of_caches, | struct sos_kslab **first_struct_slab_of_caches, | ||
sos_vaddr_t *first_slab_of_caches_base, | sos_vaddr_t *first_slab_of_caches_base, | ||
sos_count_t *first_slab_of_caches_nb_pages, | sos_count_t *first_slab_of_caches_nb_pages, | ||
struct sos_kslab **first_struct_slab_of_ranges, | struct sos_kslab **first_struct_slab_of_ranges, | ||
sos_vaddr_t *first_slab_of_ranges_base, | sos_vaddr_t *first_slab_of_ranges_base, | ||
sos_count_t *first_slab_of_ranges_nb_pages) | sos_count_t *first_slab_of_ranges_nb_pages) | ||
int i; | int i; | ||
sos_ret_t retval; | sos_ret_t retval; | ||
|
| ||
= sos_physmem_ref_physpage_new(FALSE); | = sos_physmem_ref_physpage_new(FALSE); | ||
SOS_ASSERT_FATAL(ppage_paddr != (sos_paddr_t)NULL); | SOS_ASSERT_FATAL(ppage_paddr != (sos_paddr_t)NULL); | ||
retval =sos_paging_map(ppage_paddr, vaddr, | retval = sos_paging_map(ppage_paddr, vaddr, | ||
FALSE, | FALSE, | ||
SOS_VM_MAP_ATOMIC | SOS_VM_MAP_ATOMIC | ||
| SOS_VM_MAP_PROT_READ | | SOS_VM_MAP_PROT_READ | ||
| SOS_VM_MAP_PROT_WRITE); | | SOS_VM_MAP_PROT_WRITE); | ||
retval = sos_physmem_unref_physpage(ppage_paddr); | retval = sos_physmem_unref_physpage(ppage_paddr); | ||
SOS_ASSERT_FATAL(retval == SOS_OK); | SOS_ASSERT_FATAL(retval == FALSE); | ||
/* Create the cache of caches */ | /* Create the cache of caches */ | ||
|
| ||
SOS_ASSERT_FATAL(retval == SOS_OK); | SOS_ASSERT_FATAL(retval == SOS_OK); | ||
retval = sos_physmem_unref_physpage(ppage_paddr); | retval = sos_physmem_unref_physpage(ppage_paddr); | ||
SOS_ASSERT_FATAL(retval == SOS_OK); | SOS_ASSERT_FATAL(retval == FALSE); | ||
/* Create the cache of ranges */ | /* Create the cache of ranges */ | ||
|
| ||
sos_ret_t | sos_ret_t | ||
sos_kmem_cache_setup_commit(struct sos_kslab *first_struct_slab_of_caches, | sos_kmem_cache_subsystem_setup_commit(struct sos_kslab *first_struct_slab_of_caches, | ||
struct sos_kmem_range *first_range_of_caches, | struct sos_kmem_range *first_range_of_caches, | ||
struct sos_kslab *first_struct_slab_of_ranges, | struct sos_kslab *first_struct_slab_of_ranges, | ||
struct sos_kmem_range *first_range_of_ranges) | struct sos_kmem_range *first_range_of_ranges) | ||
first_struct_slab_of_caches->range = first_range_of_caches; | first_struct_slab_of_caches->range = first_range_of_caches; | ||
first_struct_slab_of_ranges->range = first_range_of_ranges; | first_struct_slab_of_ranges->range = first_range_of_ranges; | ||
|
| ||
|
| ||
* @return the cache of kmem_range immediatly usable | * @return the cache of kmem_range immediatly usable | ||
*/ | */ | ||
struct sos_kslab_cache * | struct sos_kslab_cache * | ||
sos_kmem_cache_setup_prepare(sos_vaddr_t kernel_core_base, | sos_kmem_cache_subsystem_setup_prepare(sos_vaddr_t kernel_core_base, | ||
sos_vaddr_t kernel_core_top, | sos_vaddr_t kernel_core_top, | ||
sos_size_t sizeof_struct_range, | sos_size_t sizeof_struct_range, | ||
/* results */ | /* results */ | ||
struct sos_kslab **first_struct_slab_of_caches, | struct sos_kslab **first_struct_slab_of_caches, | ||
sos_vaddr_t *first_slab_of_caches_base, | sos_vaddr_t *first_slab_of_caches_base, | ||
sos_count_t *first_slab_of_caches_nb_pages, | sos_count_t *first_slab_of_caches_nb_pages, | ||
struct sos_kslab **first_struct_slab_of_ranges, | struct sos_kslab **first_struct_slab_of_ranges, | ||
sos_vaddr_t *first_slab_of_ranges_base, | sos_vaddr_t *first_slab_of_ranges_base, | ||
sos_count_t *first_slab_of_ranges_nb_pages); | sos_count_t *first_slab_of_ranges_nb_pages); | ||
/** | /** | ||
* Update the configuration of the cache subsystem once the vmm | * Update the configuration of the cache subsystem once the vmm | ||
* subsystem has been fully initialized | * subsystem has been fully initialized | ||
*/ | */ | ||
sos_ret_t | sos_ret_t | ||
sos_kmem_cache_setup_commit(struct sos_kslab *first_struct_slab_of_caches, | sos_kmem_cache_subsystem_setup_commit(struct sos_kslab *first_struct_slab_of_caches, | ||
struct sos_kmem_range *first_range_of_caches, | struct sos_kmem_range *first_range_of_caches, | ||
struct sos_kslab *first_struct_slab_of_ranges, | struct sos_kslab *first_struct_slab_of_ranges, | ||
struct sos_kmem_range *first_range_of_ranges); | struct sos_kmem_range *first_range_of_ranges); | ||
/* | /* | ||
|
| ||
|
| ||
struct sos_kmem_range *range; | struct sos_kmem_range *range; | ||
/* First: try to retrieve the physical page mapped at this address */ | /* First: try to retrieve the physical page mapped at this address */ | ||
sos_paddr_t ppage_paddr = sos_paging_get_paddr(vaddr); | sos_paddr_t ppage_paddr = SOS_PAGE_ALIGN_INF(sos_paging_get_paddr(vaddr)); | ||
if (! ppage_paddr) | if (ppage_paddr) | ||
range = sos_physmem_get_kmem_range(ppage_paddr); | range = sos_physmem_get_kmem_range(ppage_paddr); | ||
/* If a page is mapped at this address, it is EXPECTED that it | /* If a page is mapped at this address, it is EXPECTED that it | ||
is really associated with a range */ | is really associated with a range */ | ||
SOS_ASSERT_FATAL(range != NULL); | SOS_ASSERT_FATAL(range != NULL); | ||
|
| ||
/* Not found */ | /* Not found */ | ||
if (! range) | if (! range) | ||
return NULL; | return NULL; | ||
/* vaddr not covered by this range */ | |||
if ( (vaddr < range->base_vaddr) | |||
|| (vaddr >= (range->base_vaddr + range->nb_pages*SOS_PAGE_SIZE)) ) | |||
return NULL; | |||
} | } | ||
return range; | return range; | ||
|
| ||
static struct sos_kmem_range * | static struct sos_kmem_range * | ||
create_range(sos_bool_t is_free, | create_range(sos_bool_t is_free, | ||
sos_vaddr_t base_vaddr, | sos_vaddr_t base_vaddr, | ||
sos_vaddr_t top_addr, | sos_vaddr_t top_vaddr, | ||
{ | { | ||
struct sos_kmem_range *range; | struct sos_kmem_range *range; | ||
SOS_ASSERT_FATAL(SOS_IS_PAGE_ALIGNED(base_vaddr)); | |||
SOS_ASSERT_FATAL(SOS_IS_PAGE_ALIGNED(top_vaddr)); | |||
if ((top_vaddr - base_vaddr) < SOS_PAGE_SIZE) | |||
return NULL; | |||
range = (struct sos_kmem_range*)sos_kmem_cache_alloc(kmem_range_cache, | range = (struct sos_kmem_range*)sos_kmem_cache_alloc(kmem_range_cache, | ||
SOS_KSLAB_ALLOC_ATOMIC); | SOS_KSLAB_ALLOC_ATOMIC); | ||
SOS_ASSERT_FATAL(range != NULL); | SOS_ASSERT_FATAL(range != NULL); | ||
range->base_vaddr = base_vaddr; | range->base_vaddr = base_vaddr; | ||
range->nb_pages = (top_addr - base_vaddr) / SOS_PAGE_SIZE; | range->nb_pages = (top_vaddr - base_vaddr) / SOS_PAGE_SIZE; | ||
if (is_free) | if (is_free) | ||
{ | { | ||
|
| ||
/* Ok, set the range owner for the pages in this page */ | /* Ok, set the range owner for the pages in this page */ | ||
for (vaddr = base_vaddr ; | for (vaddr = base_vaddr ; | ||
vaddr < top_addr ; | vaddr < top_vaddr ; | ||
{ | { | ||
sos_paddr_t ppage_paddr = sos_paging_get_paddr(vaddr); | sos_paddr_t ppage_paddr = sos_paging_get_paddr(vaddr); | ||
|
| ||
} | } | ||
sos_ret_t sos_kmem_vmm_setup(sos_vaddr_t kernel_core_base, | sos_ret_t | ||
sos_vaddr_t kernel_core_top) | sos_kmem_vmm_subsystem_setup(sos_vaddr_t kernel_core_base, | ||
sos_vaddr_t kernel_core_top, | |||
sos_vaddr_t bootstrap_stack_bottom_vaddr, | |||
sos_vaddr_t bootstrap_stack_top_vaddr) | |||
struct sos_kslab *first_struct_slab_of_caches, | struct sos_kslab *first_struct_slab_of_caches, | ||
*first_struct_slab_of_ranges; | *first_struct_slab_of_ranges; | ||
|
| ||
list_init(kmem_used_range_list); | list_init(kmem_used_range_list); | ||
kmem_range_cache | kmem_range_cache | ||
= sos_kmem_cache_setup_prepare(kernel_core_base, | = sos_kmem_cache_subsystem_setup_prepare(kernel_core_base, | ||
kernel_core_top, | kernel_core_top, | ||
sizeof(struct sos_kmem_range), | sizeof(struct sos_kmem_range), | ||
& first_struct_slab_of_caches, | & first_struct_slab_of_caches, | ||
& first_slab_of_caches_base, | & first_slab_of_caches_base, | ||
& first_slab_of_caches_nb_pages, | & first_slab_of_caches_nb_pages, | ||
& first_struct_slab_of_ranges, | & first_struct_slab_of_ranges, | ||
& first_slab_of_ranges_base, | & first_slab_of_ranges_base, | ||
& first_slab_of_ranges_nb_pages); | & first_slab_of_ranges_nb_pages); | ||
/* Mark virtual addresses 16kB - Video as FREE */ | /* Mark virtual addresses 16kB - Video as FREE */ | ||
|
| ||
SOS_PAGE_ALIGN_INF(kernel_core_base), | SOS_PAGE_ALIGN_INF(kernel_core_base), | ||
NULL); | NULL); | ||
/* Mark virtual addresses in Kernel code/data as NOT FREE */ | /* Mark virtual addresses in Kernel code/data up to the bootstrap stack | ||
as NOT FREE */ | |||
SOS_PAGE_ALIGN_INF(kernel_core_base), | SOS_PAGE_ALIGN_INF(kernel_core_base), | ||
bootstrap_stack_bottom_vaddr, | |||
NULL); | |||
/* Mark virtual addresses in the bootstrap stack as NOT FREE too, | |||
but in another vmm region in order to be un-allocated later */ | |||
create_range(FALSE, | |||
bootstrap_stack_bottom_vaddr, | |||
bootstrap_stack_top_vaddr, | |||
NULL); | |||
/* Mark the remaining virtual addresses in Kernel code/data after | |||
the bootstrap stack as NOT FREE */ | |||
create_range(FALSE, | |||
bootstrap_stack_top_vaddr, | |||
SOS_PAGE_ALIGN_SUP(kernel_core_top), | SOS_PAGE_ALIGN_SUP(kernel_core_top), | ||
NULL); | NULL); | ||
|
| ||
/* Update the cache subsystem so that the artificially-created | /* Update the cache subsystem so that the artificially-created | ||
caches of caches and ranges really behave like *normal* caches (ie | caches of caches and ranges really behave like *normal* caches (ie | ||
those allocated by the normal slab API) */ | those allocated by the normal slab API) */ | ||
sos_kmem_cache_setup_commit(first_struct_slab_of_caches, | sos_kmem_cache_subsystem_setup_commit(first_struct_slab_of_caches, | ||
first_range_of_caches, | first_range_of_caches, | ||
first_struct_slab_of_ranges, | first_struct_slab_of_ranges, | ||
first_range_of_ranges); | first_range_of_ranges); | ||
return SOS_OK; | return SOS_OK; | ||
} | } | ||
|
| ||
sos_physmem_set_kmem_range(ppage_paddr, new_range); | sos_physmem_set_kmem_range(ppage_paddr, new_range); | ||
} | } | ||
} | } | ||
/* ... Otherwise: Demand Paging will do the job */ | |||
/* Otherwise we need a correct page fault handler to support | |||
deferred mapping (aka demand paging) of ranges */ | |||
else | |||
SOS_ASSERT_FATAL(! "No demand paging yet"); | |||
if (range_start) | if (range_start) | ||
*range_start = new_range->base_vaddr; | *range_start = new_range->base_vaddr; | ||
|
| ||
} | } | ||
sos_vaddr_t sos_kmem_vmm_del_range(struct sos_kmem_range *range) | sos_ret_t sos_kmem_vmm_del_range(struct sos_kmem_range *range) | ||
int i; | int i; | ||
struct sos_kmem_range *ranges_to_free; | struct sos_kmem_range *ranges_to_free; | ||
|
| ||
} | } | ||
sos_vaddr_t sos_kmem_vmm_free(sos_vaddr_t vaddr) | sos_ret_t sos_kmem_vmm_free(sos_vaddr_t vaddr) | ||
struct sos_kmem_range *range = lookup_range(vaddr); | struct sos_kmem_range *range = lookup_range(vaddr); | ||
|
| ||
return range->slab; | return range->slab; | ||
} | } | ||
sos_bool_t sos_kmem_vmm_is_valid_vaddr(sos_vaddr_t vaddr) | |||
{ | |||
struct sos_kmem_range *range = lookup_range(vaddr); | |||
return (range != NULL); | |||
} | |||
|
| ||
|
| ||
* as "used", and the 0..SOS_KMEM_VMM_BASE virtual addresses as marked | * as "used", and the 0..SOS_KMEM_VMM_BASE virtual addresses as marked | ||
* as "used" too (to detect incorrect pointer dereferences). | * as "used" too (to detect incorrect pointer dereferences). | ||
*/ | */ | ||
sos_ret_t sos_kmem_vmm_setup(sos_vaddr_t kernel_core_base, | sos_ret_t | ||
sos_vaddr_t kernel_core_top); | sos_kmem_vmm_subsystem_setup(sos_vaddr_t kernel_core_base_vaddr, | ||
sos_vaddr_t kernel_core_top_vaddr, | |||
sos_vaddr_t bootstrap_stack_bottom_vaddr, | |||
sos_vaddr_t bootstrap_stack_top_vaddr); | |||
/* | /* | ||
|
| ||
struct sos_kmem_range *sos_kmem_vmm_new_range(sos_size_t nb_pages, | struct sos_kmem_range *sos_kmem_vmm_new_range(sos_size_t nb_pages, | ||
sos_ui32_t flags, | sos_ui32_t flags, | ||
sos_vaddr_t *range_base_vaddr); | sos_vaddr_t *range_base_vaddr); | ||
sos_vaddr_t sos_kmem_vmm_del_range(struct sos_kmem_range *range); | sos_ret_t sos_kmem_vmm_del_range(struct sos_kmem_range *range); | ||
/** | /** | ||
|
| ||
* the kernel/bios WILL be "deallocated". But if you really want to do | * the kernel/bios WILL be "deallocated". But if you really want to do | ||
* this, well..., do expect some "surprises" ;) | * this, well..., do expect some "surprises" ;) | ||
*/ | */ | ||
sos_vaddr_t sos_kmem_vmm_free(sos_vaddr_t vaddr); | sos_ret_t sos_kmem_vmm_free(sos_vaddr_t vaddr); | ||
/** | |||
* @return TRUE when vaddr is covered by any (used) kernel range | |||
*/ | |||
sos_bool_t sos_kmem_vmm_is_valid_vaddr(sos_vaddr_t vaddr); | |||
/* ***************************** | /* ***************************** | ||
|
| ||
struct sos_kslab *slab); | struct sos_kslab *slab); | ||
/** | /** | ||
* Retrieve the slab associated with the | * Retrieve the (used) slab associated with the range covering vaddr. | ||
* range covering vaddr. | |||
* @return NULL if the range is not associated with a KMEM range | * @return NULL if the range is not associated with a KMEM range | ||
*/ | */ | ||
|
| ||
|
| ||
({ unsigned int __bnd=(boundary); \ | ({ unsigned int __bnd=(boundary); \ | ||
(((((unsigned)(val))-1) & (~(__bnd - 1))) + __bnd); }) | (((((unsigned)(val))-1) & (~(__bnd - 1))) + __bnd); }) | ||
/** Check whether val is aligned on a boundary (MUST be a power of 2) */ | |||
#define SOS_IS_ALIGNED(val,boundary) \ | |||
( 0 == (((unsigned)(val)) & ((boundary)-1)) ) | |||
/** | /** | ||
* @return TRUE if val is a power of 2. | * @return TRUE if val is a power of 2. | ||
* @note val is evaluated multiple times | * @note val is evaluated multiple times | ||
|
| ||
|
| ||
/* Helper function to display each bits of a 32bits integer on the | /* Helper function to display each bits of a 32bits integer on the | ||
screen as dark or light carrets */ | screen as dark or light carrets */ | ||
static void display_bits(unsigned char row, unsigned char col, | void display_bits(unsigned char row, unsigned char col, | ||
unsigned char attribute, | unsigned char attribute, | ||
sos_ui32_t integer) | sos_ui32_t integer) | ||
int i; | int i; | ||
/* Scan each bit of the integer, MSb first */ | /* Scan each bit of the integer, MSb first */ | ||
|
| ||
/* Clock IRQ handler */ | /* Clock IRQ handler */ | ||
static void clk_it(int intid) | static void clk_it(int intid, | ||
const struct sos_cpu_kstate *cpu_kstate) | |||
static sos_ui32_t clock_count = 0; | static sos_ui32_t clock_count = 0; | ||
|
| ||
SOS_X86_VIDEO_FG_LTGREEN | SOS_X86_VIDEO_BG_BLUE, | SOS_X86_VIDEO_FG_LTGREEN | SOS_X86_VIDEO_BG_BLUE, | ||
clock_count); | clock_count); | ||
clock_count++; | clock_count++; | ||
struct digit | |||
/* ====================================================================== | |||
* Page fault exception handling | |||
*/ | |||
/* Helper function to dump a backtrace on bochs and/or the console */ | |||
static void dump_backtrace(const struct sos_cpu_kstate *cpu_kstate, | |||
sos_vaddr_t stack_bottom, | |||
sos_size_t stack_size, | |||
sos_bool_t on_console, | |||
sos_bool_t on_bochs) | |||
struct digit *prev, *next; | static void backtracer(sos_vaddr_t PC, | ||
char value; | sos_vaddr_t params, | ||
}; | sos_ui32_t depth, | ||
void *custom_arg) | |||
{ | |||
sos_ui32_t invalid = 0xffffffff, *arg1, *arg2, *arg3, *arg4; | |||
/* Representation of a big (positive) integer: Most Significant Digit | /* Get the address of the first 3 arguments from the | ||
(MSD) is the HEAD of the list. Least Significant Digit (LSD) is the | frame. Among these arguments, 0, 1, 2, 3 arguments might be | ||
TAIL of the list */ | meaningful (depending on how many arguments the function may | ||
typedef struct digit * big_number_t; | take). */ | ||
arg1 = (sos_ui32_t*)params; | |||
arg2 = (sos_ui32_t*)(params+4); | |||
arg3 = (sos_ui32_t*)(params+8); | |||
arg4 = (sos_ui32_t*)(params+12); | |||
/* Make sure the addresses of these arguments fit inside the | |||
stack boundaries */ | |||
#define INTERVAL_OK(b,v,u) ( ((b) <= (sos_vaddr_t)(v)) \ | |||
&& ((sos_vaddr_t)(v) < (u)) ) | |||
if (!INTERVAL_OK(stack_bottom, arg1, stack_bottom + stack_size)) | |||
arg1 = &invalid; | |||
if (!INTERVAL_OK(stack_bottom, arg2, stack_bottom + stack_size)) | |||
arg2 = &invalid; | |||
if (!INTERVAL_OK(stack_bottom, arg3, stack_bottom + stack_size)) | |||
arg3 = &invalid; | |||
if (!INTERVAL_OK(stack_bottom, arg4, stack_bottom + stack_size)) | |||
arg4 = &invalid; | |||
/* Print the function context for this frame */ | |||
if (on_bochs) | |||
sos_bochs_printf("[%d] PC=0x%x arg1=0x%x arg2=0x%x arg3=0x%x\n", | |||
(unsigned)depth, (unsigned)PC, | |||
(unsigned)*arg1, (unsigned)*arg2, | |||
(unsigned)*arg3); | |||
if (on_console) | |||
sos_x86_videomem_printf(23-depth, 3, | |||
SOS_X86_VIDEO_BG_BLUE | |||
| SOS_X86_VIDEO_FG_LTGREEN, | |||
"[%d] PC=0x%x arg1=0x%x arg2=0x%x arg3=0x%x arg4=0x%x", | |||
(unsigned)depth, PC, | |||
(unsigned)*arg1, (unsigned)*arg2, | |||
(unsigned)*arg3, (unsigned)*arg4); | |||
} | |||
sos_backtrace(cpu_kstate, 15, stack_bottom, stack_size, backtracer, NULL); | |||
} | |||
/* Add a new digit after the LSD */ | /* Page fault exception handler with demand paging for the kernel */ | ||
void bn_push_lsd(big_number_t * bn, char value) | static void pgflt_ex(int intid, const struct sos_cpu_kstate *ctxt) | ||
struct digit *d; | static sos_ui32_t demand_paging_count = 0; | ||
d = (struct digit*) sos_kmalloc(sizeof(struct digit), 0); | sos_vaddr_t faulting_vaddr = sos_cpu_kstate_get_EX_faulting_vaddr(ctxt); | ||
SOS_ASSERT_FATAL(d != NULL); | sos_paddr_t ppage_paddr; | ||
d->value = value; | |||
list_add_tail(*bn, d); | /* Check if address is covered by any VMM range */ | ||
if (! sos_kmem_vmm_is_valid_vaddr(faulting_vaddr)) | |||
{ | |||
/* No: The page fault is out of any kernel virtual region. For | |||
the moment, we don't handle this. */ | |||
dump_backtrace(ctxt, | |||
bootstrap_stack_bottom, | |||
bootstrap_stack_size, | |||
TRUE, TRUE); | |||
sos_display_fatal_error("Unresolved page Fault on access to address 0x%x (info=%x)!", | |||
(unsigned)faulting_vaddr, | |||
(unsigned)sos_cpu_kstate_get_EX_info(ctxt)); | |||
SOS_ASSERT_FATAL(! "Got page fault (note: demand paging is disabled)"); | |||
} | |||
/* | |||
* Demand paging | |||
*/ | |||
/* Update the number of demand paging requests handled */ | |||
demand_paging_count ++; | |||
display_bits(0, 0, | |||
SOS_X86_VIDEO_FG_LTRED | SOS_X86_VIDEO_BG_BLUE, | |||
demand_paging_count); | |||
/* Allocate a new page for the virtual address */ | |||
ppage_paddr = sos_physmem_ref_physpage_new(FALSE); | |||
if (! ppage_paddr) | |||
SOS_ASSERT_FATAL(! "TODO: implement swap. (Out of mem in demand paging because no swap for kernel yet !)"); | |||
SOS_ASSERT_FATAL(SOS_OK == sos_paging_map(ppage_paddr, | |||
SOS_PAGE_ALIGN_INF(faulting_vaddr), | |||
FALSE, | |||
SOS_VM_MAP_PROT_READ | |||
| SOS_VM_MAP_PROT_WRITE | |||
| SOS_VM_MAP_ATOMIC)); | |||
sos_physmem_unref_physpage(ppage_paddr); | |||
/* Ok, we can now return to interrupted context */ | |||
/* Add a new digit before the MSD */ | |||
void bn_push_msd(big_number_t * bn, char value) | /* ====================================================================== | ||
* Demonstrate the use of the CPU kernet context management API: | |||
* - A coroutine prints "Hlowrd" and switches to the other after each | |||
* letter | |||
* - A coroutine prints "el ol\n" and switches back to the other after | |||
* each letter. | |||
* The first to reach the '\n' returns back to main. | |||
*/ | |||
struct sos_cpu_kstate *ctxt_hello1; | |||
struct sos_cpu_kstate *ctxt_hello2; | |||
struct sos_cpu_kstate *ctxt_main; | |||
sos_vaddr_t hello1_stack, hello2_stack; | |||
static void reclaim_stack(sos_vaddr_t stack_vaddr) | |||
struct digit *d; | sos_kfree(stack_vaddr); | ||
d = (struct digit*) sos_kmalloc(sizeof(struct digit), 0); | |||
SOS_ASSERT_FATAL(d != NULL); | |||
d->value = value; | |||
list_add_head(*bn, d); | |||
/* Construct a big integer from a (machine) integer */ | static void exit_hello12(sos_vaddr_t stack_vaddr) | ||
big_number_t bn_new(unsigned long int i) | |||
big_number_t retval; | sos_cpu_kstate_exit_to(ctxt_main, | ||
(sos_cpu_kstate_function_arg1_t*) reclaim_stack, | |||
stack_vaddr); | |||
} | |||
list_init(retval); | |||
do | static void hello1 (char *str) | ||
{ | |||
for ( ; *str != '\n' ; str++) | |||
bn_push_msd(&retval, i%10); | sos_bochs_printf("hello1: %c\n", *str); | ||
i /= 10; | sos_cpu_kstate_switch(& ctxt_hello1, ctxt_hello2); | ||
while (i != 0); | |||
return retval; | /* You can uncomment this in case you explicitly want to exit | ||
now. But returning from the function will do the same */ | |||
/* sos_cpu_kstate_exit_to(ctxt_main, | |||
(sos_cpu_kstate_function_arg1_t*) reclaim_stack, | |||
hello1_stack); */ | |||
/* Create a new big integer from another big integer */ | static void hello2 (char *str) | ||
big_number_t bn_copy(const big_number_t bn) | |||
big_number_t retval; | for ( ; *str != '\n' ; str++) | ||
int nb_elts; | |||
struct digit *d; | |||
list_init(retval); | |||
list_foreach(bn, d, nb_elts) | |||
bn_push_lsd(&retval, d->value); | sos_bochs_printf("hello2: %c\n", *str); | ||
sos_cpu_kstate_switch(& ctxt_hello2, ctxt_hello1); | |||
return retval; | /* You can uncomment this in case you explicitly want to exit | ||
now. But returning from the function will do the same */ | |||
/* sos_cpu_kstate_exit_to(ctxt_main, | |||
(sos_cpu_kstate_function_arg1_t*) reclaim_stack, | |||
hello2_stack); */ | |||
/* Free the memory used by a big integer */ | void print_hello_world () | ||
void bn_del(big_number_t * bn) | |||
struct digit *d; | #define DEMO_STACK_SIZE 1024 | ||
/* Allocate the stacks */ | |||
hello1_stack = sos_kmalloc(DEMO_STACK_SIZE, 0); | |||
hello2_stack = sos_kmalloc(DEMO_STACK_SIZE, 0); | |||
/* Initialize the coroutines' contexts */ | |||
sos_cpu_kstate_init(&ctxt_hello1, | |||
(sos_cpu_kstate_function_arg1_t*) hello1, | |||
(sos_ui32_t) "Hlowrd", | |||
(sos_vaddr_t) hello1_stack, DEMO_STACK_SIZE, | |||
(sos_cpu_kstate_function_arg1_t*) exit_hello12, | |||
(sos_ui32_t) hello1_stack); | |||
sos_cpu_kstate_init(&ctxt_hello2, | |||
(sos_cpu_kstate_function_arg1_t*) hello2, | |||
(sos_ui32_t) "el ol\n", | |||
(sos_vaddr_t) hello2_stack, DEMO_STACK_SIZE, | |||
(sos_cpu_kstate_function_arg1_t*) exit_hello12, | |||
(sos_ui32_t) hello2_stack); | |||
/* Go to first coroutine */ | |||
sos_bochs_printf("Printing Hello World\\n...\n"); | |||
sos_cpu_kstate_switch(& ctxt_main, ctxt_hello1); | |||
list_collapse(*bn, d) | /* The first coroutine to reach the '\n' switched back to us */ | ||
{ | sos_bochs_printf("Back in main !\n"); | ||
sos_kfree((sos_vaddr_t)d); | |||
} | |||
/* Shift left a big integer: bn := bn*10^shift */ | /* ====================================================================== | ||
void bn_shift(big_number_t *bn, int shift) | * Generate page faults on an unmapped but allocated kernel virtual | ||
* region, which results in a series of physical memory mappings for the | |||
* faulted pages. | |||
*/ | |||
static void test_demand_paging(int nb_alloc_vpages, int nb_alloc_ppages) | |||
for ( ; shift > 0 ; shift --) | int i; | ||
sos_vaddr_t base_vaddr; | |||
sos_x86_videomem_printf(10, 0, | |||
SOS_X86_VIDEO_BG_BLUE | SOS_X86_VIDEO_FG_LTGREEN, | |||
"Demand paging test (alloc %dMB of VMM, test %dkB RAM)", | |||
nb_alloc_vpages >> 8, nb_alloc_ppages << 2); | |||
/* Allocate virtual memory */ | |||
base_vaddr = sos_kmem_vmm_alloc(nb_alloc_vpages, 0); | |||
SOS_ASSERT_FATAL(base_vaddr != (sos_vaddr_t)NULL); | |||
sos_x86_videomem_printf(11, 0, | |||
SOS_X86_VIDEO_BG_BLUE | SOS_X86_VIDEO_FG_YELLOW, | |||
"Allocated virtual region [0x%x, 0x%x[", | |||
base_vaddr, | |||
base_vaddr + nb_alloc_vpages*SOS_PAGE_SIZE); | |||
/* Now use part of it in physical memory */ | |||
for (i = 0 ; (i < nb_alloc_ppages) && (i < nb_alloc_vpages) ; i++) | |||
bn_push_lsd(bn, 0); | /* Compute an address inside the range */ | ||
sos_ui32_t *value, j; | |||
sos_vaddr_t vaddr = base_vaddr; | |||
vaddr += (nb_alloc_vpages - (i + 1))*SOS_PAGE_SIZE; | |||
vaddr += 2345; | |||
sos_x86_videomem_printf(12, 0, | |||
SOS_X86_VIDEO_BG_BLUE | SOS_X86_VIDEO_FG_YELLOW, | |||
"Writing %d at virtual address 0x%x...", | |||
i, vaddr); | |||
/* Write at this address */ | |||
value = (sos_ui32_t*)vaddr; | |||
*value = i; | |||
/* Yep ! A new page should normally have been allocated for us */ | |||
sos_x86_videomem_printf(13, 0, | |||
SOS_X86_VIDEO_BG_BLUE | SOS_X86_VIDEO_FG_YELLOW, | |||
"Value read at address 0x%x = %d", | |||
vaddr, (unsigned)*value); | |||
SOS_ASSERT_FATAL(SOS_OK == sos_kmem_vmm_free(base_vaddr)); | |||
/* Yep ! A new page should normally have been allocated for us */ | |||
sos_x86_videomem_printf(14, 0, | |||
SOS_X86_VIDEO_BG_BLUE | SOS_X86_VIDEO_FG_YELLOW, | |||
"Done (area un-allocated)"); | |||
} | } | ||
/* Dump the big integer in bochs */ | |||
void bn_print_bochs(const big_number_t bn) | /* ====================================================================== | ||
* Shows how the backtrace stuff works | |||
*/ | |||
/* Recursive function. Print the backtrace from the innermost function */ | |||
static void test_backtrace(int i, int magic, sos_vaddr_t stack_bottom, | |||
sos_size_t stack_size) | |||
int nb_elts; | if (i <= 0) | ||
const struct digit *d; | { | ||
/* The page fault exception handler will print the backtrace of | |||
this function, because address 0x42 is not mapped */ | |||
*((char*)0x42) = 12; | |||
if (list_is_empty(bn)) | /* More direct variant: */ | ||
sos_bochs_printf("0"); | /* dump_backtrace(NULL, stack_bottom, stack_size, TRUE, TRUE); */ | ||
} | |||
list_foreach(bn, d, nb_elts) | test_backtrace(i-1, magic, stack_bottom, stack_size); | ||
sos_bochs_printf("%d", d->value); | |||
/* Dump the big integer on the console */ | |||
void bn_print_console(unsigned char row, unsigned char col, | /* ====================================================================== | ||
unsigned char attribute, | * Parsing of Mathematical expressions | ||
const big_number_t bn, | * | ||
int nb_decimals) | * This is a recursive lexer/parser/evaluator for arithmetical | ||
* expressions. Supports both binary +/-* and unary +- operators, as | |||
* well as parentheses. | |||
* | |||
* Terminal tokens (Lexer): | |||
* - Number: positive integer number | |||
* - Variable: ascii name (regexp: [a-zA-Z]+) | |||
* - Operator: +*-/ | |||
* - Opening/closing parentheses | |||
* | |||
* Grammar (Parser): | |||
* Expression ::= Term E' | |||
* Expr_lr ::= + Term Expr_lr | - Term Expr_lr | Nothing | |||
* Term ::= Factor Term_lr | |||
* Term_lr ::= * Factor Term_lr | / Factor Term_lr | Nothing | |||
* Factor ::= - Factor | + Factor | Scalar | ( Expression ) | |||
* Scalar ::= Number | Variable | |||
* | |||
* Note. This is the left-recursive equivalent of the following basic grammar: | |||
* Expression ::= Expression + Term | Expression - Term | |||
* Term ::= Term * Factor | Term / Factor | |||
* factor ::= - Factor | + Factor | Scalar | Variable | ( Expression ) | |||
* Scalar ::= Number | Variable | |||
* | |||
* The parsing is composed of a 3 stages pipeline: | |||
* - The reader: reads a string 1 character at a time, transferring | |||
* the control back to lexer after each char. This function shows the | |||
* interest in using coroutines, because its state (str) is | |||
* implicitely stored in the stack between each iteration. | |||
* - The lexer: consumes the characters from the reader and identifies | |||
* the terminal tokens, 1 token at a time, transferring control back | |||
* to the parser after each token. This function shows the interest | |||
* in using coroutines, because its state (c and got_what_before) is | |||
* implicitely stored in the stack between each iteration. | |||
* - The parser: consumes the tokens from the lexer and builds the | |||
* syntax tree of the expression. There is no real algorithmic | |||
* interest in defining a coroutine devoted to do this. HOWEVER, we | |||
* do use one for that because this allows us to switch to a much | |||
* deeper stack. Actually, the parser is highly recursive, so that | |||
* the default 16kB stack of the sos_main() function might not be | |||
* enough. Here, we switch to a 64kB stack, which is safer for | |||
* recursive functions. The Parser uses intermediary functions: these | |||
* are defined and implemented as internal nested functions. This is | |||
* just for the sake of clarity, and is absolutely not mandatory for | |||
* the algorithm: one can transfer these functions out of the parser | |||
* function without restriction. | |||
* | |||
* The evaluator is another recursive function that reuses the | |||
* parser's stack to evaluate the parsed expression with the given | |||
* values for the variables present in the expression. As for the | |||
* parser function, this function defines and uses a nested function, | |||
* which can be extracted from the main evaluation function at will. | |||
* | |||
* All these functions support a kind of "exception" feature: when | |||
* something goes wrong, control is transferred DIRECTLY back to the | |||
* sos_main() context, without unrolling the recursions. This shows | |||
* how exceptions basically work, but one should not consider this as | |||
* a reference exceptions implementation. Real exception mechanisms | |||
* (such as that in the C++ language) call the destructors to the | |||
* objects allocated on the stack during the "stack unwinding" process | |||
* upon exception handling, which complicates a lot the mechanism. We | |||
* don't have real Objects here (in the OOP sense, full-featured with | |||
* destructors), so we don't have to complicate things. | |||
* | |||
* After this little coroutine demo, one should forget all about such | |||
* a low-level manual direct manipulation of stacks. This would | |||
* probably mess up the whole kernel to do what we do here (locked | |||
* resources such as mutex/semaphore won't be correctly unlocked, | |||
* ...). Higher level "kernel thread" primitives will soon be | |||
* presented, which provide a higher-level set of APIs to manage CPU | |||
* contexts. You'll have to use EXCLUSIVELY those APIs. If you still | |||
* need a huge stack to do recursion for example, please don't even | |||
* think of changing manually the stack for something bigger ! Simply | |||
* rethink your algorithm, making it non-recursive. | |||
*/ | |||
/* The stacks involved */ | |||
static char stack_reader[1024]; | |||
static char stack_lexer[1024]; | |||
static char deep_stack[65536]; /* For the parser and the evaluator */ | |||
/* The CPU states for the various coroutines */ | |||
static struct sos_cpu_kstate *st_reader, *st_lexer, *st_parser, | |||
*st_eval, *st_free, *st_main; | |||
/* | |||
* Default exit/reclaim functions: return control to the "sos_main()" | |||
* context | |||
*/ | |||
static void reclaim(int unused) | |||
if (list_is_empty(bn)) | } | ||
sos_x86_videomem_printf(row, col, attribute, "0"); | static void func_exit(sos_ui32_t unused) | ||
else | { | ||
{ | sos_cpu_kstate_exit_to(st_main, (sos_cpu_kstate_function_arg1_t*)reclaim, 0); | ||
int nb_elts; | } | ||
const struct digit *d; | |||
unsigned char x = col; | |||
list_foreach(bn, d, nb_elts) | |||
{ | |||
if (nb_elts == 0) | |||
{ | |||
sos_x86_videomem_printf(row, x, attribute, "%d.", d->value); | |||
x += 2; | |||
} | |||
else if (nb_elts < nb_decimals) | |||
{ | |||
sos_x86_videomem_printf(row, x, attribute, "%d", d->value); | |||
x ++; | |||
} | |||
} | |||
sos_x86_videomem_printf(row, x, attribute, " . 10^{%d} ", nb_elts-1); | /* | ||
* The reader coroutine and associated variable. This coroutine could | |||
* have been a normal function, except that the current parsed | |||
* character would have to be stored somewhere. | |||
*/ | |||
static char data_reader_to_lexer; | |||
static void func_reader(const char *str) | |||
{ | |||
for ( ; str && (*str != '\0') ; str++) | |||
{ | |||
data_reader_to_lexer = *str; | |||
sos_cpu_kstate_switch(& st_reader, st_lexer); | |||
data_reader_to_lexer = '\0'; | |||
sos_cpu_kstate_switch(& st_reader, st_lexer); | |||
} | } | ||
/* Result is the addition of 2 big integers */ | /* | ||
big_number_t bn_add (const big_number_t bn1, const big_number_t bn2) | * The Lexer coroutine and associated types/variables. This coroutine | ||
* could have been a normal function, except that the current parsed | |||
* character, token and previous token would have to be stored | |||
* somewhere. | |||
*/ | |||
#define STR_VAR_MAXLEN 16 | |||
static struct lex_elem | |||
{ | |||
enum { LEX_IS_NUMBER, LEX_IS_OPER, LEX_IS_VAR, | |||
LEX_IS_OPENPAR, LEX_IS_CLOSEPAR, LEX_END } type; | |||
union { | |||
int number; | |||
char operator; | |||
char var[STR_VAR_MAXLEN]; | |||
}; | |||
} data_lexer_to_parser; | |||
static void func_lexer(sos_ui32_t unused) | |||
big_number_t retval; | char c; | ||
const struct digit *d1, *d2; | enum { GOT_SPACE, GOT_NUM, GOT_OP, GOT_STR, | ||
sos_bool_t bn1_end = FALSE, bn2_end = FALSE; | GOT_OPENPAR, GOT_CLOSEPAR } got_what, got_what_before; | ||
char carry = 0; | |||
list_init(retval); | data_lexer_to_parser.number = 0; | ||
d1 = list_get_tail(bn1); | got_what_before = GOT_SPACE; | ||
bn1_end = list_is_empty(bn1); | |||
d2 = list_get_tail(bn2); | |||
bn2_end = list_is_empty(bn2); | |||
{ | { | ||
if (! bn1_end) | /* Consume one character from the reader */ | ||
carry += d1->value; | sos_cpu_kstate_switch(& st_lexer, st_reader); | ||
if (! bn2_end) | c = data_reader_to_lexer; | ||
carry += d2->value; | |||
/* Classify the consumed character */ | |||
bn_push_msd(&retval, carry % 10); | if ( (c >= '0') && (c <= '9') ) | ||
carry /= 10; | got_what = GOT_NUM; | ||
else if ( (c == '+') || (c == '-') || (c == '*') || (c == '/') ) | |||
if (! bn1_end) | got_what = GOT_OP; | ||
d1 = d1->prev; | else if ( ( (c >= 'a') && (c <= 'z') ) | ||
if (! bn2_end) | || ( (c >= 'A') && (c <= 'Z') ) ) | ||
d2 = d2->prev; | got_what = GOT_STR; | ||
if (d1 == list_get_tail(bn1)) | else if (c == '(') | ||
bn1_end = TRUE; | got_what = GOT_OPENPAR; | ||
if (d2 == list_get_tail(bn2)) | else if (c == ')') | ||
bn2_end = TRUE; | got_what = GOT_CLOSEPAR; | ||
} | else | ||
while (!bn1_end || !bn2_end); | got_what = GOT_SPACE; | ||
/* Determine whether the current token is ended */ | |||
if ( (got_what != got_what_before) | |||
|| (got_what_before == GOT_OP) | |||
|| (got_what_before == GOT_OPENPAR) | |||
|| (got_what_before == GOT_CLOSEPAR) ) | |||
{ | |||
/* return control back to the parser if the previous token | |||
has been recognized */ | |||
if ( (got_what_before != GOT_SPACE) ) | |||
sos_cpu_kstate_switch(& st_lexer, st_parser); | |||
if (carry > 0) | data_lexer_to_parser.number = 0; | ||
{ | } | ||
bn_push_msd(&retval, carry); | |||
/* Update the token being currently recognized */ | |||
if (got_what == GOT_OP) | |||
{ | |||
data_lexer_to_parser.type = LEX_IS_OPER; | |||
data_lexer_to_parser.operator = c; | |||
} | |||
else if (got_what == GOT_NUM) | |||
{ | |||
data_lexer_to_parser.type = LEX_IS_NUMBER; | |||
data_lexer_to_parser.number *= 10; | |||
data_lexer_to_parser.number += (c - '0'); | |||
} | |||
else if (got_what == GOT_STR) | |||
{ | |||
char to_cat[] = { c, '\0' }; | |||
data_lexer_to_parser.type = LEX_IS_VAR; | |||
strzcat(data_lexer_to_parser.var, to_cat, STR_VAR_MAXLEN); | |||
} | |||
else if (got_what == GOT_OPENPAR) | |||
data_lexer_to_parser.type = LEX_IS_OPENPAR; | |||
else if (got_what == GOT_CLOSEPAR) | |||
data_lexer_to_parser.type = LEX_IS_CLOSEPAR; | |||
got_what_before = got_what; | |||
while (c != '\0'); | |||
return retval; | /* Transfer last recognized token to the parser */ | ||
if ( (got_what_before != GOT_SPACE) ) | |||
sos_cpu_kstate_switch(& st_lexer, st_parser); | |||
/* Signal that no more token are available */ | |||
data_lexer_to_parser.type = LEX_END; | |||
sos_cpu_kstate_switch(& st_lexer, st_parser); | |||
/* Exception: parser asks for a token AFTER having received the last | |||
one */ | |||
sos_bochs_printf("Error: end of string already reached !\n"); | |||
sos_cpu_kstate_switch(& st_lexer, st_main); | |||
/* Result is the multiplication of a big integer by a single digit */ | /* | ||
big_number_t bn_muli (const big_number_t bn, char digit) | * The Parser coroutine and associated types/variables | ||
*/ | |||
struct syntax_node | |||
big_number_t retval; | enum { YY_IS_BINOP, YY_IS_UNAROP, YY_IS_NUM, YY_IS_VAR } type; | ||
int nb_elts; | union | ||
char carry = 0; | { | ||
const struct digit *d; | int number; | ||
char var[STR_VAR_MAXLEN]; | |||
struct | |||
{ | |||
char op; | |||
struct syntax_node *parm_left, *parm_right; | |||
} binop; | |||
struct | |||
{ | |||
char op; | |||
struct syntax_node *parm; | |||
} unarop; | |||
}; | |||
}; | |||
list_init(retval); | static void func_parser(struct syntax_node ** syntax_tree) | ||
list_foreach_backward(bn, d, nb_elts) | { | ||
static struct syntax_node *alloc_node_num(int val); | |||
static struct syntax_node *alloc_node_var(const char * name); | |||
static struct syntax_node *alloc_node_binop(char op, | |||
struct syntax_node *parm_left, | |||
struct syntax_node *parm_right); | |||
static struct syntax_node *alloc_node_unarop(char op, | |||
struct syntax_node *parm); | |||
static struct syntax_node * get_expr(); | |||
static struct syntax_node * get_expr_lr(struct syntax_node *n); | |||
static struct syntax_node * get_term(); | |||
static struct syntax_node * get_term_lr(struct syntax_node *n); | |||
static struct syntax_node * get_factor(); | |||
static struct syntax_node * get_scalar(); | |||
/* Create a new node to store a number */ | |||
static struct syntax_node *alloc_node_num(int val) | |||
{ | |||
struct syntax_node *n | |||
= (struct syntax_node*) sos_kmalloc(sizeof(struct syntax_node), 0); | |||
n->type = YY_IS_NUM; | |||
n->number = val; | |||
return n; | |||
} | |||
/* Create a new node to store a variable */ | |||
static struct syntax_node *alloc_node_var(const char * name) | |||
{ | |||
struct syntax_node *n | |||
= (struct syntax_node*) sos_kmalloc(sizeof(struct syntax_node), 0); | |||
n->type = YY_IS_VAR; | |||
strzcpy(n->var, name, STR_VAR_MAXLEN); | |||
return n; | |||
} | |||
/* Create a new node to store a binary operator */ | |||
static struct syntax_node *alloc_node_binop(char op, | |||
struct syntax_node *parm_left, | |||
struct syntax_node *parm_right) | |||
carry += d->value * digit; | struct syntax_node *n | ||
bn_push_msd(&retval, carry % 10); | = (struct syntax_node*) sos_kmalloc(sizeof(struct syntax_node), 0); | ||
carry /= 10; | n->type = YY_IS_BINOP; | ||
n->binop.op = op; | |||
n->binop.parm_left = parm_left; | |||
n->binop.parm_right = parm_right; | |||
return n; | |||
} | |||
/* Create a new node to store a unary operator */ | |||
static struct syntax_node *alloc_node_unarop(char op, | |||
struct syntax_node *parm) | |||
{ | |||
struct syntax_node *n | |||
= (struct syntax_node*) sos_kmalloc(sizeof(struct syntax_node), 0); | |||
n->type = YY_IS_UNAROP; | |||
n->unarop.op = op; | |||
n->unarop.parm = parm; | |||
return n; | |||
if (carry > 0) | /* Raise an exception: transfer control back to main context, | ||
without unrolling the whole recursion */ | |||
static void parser_exception(const char *str) | |||
bn_push_msd(&retval, carry); | sos_bochs_printf("Parser exception: %s\n", str); | ||
sos_cpu_kstate_switch(& st_parser, st_main); | |||
return retval; | /* Consume the current terminal "number" token and ask for a new | ||
token */ | |||
static int get_number() | |||
{ | |||
int v; | |||
if (data_lexer_to_parser.type != LEX_IS_NUMBER) | |||
parser_exception("Expected number"); | |||
v = data_lexer_to_parser.number; | |||
sos_cpu_kstate_switch(& st_parser, st_lexer); | |||
return v; | |||
} | |||
/* Consume the current terminal "variable" token and ask for a new | |||
token */ | |||
static void get_str(char name[STR_VAR_MAXLEN]) | |||
{ | |||
if (data_lexer_to_parser.type != LEX_IS_VAR) | |||
parser_exception("Expected variable"); | |||
strzcpy(name, data_lexer_to_parser.var, STR_VAR_MAXLEN); | |||
sos_cpu_kstate_switch(& st_parser, st_lexer); | |||
} | |||
/* Consume the current terminal "operator" token and ask for a new | |||
token */ | |||
static char get_op() | |||
{ | |||
char op; | |||
if (data_lexer_to_parser.type != LEX_IS_OPER) | |||
parser_exception("Expected operator"); | |||
op = data_lexer_to_parser.operator; | |||
sos_cpu_kstate_switch(& st_parser, st_lexer); | |||
return op; | |||
} | |||
/* Consume the current terminal "parenthese" token and ask for a new | |||
token */ | |||
static void get_par() | |||
{ | |||
if ( (data_lexer_to_parser.type != LEX_IS_OPENPAR) | |||
&& (data_lexer_to_parser.type != LEX_IS_CLOSEPAR) ) | |||
parser_exception("Expected parenthese"); | |||
sos_cpu_kstate_switch(& st_parser, st_lexer); | |||
} | |||
/* Parse an Expression */ | |||
static struct syntax_node * get_expr() | |||
{ | |||
struct syntax_node *t = get_term(); | |||
return get_expr_lr(t); | |||
} | |||
/* Parse an Expr_lr */ | |||
static struct syntax_node * get_expr_lr(struct syntax_node *n) | |||
{ | |||
if ( (data_lexer_to_parser.type == LEX_IS_OPER) | |||
&& ( (data_lexer_to_parser.operator == '+') | |||
|| (data_lexer_to_parser.operator == '-') ) ) | |||
{ | |||
char op = get_op(); | |||
struct syntax_node *term = get_term(); | |||
struct syntax_node *node_op = alloc_node_binop(op, n, term); | |||
return get_expr_lr(node_op); | |||
} | |||
return n; | |||
} | |||
/* Parse a Term */ | |||
static struct syntax_node * get_term() | |||
{ | |||
struct syntax_node *f1 = get_factor(); | |||
return get_term_lr(f1); | |||
} | |||
/* Parse a Term_lr */ | |||
static struct syntax_node * get_term_lr(struct syntax_node *n) | |||
{ | |||
if ( (data_lexer_to_parser.type == LEX_IS_OPER) | |||
&& ( (data_lexer_to_parser.operator == '*') | |||
|| (data_lexer_to_parser.operator == '/') ) ) | |||
{ | |||
char op = get_op(); | |||
struct syntax_node *factor = get_factor(); | |||
struct syntax_node *node_op = alloc_node_binop(op, n, factor); | |||
return get_term_lr(node_op); | |||
} | |||
return n; | |||
} | |||
/* Parse a Factor */ | |||
static struct syntax_node * get_factor() | |||
{ | |||
if ( (data_lexer_to_parser.type == LEX_IS_OPER) | |||
&& ( (data_lexer_to_parser.operator == '-') | |||
|| (data_lexer_to_parser.operator == '+') ) ) | |||
{ char op = data_lexer_to_parser.operator; | |||
get_op(); return alloc_node_unarop(op, get_factor()); } | |||
else if (data_lexer_to_parser.type == LEX_IS_OPENPAR) | |||
{ | |||
struct syntax_node *expr; | |||
get_par(); | |||
expr = get_expr(); | |||
if (data_lexer_to_parser.type != LEX_IS_CLOSEPAR) | |||
parser_exception("Mismatched parentheses"); | |||
get_par(); | |||
return expr; | |||
} | |||
return get_scalar(); | |||
} | |||
/* Parse a Scalar */ | |||
static struct syntax_node * get_scalar() | |||
{ | |||
if (data_lexer_to_parser.type != LEX_IS_NUMBER) | |||
{ | |||
char var[STR_VAR_MAXLEN]; | |||
get_str(var); | |||
return alloc_node_var(var); | |||
} | |||
return alloc_node_num(get_number()); | |||
} | |||
/* | |||
* Body of the function | |||
*/ | |||
/* Get the first token */ | |||
sos_cpu_kstate_switch(& st_parser, st_lexer); | |||
/* Begin the parsing ! */ | |||
*syntax_tree = get_expr(); | |||
/* The result is returned in the syntax_tree parameter */ | |||
/* Result is the multiplication of 2 big integers */ | /* | ||
big_number_t bn_mult(const big_number_t bn1, const big_number_t bn2) | * Setup the parser's pipeline | ||
*/ | |||
static struct syntax_node * parse_expression(const char *expr) | |||
int shift = 0; | struct syntax_node *retval = NULL; | ||
big_number_t retval; | |||
int nb_elts; | |||
struct digit *d; | |||
list_init(retval); | |||
list_foreach_backward(bn2, d, nb_elts) | |||
{ | |||
big_number_t retmult = bn_muli(bn1, d->value); | |||
big_number_t old_retval = retval; | |||
bn_shift(& retmult, shift); | |||
retval = bn_add(old_retval, retmult); | |||
bn_del(& retmult); | |||
bn_del(& old_retval); | |||
shift ++; | |||
} | |||
/* Build the context of the functions in the pipeline */ | |||
sos_cpu_kstate_init(& st_reader, | |||
(sos_cpu_kstate_function_arg1_t*)func_reader, | |||
(sos_ui32_t)expr, | |||
(sos_vaddr_t)stack_reader, sizeof(stack_reader), | |||
(sos_cpu_kstate_function_arg1_t*)func_exit, 0); | |||
sos_cpu_kstate_init(& st_lexer, | |||
(sos_cpu_kstate_function_arg1_t*)func_lexer, | |||
0, | |||
(sos_vaddr_t)stack_lexer, sizeof(stack_lexer), | |||
(sos_cpu_kstate_function_arg1_t*)func_exit, 0); | |||
sos_cpu_kstate_init(& st_parser, | |||
(sos_cpu_kstate_function_arg1_t*)func_parser, | |||
(sos_ui32_t) /* syntax tree ! */&retval, | |||
(sos_vaddr_t)deep_stack, sizeof(deep_stack), | |||
(sos_cpu_kstate_function_arg1_t*)func_exit, 0); | |||
/* Parse the expression */ | |||
sos_cpu_kstate_switch(& st_main, st_parser); | |||
return retval; | return retval; | ||
} | } | ||
/* Result is the factorial of an integer */ | /* | ||
big_number_t bn_fact(unsigned long int v) | * The Evaluator coroutine and associated types/variables | ||
*/ | |||
struct func_eval_params | |||
unsigned long int i; | const struct syntax_node *e; | ||
big_number_t retval = bn_new(1); | const char **var_name; | ||
for (i = 1 ; i <= v ; i++) | int *var_val; | ||
{ | int nb_vars; | ||
big_number_t I = bn_new(i); | |||
big_number_t tmp = bn_mult(retval, I); | int result; | ||
sos_x86_videomem_printf(4, 0, | }; | ||
SOS_X86_VIDEO_BG_BLUE | SOS_X86_VIDEO_FG_LTGREEN, | |||
"%d! = ", (int)i); | static void func_eval(struct func_eval_params *parms) | ||
bn_print_console(4, 8, SOS_X86_VIDEO_BG_BLUE | SOS_X86_VIDEO_FG_WHITE, | { | ||
tmp, 55); | /* The internal (recursive) nested function to evaluate each node of | ||
bn_del(& I); | the syntax tree */ | ||
bn_del(& retval); | static int rec_eval(const struct syntax_node *n, | ||
retval = tmp; | const char* var_name[], int var_val[], int nb_vars) | ||
{ | |||
switch (n->type) | |||
{ | |||
case YY_IS_NUM: | |||
return n->number; | |||
case YY_IS_VAR: | |||
{ | |||
int i; | |||
for (i = 0 ; i < nb_vars ; i++) | |||
if (0 == strcmp(var_name[i], n->var)) | |||
return var_val[i]; | |||
/* Exception: no variable with that name ! */ | |||
sos_bochs_printf("ERROR: unknown variable %s\n", n->var); | |||
sos_cpu_kstate_switch(& st_eval, st_main); | |||
} | |||
case YY_IS_BINOP: | |||
{ | |||
int left = rec_eval(n->binop.parm_left, | |||
var_name, var_val, nb_vars); | |||
int right = rec_eval(n->binop.parm_right, | |||
var_name, var_val, nb_vars); | |||
switch (n->binop.op) | |||
{ | |||
case '+': return left + right; | |||
case '-': return left - right; | |||
case '*': return left * right; | |||
case '/': return left / right; | |||
default: | |||
/* Exception: no such operator (INTERNAL error) ! */ | |||
sos_bochs_printf("ERROR: unknown binop %c\n", n->binop.op); | |||
sos_cpu_kstate_switch(& st_eval, st_main); | |||
} | |||
} | |||
case YY_IS_UNAROP: | |||
{ | |||
int arg = rec_eval(n->unarop.parm, var_name, var_val, nb_vars); | |||
switch (n->unarop.op) | |||
{ | |||
case '-': return -arg; | |||
case '+': return arg; | |||
default: | |||
/* Exception: no such operator (INTERNAL error) ! */ | |||
sos_bochs_printf("ERROR: unknown unarop %c\n", n->unarop.op); | |||
sos_cpu_kstate_switch(& st_eval, st_main); | |||
} | |||
} | |||
} | |||
/* Exception: no such syntax node (INTERNAL error) ! */ | |||
sos_bochs_printf("ERROR: invalid node type\n"); | |||
sos_cpu_kstate_switch(& st_eval, st_main); | |||
return -1; /* let's make gcc happy */ | |||
return retval; | |||
/* | |||
* Function BODY | |||
*/ | |||
/* Update p.result returned back to calling function */ | |||
parms->result | |||
= rec_eval(parms->e, parms->var_name, parms->var_val, parms->nb_vars); | |||
} | |||
/* | |||
* Change the stack for something larger in order to call the | |||
* recursive function above in a safe way | |||
*/ | |||
static int eval_expression(const struct syntax_node *e, | |||
const char* var_name[], int var_val[], int nb_vars) | |||
{ | |||
struct func_eval_params p | |||
= (struct func_eval_params){ .e=e, | |||
.var_name=var_name, | |||
.var_val=var_val, | |||
.nb_vars=nb_vars, | |||
.result = 0 }; | |||
sos_cpu_kstate_init(& st_eval, | |||
(sos_cpu_kstate_function_arg1_t*)func_eval, | |||
(sos_ui32_t)/* p.result is modified upon success */&p, | |||
(sos_vaddr_t)deep_stack, sizeof(deep_stack), | |||
(sos_cpu_kstate_function_arg1_t*)func_exit, 0); | |||
/* Go ! */ | |||
sos_cpu_kstate_switch(& st_main, st_eval); | |||
return p.result; | |||
void bn_test() | /* | ||
* Function to free the syntax tree | |||
*/ | |||
static void func_free(struct syntax_node *n) | |||
big_number_t bn = bn_fact(1000); | switch (n->type) | ||
sos_bochs_printf("1000! = "); | { | ||
bn_print_bochs(bn); | case YY_IS_NUM: | ||
sos_bochs_printf("\n"); | case YY_IS_VAR: | ||
break; | |||
case YY_IS_BINOP: | |||
func_free(n->binop.parm_left); | |||
func_free(n->binop.parm_right); | |||
break; | |||
case YY_IS_UNAROP: | |||
func_free(n->unarop.parm); | |||
break; | |||
} | |||
sos_kfree((sos_vaddr_t)n); | |||
} | } | ||
/* | |||
* Change the stack for something larger in order to call the | |||
* recursive function above in a safe way | |||
*/ | |||
static void free_syntax_tree(struct syntax_node *tree) | |||
{ | |||
sos_cpu_kstate_init(& st_free, | |||
(sos_cpu_kstate_function_arg1_t*)func_free, | |||
(sos_ui32_t)tree, | |||
(sos_vaddr_t)deep_stack, sizeof(deep_stack), | |||
(sos_cpu_kstate_function_arg1_t*)func_exit, 0); | |||
/* The C entry point of our operating system */ | /* Go ! */ | ||
sos_cpu_kstate_switch(& st_main, st_free); | |||
} | |||
/* ====================================================================== | |||
* The C entry point of our operating system | |||
*/ | |||
{ | { | ||
unsigned i; | unsigned i; | ||
sos_paddr_t sos_kernel_core_base_paddr, sos_kernel_core_top_paddr; | sos_paddr_t sos_kernel_core_base_paddr, sos_kernel_core_top_paddr; | ||
struct syntax_node *syntax_tree; | |||
/* Grub sends us a structure, called multiboot_info_t with a lot of | /* Grub sends us a structure, called multiboot_info_t with a lot of | ||
precious informations about the system, see the multiboot | precious informations about the system, see the multiboot | ||
|
| ||
sos_bochs_putstring("Message in a bochs\n"); | sos_bochs_putstring("Message in a bochs\n"); | ||
/* Setup CPU segmentation and IRQ subsystem */ | /* Setup CPU segmentation and IRQ subsystem */ | ||
sos_gdt_setup(); | sos_gdt_subsystem_setup(); | ||
sos_idt_setup(); | sos_idt_subsystem_setup(); | ||
/* Setup SOS IRQs and exceptions subsystem */ | /* Setup SOS IRQs and exceptions subsystem */ | ||
sos_exceptions_setup(); | sos_exception_subsystem_setup(); | ||
sos_irq_setup(); | sos_irq_subsystem_setup(); | ||
/* Configure the timer so as to raise the IRQ0 at a 100Hz rate */ | /* Configure the timer so as to raise the IRQ0 at a 100Hz rate */ | ||
sos_i8254_set_frequency(100); | sos_i8254_set_frequency(100); | ||
if (magic != MULTIBOOT_BOOTLOADER_MAGIC) | if (magic != MULTIBOOT_BOOTLOADER_MAGIC) | ||
{ | { | ||
|
| ||
continue; | continue; | ||
} | } | ||
/* | |||
* Some interrupt handlers | |||
*/ | |||
/* Binding some HW interrupts and exceptions to software routines */ | /* Binding some HW interrupts and exceptions to software routines */ | ||
sos_irq_set_routine(SOS_IRQ_TIMER, | sos_irq_set_routine(SOS_IRQ_TIMER, | ||
clk_it); | clk_it); | ||
/* Enabling the HW interrupts here, this will make the timer HW | |||
interrupt call our clk_it handler */ | /* | ||
asm volatile ("sti\n"); | * Setup physical memory management | ||
*/ | |||
the address of the first upper memory hole minus 1 megabyte.". It | the address of the first upper memory hole minus 1 megabyte.". It | ||
also adds: "It is not guaranteed to be this value." aka "YMMV" ;) */ | also adds: "It is not guaranteed to be this value." aka "YMMV" ;) */ | ||
sos_physmem_setup((mbi->mem_upper<<10) + (1<<20), | sos_physmem_subsystem_setup((mbi->mem_upper<<10) + (1<<20), | ||
& sos_kernel_core_base_paddr, | & sos_kernel_core_base_paddr, | ||
& sos_kernel_core_top_paddr); | & sos_kernel_core_top_paddr); | ||
/* | /* | ||
* Switch to paged-memory mode | * Switch to paged-memory mode | ||
|
| ||
/* Disabling interrupts should seem more correct, but it's not really | /* Disabling interrupts should seem more correct, but it's not really | ||
necessary at this stage */ | necessary at this stage */ | ||
if (sos_paging_setup(sos_kernel_core_base_paddr, | SOS_ASSERT_FATAL(SOS_OK == | ||
sos_kernel_core_top_paddr)) | sos_paging_subsystem_setup(sos_kernel_core_base_paddr, | ||
sos_bochs_printf("Could not setup paged memory mode\n"); | sos_kernel_core_top_paddr)); | ||
sos_x86_videomem_printf(2, 0, | |||
SOS_X86_VIDEO_FG_YELLOW | SOS_X86_VIDEO_BG_BLUE, | /* Bind the page fault exception */ | ||
"Paged-memory mode is activated"); | sos_exception_set_routine(SOS_EXCEPT_PAGE_FAULT, | ||
pgflt_ex); | |||
/* | |||
* Setup kernel virtual memory allocator | |||
*/ | |||
if (sos_kmem_vmm_setup(sos_kernel_core_base_paddr, | if (sos_kmem_vmm_subsystem_setup(sos_kernel_core_base_paddr, | ||
sos_kernel_core_top_paddr)) | sos_kernel_core_top_paddr, | ||
bootstrap_stack_bottom, | |||
bootstrap_stack_bottom | |||
+ bootstrap_stack_size)) | |||
if (sos_kmalloc_setup()) | if (sos_kmalloc_subsystem_setup()) | ||
/* Run some kmalloc tests */ | /* | ||
bn_test(); | * Enabling the HW interrupts here, this will make the timer HW | ||
* interrupt call our clk_it handler | |||
*/ | |||
asm volatile ("sti\n"); | |||
/* | |||
* Print hello world using coroutines | |||
*/ | |||
print_hello_world(); | |||
/* | |||
* Run coroutine tests | |||
*/ | |||
sos_x86_videomem_printf(4, 0, | |||
SOS_X86_VIDEO_BG_BLUE | SOS_X86_VIDEO_FG_LTGREEN, | |||
"Coroutine test"); | |||
sos_x86_videomem_printf(5, 0, | |||
SOS_X86_VIDEO_BG_BLUE | SOS_X86_VIDEO_FG_YELLOW, | |||
"Parsing..."); | |||
syntax_tree = parse_expression(" - ( (69/ toto)+ ( (( - +-- 1))) + --toto*((toto+ - - y - +2*(y-toto))*y) +2*(y-toto) )/- (( y - toto)*2)"); | |||
if (syntax_tree != NULL) | |||
{ | |||
sos_x86_videomem_printf(6, 0, | |||
SOS_X86_VIDEO_BG_BLUE | SOS_X86_VIDEO_FG_YELLOW, | |||
"Evaluating..."); | |||
sos_x86_videomem_printf(7, 0, | |||
SOS_X86_VIDEO_BG_BLUE | SOS_X86_VIDEO_FG_YELLOW, | |||
"Result=%d (if 0: check bochs output)", | |||
eval_expression(syntax_tree, | |||
(const char*[]){"toto", "y"}, | |||
(int[]){3, 4}, | |||
2)); | |||
free_syntax_tree(syntax_tree); | |||
sos_x86_videomem_printf(8, 0, | |||
SOS_X86_VIDEO_BG_BLUE | SOS_X86_VIDEO_FG_YELLOW, | |||
"Done (un-allocated syntax tree)"); | |||
} | |||
else | |||
{ | |||
sos_x86_videomem_printf(6, 0, | |||
SOS_X86_VIDEO_BG_BLUE | SOS_X86_VIDEO_FG_YELLOW, | |||
"Error in parsing (see bochs output)"); | |||
} | |||
/* | |||
* Run some demand-paging tests | |||
*/ | |||
test_demand_paging(234567, 500); | |||
/* | |||
* Create an un-resolved page fault, which will make the page fault | |||
* handler print the backtrace. | |||
*/ | |||
test_backtrace(6, 0xdeadbeef, bootstrap_stack_bottom, bootstrap_stack_size); | |||
/* | |||
* System should be halted BEFORE here ! | |||
*/ | |||
/* An operatig system never ends */ | /* An operatig system never ends */ | ||
for (;;) | for (;;) | ||
continue; | { | ||
/* Remove this instruction if you get an "Invalid opcode" CPU | |||
exception (old 80386 CPU) */ | |||
asm("hlt\n"); | |||
return; | continue; | ||
} | |||
|
| ||
|
| ||
/** We store the number of pages used/free */ | /** We store the number of pages used/free */ | ||
static sos_count_t physmem_total_pages, physmem_used_pages; | static sos_count_t physmem_total_pages, physmem_used_pages; | ||
sos_ret_t sos_physmem_setup(sos_size_t ram_size, | sos_ret_t sos_physmem_subsystem_setup(sos_size_t ram_size, | ||
/* out */sos_paddr_t *kernel_core_base, | /* out */sos_paddr_t *kernel_core_base, | ||
/* out */sos_paddr_t *kernel_core_top) | /* out */sos_paddr_t *kernel_core_top) | ||
/* The iterator over the page descriptors */ | /* The iterator over the page descriptors */ | ||
struct physical_page_descr *ppage_descr; | struct physical_page_descr *ppage_descr; | ||
|
| ||
|
| ||
/** The corresponding mask */ | /** The corresponding mask */ | ||
#define SOS_PAGE_MASK ((1<<12) - 1) | #define SOS_PAGE_MASK ((1<<12) - 1) | ||
#define SOS_PAGE_ALIGN_INF(val) \ | #define SOS_PAGE_ALIGN_INF(val) \ | ||
#define SOS_PAGE_ALIGN_SUP(val) \ | #define SOS_PAGE_ALIGN_SUP(val) \ | ||
#define SOS_IS_PAGE_ALIGNED(val) \ | |||
SOS_IS_ALIGNED((val), SOS_PAGE_SIZE) | |||
/** | /** | ||
* This is the reserved physical interval for the x86 video memory and | * This is the reserved physical interval for the x86 video memory and | ||
|
| ||
* assumes identity mapping (ie virtual address == physical address) | * assumes identity mapping (ie virtual address == physical address) | ||
* will be stored here | * will be stored here | ||
*/ | */ | ||
sos_ret_t sos_physmem_setup(sos_size_t ram_size, | sos_ret_t sos_physmem_subsystem_setup(sos_size_t ram_size, | ||
/* out */sos_paddr_t *kernel_core_base, | /* out */sos_paddr_t *kernel_core_base, | ||
/* out */sos_paddr_t *kernel_core_top); | /* out */sos_paddr_t *kernel_core_top); | ||
/** | /** | ||
* Retrieve the total number of pages, and the number of free pages | * Retrieve the total number of pages, and the number of free pages | ||
|
| ||
* Increment the reference count of a given physical page. Useful for | * Increment the reference count of a given physical page. Useful for | ||
* VM code which tries to map a precise physical address. | * VM code which tries to map a precise physical address. | ||
* | * | ||
* @param ppage_paddr Physical address of the page (MUST be page-aligned) | |||
* | |||
* @return TRUE when the page was previously in use, FALSE when the | * @return TRUE when the page was previously in use, FALSE when the | ||
* page was previously in the free list, <0 when the page address is | * page was previously in the free list, <0 when the page address is | ||
* invalid. | * invalid. | ||
|
| ||
* reference count reaches 0, the page is marked free, ie is available | * reference count reaches 0, the page is marked free, ie is available | ||
* for future sos_physmem_get_physpage() | * for future sos_physmem_get_physpage() | ||
* | * | ||
* @param ppage_paddr Physical address of the page (MUST be page-aligned) | |||
* | |||
* @return FALSE when the page is still in use, TRUE when the page is now | * @return FALSE when the page is still in use, TRUE when the page is now | ||
* unreferenced, <0 when the page address is invalid | * unreferenced, <0 when the page address is invalid | ||
*/ | */ | ||
|
| ||
/** | /** | ||
* Return the kernel memory allocation range associated with the given | * Return the kernel memory allocation range associated with the given | ||
* physical page, or NULL when page has no associated range | * physical page, or NULL when page has no associated range | ||
* | |||
* @param ppage_paddr Physical address of the page (MUST be page-aligned) | |||
*/ | */ | ||
struct sos_kmem_range* sos_physmem_get_kmem_range(sos_paddr_t ppage_paddr); | struct sos_kmem_range* sos_physmem_get_kmem_range(sos_paddr_t ppage_paddr); | ||
|
| ||
/** | /** | ||
* Set the kernel memory allocation range associated to the given | * Set the kernel memory allocation range associated to the given | ||
* physical page. | * physical page. | ||
* | |||
* @param ppage_paddr Physical address of the page (MUST be page-aligned) | |||
* | |||
* @return error if page is invalid | * @return error if page is invalid | ||
*/ | */ | ||
sos_ret_t sos_physmem_set_kmem_range(sos_paddr_t ppage_paddr, | sos_ret_t sos_physmem_set_kmem_range(sos_paddr_t ppage_paddr, | ||
|
| ||
|
| ||
typedef unsigned long int sos_ui32_t; /* 32b unsigned */ | typedef unsigned long int sos_ui32_t; /* 32b unsigned */ | ||
typedef unsigned short int sos_ui16_t; /* 16b unsigned */ | typedef unsigned short int sos_ui16_t; /* 16b unsigned */ | ||
typedef unsigned char sos_ui8_t; /* 8b unsigned */ | typedef unsigned char sos_ui8_t; /* 8b unsigned */ | ||
typedef signed long int sos_si32_t; /* 32b signed */ | |||
typedef signed short int sos_si16_t; /* 16b signed */ | |||
typedef signed char sos_si8_t; /* 8b signed */ | |||
typedef enum { FALSE=0, TRUE } sos_bool_t; | typedef enum { FALSE=0, TRUE } sos_bool_t; | ||
|
| ||
|
| ||
.bss SIZEOF(.rodata) + ADDR(.rodata) : | .bss SIZEOF(.rodata) + ADDR(.rodata) : | ||
{ *(.bss) | { *(.bss) | ||
*(COMMON) | *(COMMON) | ||
/* We put the stack of the bootstrap thread on a page | |||
boundary, because it can be un-allocated later */ | |||
. = ALIGN(4096); | |||
*(.init_stack) | |||
PROVIDE(ebss = .); | PROVIDE(ebss = .); | ||
PROVIDE(_ebss = .); | PROVIDE(_ebss = .); | ||
} | } | ||
/* We take note of the end of the kernel */ | /* We take note of the end of the kernel: this is where the GPFM | ||
will begin */ | |||
/* We don't care of the note, indent, comment, etc.. sections | /* We don't care of the note, indent, comment, etc.. sections |
Legend:  
|
File list:
../sos-code-article6/Makefile
../sos-code-article6/VERSION
../sos-code-article6/bootstrap/multiboot.S
../sos-code-article6/bootstrap/multiboot.h
../sos-code-article6/drivers/bochs.c
../sos-code-article6/drivers/bochs.h
../sos-code-article6/extra/bootsect.S
../sos-code-article6/hwcore/cpu_context.c
../sos-code-article6/hwcore/cpu_context.h
../sos-code-article6/hwcore/cpu_context_switch.S
../sos-code-article6/hwcore/exception.c
../sos-code-article6/hwcore/exception.h
../sos-code-article6/hwcore/exception_wrappers.S
../sos-code-article6/hwcore/gdt.c
../sos-code-article6/hwcore/gdt.h
../sos-code-article6/hwcore/i8259.c
../sos-code-article6/hwcore/i8259.h
../sos-code-article6/hwcore/idt.c
../sos-code-article6/hwcore/idt.h
../sos-code-article6/hwcore/irq.c
../sos-code-article6/hwcore/irq.h
../sos-code-article6/hwcore/irq_wrappers.S
../sos-code-article6/hwcore/paging.c
../sos-code-article6/hwcore/paging.h
../sos-code-article6/hwcore/segment.h
../sos-code-article6/sos/assert.c
../sos-code-article6/sos/assert.h
../sos-code-article6/sos/klibc.c
../sos-code-article6/sos/klibc.h
../sos-code-article6/sos/kmalloc.c
../sos-code-article6/sos/kmalloc.h
../sos-code-article6/sos/kmem_slab.c
../sos-code-article6/sos/kmem_slab.h
../sos-code-article6/sos/kmem_vmm.c
../sos-code-article6/sos/kmem_vmm.h
../sos-code-article6/sos/macros.h
../sos-code-article6/sos/main.c
../sos-code-article6/sos/physmem.c
../sos-code-article6/sos/physmem.h
../sos-code-article6/sos/types.h
../sos-code-article6/support/sos.lds