From a8968330aff45e0b8cf278f49fa337d5fcb9bfd8 Mon Sep 17 00:00:00 2001 From: Alex AUVOLAT Date: Fri, 28 Mar 2014 17:09:15 +0100 Subject: Import and compile code for article 6.5 --- sos-code-article6.5/sos/main.c | 461 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 461 insertions(+) create mode 100644 sos-code-article6.5/sos/main.c (limited to 'sos-code-article6.5/sos/main.c') diff --git a/sos-code-article6.5/sos/main.c b/sos-code-article6.5/sos/main.c new file mode 100644 index 0000000..6201f47 --- /dev/null +++ b/sos-code-article6.5/sos/main.c @@ -0,0 +1,461 @@ +/* Copyright (C) 2004 The SOS 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 definitions of the multiboot standard */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + + +/* Helper function to display each bits of a 32bits integer on the + screen as dark or light carrets */ +void display_bits(unsigned char row, unsigned char col, + unsigned char attribute, + sos_ui32_t integer) +{ + int i; + /* Scan each bit of the integer, MSb first */ + for (i = 31 ; i >= 0 ; i--) + { + /* Test if bit i of 'integer' is set */ + int bit_i = (integer & (1 << i)); + /* Ascii 219 => dark carret, Ascii 177 => light carret */ + unsigned char ascii_code = bit_i?219:177; + sos_x86_videomem_putchar(row, col++, + attribute, + ascii_code); + } +} + + +/* Clock IRQ handler */ +static void clk_it(int intid) +{ + static sos_ui32_t clock_count = 0; + + display_bits(0, 48, + SOS_X86_VIDEO_FG_LTGREEN | SOS_X86_VIDEO_BG_BLUE, + clock_count); + clock_count++; + + /* Execute the expired timeout actions (if any) */ + sos_time_do_tick(); +} + + +/* ====================================================================== + * Page fault exception handling + */ + +/* Helper function to dump a backtrace on bochs and/or the console */ +static void dump_backtrace(const struct sos_cpu_state *cpu_state, + sos_vaddr_t stack_bottom, + sos_size_t stack_size, + sos_bool_t on_console, + sos_bool_t on_bochs) +{ + void backtracer(sos_vaddr_t PC, + sos_vaddr_t params, + sos_ui32_t depth, + void *custom_arg) + { + sos_ui32_t invalid = 0xffffffff, *arg1, *arg2, *arg3, *arg4; + + /* Get the address of the first 3 arguments from the + frame. Among these arguments, 0, 1, 2, 3 arguments might be + meaningful (depending on how many arguments the function may + 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_state, 15, stack_bottom, stack_size, backtracer, NULL); +} + + +/* Page fault exception handler with demand paging for the kernel */ +static void pgflt_ex(int intid, const struct sos_cpu_state *ctxt) +{ + static sos_ui32_t demand_paging_count = 0; + sos_vaddr_t faulting_vaddr = sos_cpu_context_get_EX_faulting_vaddr(ctxt); + sos_paddr_t ppage_paddr; + + /* 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 at instruction 0x%x on access to address 0x%x (info=%x)!", + sos_cpu_context_get_PC(ctxt), + (unsigned)faulting_vaddr, + (unsigned)sos_cpu_context_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 */ +} + + +/* ====================================================================== + * Demonstrate the use of SOS kernel threads + * - Kernel Threads are created with various priorities and their + * state is printed on both the console and the bochs' 0xe9 port + * - For tests regarding threads' synchronization, see mouse_sim.c + */ + +struct thr_arg +{ + char character; + int color; + + int col; + int row; +}; + + +static void demo_thread(void *arg) +{ + struct thr_arg *thr_arg = (struct thr_arg*)arg; + int progress = 0; + + sos_bochs_printf("start %c", thr_arg->character); + while (1) + { + progress ++; + display_bits(thr_arg->row, thr_arg->col+1, thr_arg->color, progress); + + sos_bochs_putchar(thr_arg->character); + + /* Yield the CPU to another thread sometimes... */ + if ((random() % 100) == 0) + { + sos_bochs_printf("yield(%c)\n", thr_arg->character); + sos_x86_videomem_putchar(thr_arg->row, thr_arg->col, 0x1e, 'Y'); + SOS_ASSERT_FATAL(SOS_OK == sos_thread_yield()); + sos_x86_videomem_putchar(thr_arg->row, thr_arg->col, 0x1e, 'R'); + } + + /* Go to sleep some other times... */ + else if ((random() % 200) == 0) + { + struct sos_time t = (struct sos_time){ .sec=0, .nanosec=50000000 }; + sos_bochs_printf("sleep1(%c)\n", thr_arg->character); + sos_x86_videomem_putchar(thr_arg->row, thr_arg->col, 0x1e, 's'); + SOS_ASSERT_FATAL(SOS_OK == sos_thread_sleep(& t)); + SOS_ASSERT_FATAL(sos_time_is_zero(& t)); + sos_x86_videomem_putchar(thr_arg->row, thr_arg->col, 0x1e, 'R'); + } + + /* Go to sleep for a longer time some other times... */ + else if ((random() % 300) == 0) + { + struct sos_time t = (struct sos_time){ .sec=0, .nanosec=300000000 }; + sos_bochs_printf("sleep2(%c)\n", thr_arg->character); + sos_x86_videomem_putchar(thr_arg->row, thr_arg->col, 0x1e, 'S'); + SOS_ASSERT_FATAL(SOS_OK == sos_thread_sleep(& t)); + SOS_ASSERT_FATAL(sos_time_is_zero(& t)); + sos_x86_videomem_putchar(thr_arg->row, thr_arg->col, 0x1e, 'R'); + } + + /* Infinite loop otherwise */ + } +} + + +static void test_thread() +{ + /* "static" variables because we want them to remain even when the + function returns */ + static struct thr_arg arg_b, arg_c, arg_d, arg_e, arg_R, arg_S; + sos_ui32_t flags; + + sos_disable_IRQs(flags); + + arg_b = (struct thr_arg) { .character='b', .col=0, .row=21, .color=0x14 }; + sos_create_kernel_thread("YO[b]", demo_thread, (void*)&arg_b); + + arg_c = (struct thr_arg) { .character='c', .col=46, .row=21, .color=0x14 }; + sos_create_kernel_thread("YO[c]", demo_thread, (void*)&arg_c); + + arg_d = (struct thr_arg) { .character='d', .col=0, .row=20, .color=0x14 }; + sos_create_kernel_thread("YO[d]", demo_thread, (void*)&arg_d); + + arg_e = (struct thr_arg) { .character='e', .col=0, .row=19, .color=0x14 }; + sos_create_kernel_thread("YO[e]", demo_thread, (void*)&arg_e); + + arg_R = (struct thr_arg) { .character='R', .col=0, .row=17, .color=0x1c }; + sos_create_kernel_thread("YO[R]", demo_thread, (void*)&arg_R); + + arg_S = (struct thr_arg) { .character='S', .col=0, .row=16, .color=0x1c }; + sos_create_kernel_thread("YO[S]", demo_thread, (void*)&arg_S); + + sos_restore_IRQs(flags); +} + + +/* ====================================================================== + * An operating system MUST always have a ready thread ! Otherwise: + * what would the CPU have to execute ?! + */ +static void idle_thread() +{ + sos_ui32_t idle_twiddle = 0; + + while (1) + { + /* Remove this instruction if you get an "Invalid opcode" CPU + exception (old 80386 CPU) */ + asm("hlt\n"); + + idle_twiddle ++; + display_bits(0, 0, SOS_X86_VIDEO_FG_GREEN | SOS_X86_VIDEO_BG_BLUE, + idle_twiddle); + + /* Lend the CPU to some other thread */ + sos_thread_yield(); + } +} + + +/* ====================================================================== + * The C entry point of our operating system + */ +void sos_main(unsigned long magic, unsigned long addr) +{ + unsigned i; + sos_paddr_t sos_kernel_core_base_paddr, sos_kernel_core_top_paddr; + struct sos_time tick_resolution; + + /* Grub sends us a structure, called multiboot_info_t with a lot of + precious informations about the system, see the multiboot + documentation for more information. */ + multiboot_info_t *mbi; + mbi = (multiboot_info_t *) addr; + + /* Setup bochs and console, and clear the console */ + sos_bochs_setup(); + + sos_x86_videomem_setup(); + sos_x86_videomem_cls(SOS_X86_VIDEO_BG_BLUE); + + /* Greetings from SOS */ + if (magic == MULTIBOOT_BOOTLOADER_MAGIC) + /* Loaded with Grub */ + sos_x86_videomem_printf(1, 0, + SOS_X86_VIDEO_FG_YELLOW | SOS_X86_VIDEO_BG_BLUE, + "Welcome From GRUB to %s%c RAM is %dMB (upper mem = 0x%x kB)", + "SOS article 6.5", ',', + (unsigned)(mbi->mem_upper >> 10) + 1, + (unsigned)mbi->mem_upper); + else + /* Not loaded with grub */ + sos_x86_videomem_printf(1, 0, + SOS_X86_VIDEO_FG_YELLOW | SOS_X86_VIDEO_BG_BLUE, + "Welcome to SOS article 6.5"); + + sos_bochs_putstring("Message in a bochs: This is SOS article 6.5.\n"); + + /* Setup CPU segmentation and IRQ subsystem */ + sos_gdt_subsystem_setup(); + sos_idt_subsystem_setup(); + + /* Setup SOS IRQs and exceptions subsystem */ + sos_exception_subsystem_setup(); + sos_irq_subsystem_setup(); + + /* Configure the timer so as to raise the IRQ0 at a 100Hz rate */ + sos_i8254_set_frequency(100); + + /* Setup the kernel time subsystem to get prepared to take the timer + ticks into account */ + tick_resolution = (struct sos_time) { .sec=0, .nanosec=10000000UL }; + sos_time_subsysem_setup(& tick_resolution); + + /* We need a multiboot-compliant boot loader to get the size of the RAM */ + if (magic != MULTIBOOT_BOOTLOADER_MAGIC) + { + sos_x86_videomem_putstring(20, 0, + SOS_X86_VIDEO_FG_LTRED + | SOS_X86_VIDEO_BG_BLUE + | SOS_X86_VIDEO_FG_BLINKING, + "I'm not loaded with Grub !"); + /* STOP ! */ + for (;;) + continue; + } + + /* + * Some interrupt handlers + */ + + /* Binding some HW interrupts and exceptions to software routines */ + sos_irq_set_routine(SOS_IRQ_TIMER, + clk_it); + + /* + * Setup physical memory management + */ + + /* Multiboot says: "The value returned for upper memory is maximally + the address of the first upper memory hole minus 1 megabyte.". It + also adds: "It is not guaranteed to be this value." aka "YMMV" ;) */ + sos_physmem_subsystem_setup((mbi->mem_upper<<10) + (1<<20), + & sos_kernel_core_base_paddr, + & sos_kernel_core_top_paddr); + + /* + * Switch to paged-memory mode + */ + + /* Disabling interrupts should seem more correct, but it's not really + necessary at this stage */ + SOS_ASSERT_FATAL(SOS_OK == + sos_paging_subsystem_setup(sos_kernel_core_base_paddr, + sos_kernel_core_top_paddr)); + + /* Bind the page fault exception */ + sos_exception_set_routine(SOS_EXCEPT_PAGE_FAULT, + pgflt_ex); + + /* + * Setup kernel virtual memory allocator + */ + + if (sos_kmem_vmm_subsystem_setup(sos_kernel_core_base_paddr, + sos_kernel_core_top_paddr, + bootstrap_stack_bottom, + bootstrap_stack_bottom + + bootstrap_stack_size)) + sos_bochs_printf("Could not setup the Kernel virtual space allocator\n"); + + if (sos_kmalloc_subsystem_setup()) + sos_bochs_printf("Could not setup the Kmalloc subsystem\n"); + + /* + * Initialize the Kernel thread and scheduler subsystems + */ + + /* Initialize kernel thread subsystem */ + sos_thread_subsystem_setup(bootstrap_stack_bottom, + bootstrap_stack_size); + + /* Initialize the scheduler */ + sos_sched_subsystem_setup(); + + /* Declare the IDLE thread */ + SOS_ASSERT_FATAL(sos_create_kernel_thread("idle", idle_thread, NULL) != NULL); + + /* Enabling the HW interrupts here, this will make the timer HW + interrupt call the scheduler */ + asm volatile ("sti\n"); + + + /* Now run some Kernel threads just for fun ! */ + extern void MouseSim(); + MouseSim(); + test_thread(); + + /* + * We can safely exit from this function now, for there is already + * an idle Kernel thread ready to make the CPU busy working... + * + * However, we must EXPLICITELY call sos_thread_exit() because a + * simple "return" will return nowhere ! Actually this first thread + * was initialized by the Grub bootstrap stage, at a time when the + * word "thread" did not exist. This means that the stack was not + * setup in order for a return here to call sos_thread_exit() + * automagically. Hence we must call it manually. This is the ONLY + * kernel thread where we must do this manually. + */ + sos_bochs_printf("Bye from primary thread !\n"); + sos_thread_exit(); + SOS_FATAL_ERROR("No trespassing !"); +} -- cgit v1.2.3