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