summaryrefslogtreecommitdiff
path: root/sos-code-article6.5/sos/main.c
diff options
context:
space:
mode:
authorAlex AUVOLAT <alex.auvolat@ens.fr>2014-03-28 17:09:15 +0100
committerAlex AUVOLAT <alex.auvolat@ens.fr>2014-03-28 17:09:15 +0100
commita8968330aff45e0b8cf278f49fa337d5fcb9bfd8 (patch)
treededf697b1a5c3c96e77f77e551e9e6af8785a51c /sos-code-article6.5/sos/main.c
parent8d9e22df8afa4c3339e52c7b3b77388ca0e69fac (diff)
downloadSOS-a8968330aff45e0b8cf278f49fa337d5fcb9bfd8.tar.gz
SOS-a8968330aff45e0b8cf278f49fa337d5fcb9bfd8.zip
Import and compile code for article 6.5
Diffstat (limited to 'sos-code-article6.5/sos/main.c')
-rw-r--r--sos-code-article6.5/sos/main.c461
1 files changed, 461 insertions, 0 deletions
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 <bootstrap/multiboot.h>
+#include <hwcore/idt.h>
+#include <hwcore/gdt.h>
+#include <hwcore/irq.h>
+#include <hwcore/exception.h>
+#include <hwcore/i8254.h>
+#include <sos/list.h>
+#include <sos/physmem.h>
+#include <hwcore/paging.h>
+#include <sos/kmem_vmm.h>
+#include <sos/kmalloc.h>
+#include <sos/time.h>
+#include <sos/thread.h>
+#include <sos/klibc.h>
+#include <sos/assert.h>
+#include <drivers/x86_videomem.h>
+#include <drivers/bochs.h>
+
+
+/* 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 !");
+}