diff --git a/README.md b/README.md index ace267b..2a3e6fb 100644 --- a/README.md +++ b/README.md @@ -75,6 +75,10 @@ Overview of all branches Description of basic synchronization primitives +4. stage3 - Preemptive multitasking + + Introduction into a preemptive multitasking and interrupt handling + Usefull Links ------------- 1. http://www.gnu.org/software/grub/manual/multiboot/ diff --git a/arch/x86/include/asm/gdt.h b/arch/x86/include/asm/gdt.h new file mode 100644 index 0000000..ffdcde9 --- /dev/null +++ b/arch/x86/include/asm/gdt.h @@ -0,0 +1,163 @@ +/* + * Copyright (c) 2010, Stefan Lankes, RWTH Aachen University + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/** + * @file arch/x86/include/asm/gdt.h + * @brief Definitions and functions related to segmentation + * @author Stefan Lankes + * + * This file defines the interface for segmentation as like structures to describe segments.\n + * There are also some other gdt-private functions in the gdt.c file defined. + */ + +#ifndef __ARCH_GDT_H__ +#define __ARCH_GDT_H__ + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/// This segment is a data segment +#define GDT_FLAG_DATASEG 0x02 +/// This segment is a code segment +#define GDT_FLAG_CODESEG 0x0a +#define GDT_FLAG_TSS 0x09 +#define GDT_FLAG_TSS_BUSY 0x02 + +#define GDT_FLAG_SEGMENT 0x10 +/// Privilege level: Ring 0 +#define GDT_FLAG_RING0 0x00 +/// Privilege level: Ring 1 +#define GDT_FLAG_RING1 0x20 +/// Privilege level: Ring 2 +#define GDT_FLAG_RING2 0x40 +/// Privilege level: Ring 3 +#define GDT_FLAG_RING3 0x60 +/// Segment is present +#define GDT_FLAG_PRESENT 0x80 +/// Segment was accessed +#define GDT_FLAG_ACCESSED 0x01 +/** + * @brief Granularity of segment limit + * - set: segment limit unit is 4 KB (page size) + * - not set: unit is bytes + */ +#define GDT_FLAG_4K_GRAN 0x80 +/** + * @brief Default operand size + * - set: 32 bit + * - not set: 16 bit + */ +#define GDT_FLAG_16_BIT 0x00 +#define GDT_FLAG_32_BIT 0x40 +#define GDT_FLAG_64_BIT 0x20 + +/** @brief Defines a GDT entry + * + * A global descriptor table entry consists of: + * - 32 bit base address (chunkwise embedded into this structure) + * - 20 bit limit + */ +typedef struct { + /// Lower 16 bits of limit range + uint16_t limit_low; + /// Lower 16 bits of base address + uint16_t base_low; + /// middle 8 bits of base address + uint8_t base_middle; + /// Access bits + uint8_t access; + /// Granularity bits + uint8_t granularity; + /// Higher 8 bits of base address + uint8_t base_high; +} __attribute__ ((packed)) gdt_entry_t; + +/** @brief defines the GDT pointer structure + * + * This structure tells the address and size of the table. + */ +typedef struct { + /// Size of the table in bytes (not the number of entries!) + uint16_t limit; + /// Address of the table + size_t base; +} __attribute__ ((packed)) gdt_ptr_t; + +#ifdef CONFIG_LGUEST + +// TODO TODO: Just hacked in +#define GDT_ENTRIES 32 + +#else + +#ifdef CONFIG_X86_32 +#define GDT_ENTRIES (5+MAX_TASKS) +#else +// a TSS descriptor is twice larger than a code/data descriptor +#define GDT_ENTRIES (5+MAX_TASKS*2) +#endif + +#if GDT_ENTRIES > 8192 +#error Too many GDT entries! +#endif + +#endif + +/** @brief Installs the global descriptor table + * + * The installation involves the following steps: + * - set up the special GDT pointer + * - set up the entries in our GDT + * - finally call gdt_flush() in our assembler file + * in order to tell the processor where the new GDT is + * - update the new segment registers + */ +void gdt_install(void); + +/** @brief Set gate with chosen attributes + */ +void gdt_set_gate(int num, unsigned long base, unsigned long limit, + unsigned char access, unsigned char gran); + +/** @brief Configures and returns a GDT descriptor with chosen attributes + * + * Just feed this function with address, limit and the flags + * you have seen in idt.h + * + * @return a preconfigured gdt descriptor + */ +void configure_gdt_entry(gdt_entry_t *dest_entry, unsigned long base, unsigned long limit, + unsigned char access, unsigned char gran); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/arch/x86/include/asm/idt.h b/arch/x86/include/asm/idt.h new file mode 100644 index 0000000..88a425f --- /dev/null +++ b/arch/x86/include/asm/idt.h @@ -0,0 +1,138 @@ +/* + * Copyright (c) 2010, Stefan Lankes, RWTH Aachen University + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/** + * @author Stefan Lankes + * @file arch/x86/include/asm/idt.h + * @brief Definition of IDT flags and functions to set interrupts + * + * This file contains define-constants for interrupt flags + * and installer functions for interrupt gates.\n + * See idt.c for structure definitions. + */ + +#ifndef __ARCH_IDT_H__ +#define __ARCH_IDT_H__ + +#include + +/// This bit shall be set to 0 if the IDT slot is empty +#define IDT_FLAG_PRESENT 0x80 +/// Interrupt can be called from within RING0 +#define IDT_FLAG_RING0 0x00 +/// Interrupt can be called from within RING1 and lower +#define IDT_FLAG_RING1 0x20 +/// Interrupt can be called from within RING2 and lower +#define IDT_FLAG_RING2 0x40 +/// Interrupt can be called from within RING3 and lower +#define IDT_FLAG_RING3 0x60 +/// Size of gate is 16 bit +#define IDT_FLAG_16BIT 0x00 +/// Size of gate is 32 bit +#define IDT_FLAG_32BIT 0x08 +/// The entry describes an interrupt gate +#define IDT_FLAG_INTTRAP 0x06 +/// The entry describes a trap gate +#define IDT_FLAG_TRAPGATE 0x07 +/// The entry describes a task gate +#define IDT_FLAG_TASKGATE 0x05 + +/* + * This is not IDT-flag related. It's the segment selectors for kernel code and data. + */ +#define KERNEL_CODE_SELECTOR 0x08 +#define KERNEL_DATA_SELECTOR 0x10 + +#ifdef __cplusplus +extern "C" { +#endif + +/** @brief Defines an IDT entry + * + * This structure defines interrupt descriptor table entries.\n + * They consist of the handling function's base address, some flags + * and a segment selector. + */ +typedef struct { + /// Handler function's lower 16 address bits + uint16_t base_lo; + /// Handler function's segment selector. + uint16_t sel; + /// These bits are reserved by Intel + uint8_t always0; + /// These 8 bits contain flags. Exact use depends on the type of interrupt gate. + uint8_t flags; + /// Higher 16 bits of handler function's base address + uint16_t base_hi; +} __attribute__ ((packed)) idt_entry_t; + +/** @brief Defines the idt pointer structure. + * + * This structure keeps information about + * base address and size of the interrupt descriptor table. + */ +typedef struct { + /// Size of the IDT in bytes (not the number of entries!) + uint16_t limit; + /// Base address of the IDT + size_t base; +} __attribute__ ((packed)) idt_ptr_t; + +/** @brief Installs IDT + * + * The installation involves the following steps: + * - Set up the IDT pointer + * - Set up int 0x80 for syscalls + * - process idt_load() + */ +void idt_install(void); + +/** @brief Set an entry in the IDT + * + * @param num index in the IDT + * @param base base-address of the handler function being installed + * @param sel Segment the IDT will use + * @param flags Flags this entry will have + */ +void idt_set_gate(unsigned char num, size_t base, unsigned short sel, + unsigned char flags); + +/** @brief Configures and returns a IDT entry with chosen attributes + * + * Just feed this function with base, selector and the flags + * you have seen in idt.h + * + * @return a preconfigured idt descriptor + */ +void configure_idt_entry(idt_entry_t *dest_entry, size_t base, + unsigned short sel, unsigned char flags); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/arch/x86/include/asm/irq.h b/arch/x86/include/asm/irq.h new file mode 100644 index 0000000..31e25b2 --- /dev/null +++ b/arch/x86/include/asm/irq.h @@ -0,0 +1,80 @@ +/* + * Copyright (c) 2010, Stefan Lankes, RWTH Aachen University + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/** + * @author Stefan Lankes + * @file arch/x86/include/asm/irq.h + * @brief Functions related to IRQs + * + * This file contains functions and a pointer type related to interrupt requests. + */ + +#ifndef __ARCH_IRQ_H__ +#define __ARCH_IRQ_H__ + +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/** @brief Pointer-type to IRQ-handling functions + * + * Whenever you write a IRQ-handling function it has to match this signature. + */ +typedef void (*irq_handler_t)(struct state *); + +/** @brief Install a custom IRQ handler for a given IRQ + * + * @param irq The desired irq + * @param handler The handler to install + */ +int irq_install_handler(unsigned int irq, irq_handler_t handler); + +/** @brief Clear the handler for a given IRQ + * + * @param irq The handler's IRQ + */ +int irq_uninstall_handler(unsigned int irq); + +/** @brief Procedure to initialize IRQ + * + * This procedure is just a small collection of calls: + * - idt_install(); + * - isrs_install(); + * - irq_install(); + * + * @return Just returns 0 in any case + */ +int irq_init(void); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/arch/x86/include/asm/isrs.h b/arch/x86/include/asm/isrs.h new file mode 100644 index 0000000..765409b --- /dev/null +++ b/arch/x86/include/asm/isrs.h @@ -0,0 +1,59 @@ +/* + * Copyright (c) 2010, Stefan Lankes, RWTH Aachen University + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/** + * @author Stefan Lankes + * @file arch/x86/include/asm/isrs.h + * @brief Installation of interrupt service routines + * + * This file contains the declaration of the ISR installer procedure.\n + * There is more interesting code related to ISRs in the isrs.c file. + */ + +#ifndef __ARCH_ISRS_H__ +#define __ARCH_ISRS_H__ + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/** @brief ISR installer procedure + * + * This procedure sets the first 32 entries in the IDT + * to the first 32 ISRs to call one general fault handler + * which will do some dispatching and exception message logging.\n + * The access flags are set to 0x8E (PRESENT, privilege: RING0, size: 32bit gate, type: interrupt gate). + */ +void isrs_install(void); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/arch/x86/include/asm/processor.h b/arch/x86/include/asm/processor.h index 751daef..28bdbf1 100644 --- a/arch/x86/include/asm/processor.h +++ b/arch/x86/include/asm/processor.h @@ -37,6 +37,7 @@ #define __ARCH_PROCESSOR_H__ #include +#include #ifdef __cplusplus extern "C" { @@ -127,6 +128,57 @@ static inline size_t lsb(size_t i) #define NOP8 asm volatile ("nop;nop;nop;nop;nop;nop;nop;nop") /// The PAUSE instruction provides a hint to the processor that the code sequence is a spin-wait loop. #define PAUSE asm volatile ("pause") +/// The HALT instruction stops the processor until the next interrupt arrives +#define HALT asm volatile ("hlt") + +/** @brief Init several subsystems + * + * This function calls the initialization procedures for: + * - GDT + * - APIC + * - PCI [if configured] + * + * @return 0 in any case + */ +inline static int system_init(void) +{ + gdt_install(); + + return 0; +} + +/** @brief Detect and read out CPU frequency + * + * @return The CPU frequency in MHz + */ +uint32_t detect_cpu_frequency(void); + +/** @brief Read out CPU frequency if detected before + * + * If you did not issue the detect_cpu_frequency() function before, + * this function will call it implicitly. + * + * @return The CPU frequency in MHz + */ +uint32_t get_cpu_frequency(void); + +/** @brief Busywait an microseconds interval of time + * @param usecs The time to wait in microseconds + */ +void udelay(uint32_t usecs); + +/** @brief System calibration + * + * This procedure will detect the CPU frequency and calibrate the APIC timer. + * + * @return 0 in any case. + */ +inline static int system_calibration(void) +{ + detect_cpu_frequency(); + + return 0; +} #ifdef __cplusplus } diff --git a/arch/x86/include/asm/stddef.h b/arch/x86/include/asm/stddef.h index 5f3b937..dcd33a7 100644 --- a/arch/x86/include/asm/stddef.h +++ b/arch/x86/include/asm/stddef.h @@ -71,6 +71,10 @@ typedef unsigned short wchar_t; /// This defines what the stack looks like after the task context is saved. struct state { + /// ds register + uint32_t ds; + /// es register + uint32_t es; /// EDI register uint32_t edi; /// ESI register @@ -88,10 +92,16 @@ struct state { /// EAX register uint32_t eax; /* pushed by 'pusha' */ - // state of the controll register - uint32_t eflags; - /// state of instruction pointer + /// Interrupt number + uint32_t int_no; + + // pushed by the processor automatically + uint32_t error; uint32_t eip; + uint32_t cs; + uint32_t eflags; + uint32_t useresp; + uint32_t ss; }; #ifdef __cplusplus diff --git a/arch/x86/include/asm/tss.h b/arch/x86/include/asm/tss.h new file mode 100644 index 0000000..d92d76e --- /dev/null +++ b/arch/x86/include/asm/tss.h @@ -0,0 +1,72 @@ +/* + * Copyright (c) 2010, Stefan Lankes, RWTH Aachen University + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/** + * @author Stefan Lankes + * @file arch/x86/include/asm/tss.h + * @brief Task state segment structure definition + */ + +#ifndef __ARCH_TSS_H__ +#define __ARCH_TSS_H__ + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/** @brief The tast state segment structure + */ +typedef struct { + uint16_t backlink, __blh; + uint32_t esp0; + uint16_t ss0, __ss0h; + uint32_t esp1; + uint16_t ss1, __ss1h; + uint32_t esp2; + uint16_t ss2, __ss2h; + uint32_t cr3; + uint32_t eip; + uint32_t eflags; + uint32_t eax, ecx, edx, ebx; + uint32_t esp, ebp, esi, edi; + uint16_t es, __esh; + uint16_t cs, __csh; + uint16_t ss, __ssh; + uint16_t ds, __dsh; + uint16_t fs, __fsh; + uint16_t gs, __gsh; + uint16_t ldt, __ldth; + uint16_t trace, bitmap; +} __attribute__ ((packed)) tss_t; + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/arch/x86/kernel/Makefile b/arch/x86/kernel/Makefile index 54cc71a..b0c0676 100644 --- a/arch/x86/kernel/Makefile +++ b/arch/x86/kernel/Makefile @@ -1,4 +1,4 @@ -C_source := tasks.c multiboot.c vga.c +C_source := tasks.c multiboot.c vga.c gdt.c irq.c idt.c isrs.c timer.c processor.c ASM_source := entry.asm string32.asm MODULE := arch_x86_kernel diff --git a/arch/x86/kernel/entry.asm b/arch/x86/kernel/entry.asm index 9b7a01f..1b1c36a 100644 --- a/arch/x86/kernel/entry.asm +++ b/arch/x86/kernel/entry.asm @@ -24,6 +24,13 @@ ; (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS ; SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +; This is the kernel's entry point. We could either call main here, +; or we can use this to setup the stack or other nice stuff, like +; perhaps setting up the GDT and segments. Please note that interrupts +; are disabled at this point: More on interrupts later! + +%include "config.inc" + [BITS 32] ; We use a special name to map this section at the begin of our kernel ; => Multiboot needs its magic number at the begin of the kernel @@ -50,8 +57,9 @@ mboot: SECTION .text ALIGN 4 stublet: -; initialize stack pointer. - mov esp, default_stack_pointer +; initialize stack pointer + mov esp, boot_stack + add esp, KERNEL_STACK_SIZE-16 ; initialize cpu features call cpu_init ; interpret multiboot information @@ -82,34 +90,178 @@ cpu_init: mov cr4, eax ret +; This will set up our new segment registers. We need to do +; something special in order to set CS. We do what is called a +; far jump. A jump that includes a segment as well as an offset. +; This is declared in C as 'extern void gdt_flush();' +global gdt_flush +extern gp +gdt_flush: + lgdt [gp] + mov ax, 0x10 + mov ds, ax + mov es, ax + mov fs, ax + mov gs, ax + mov ss, ax + jmp 0x08:flush2 +flush2: + ret + +; The first 32 interrupt service routines (isr) entries correspond to exceptions. +; Some exceptions will push an error code onto the stack which is specific to +; the exception caused. To decrease the complexity, we handle this by pushing a +; dummy error code of 0 onto the stack for any ISR that doesn't push an error +; code already. +; +; ISRs are registered as "Interrupt Gate". +; Therefore, the interrupt flag (IF) is already cleared. + +; NASM macro which pushs also an pseudo error code +%macro isrstub_pseudo_error 1 + global isr%1 + isr%1: + push byte 0 ; pseudo error code + push byte %1 + jmp common_stub +%endmacro + +; Similar to isrstub_pseudo_error, but without pushing +; a pseudo error code => The error code is already +; on the stack. +%macro isrstub 1 + global isr%1 + isr%1: + push byte %1 + jmp common_stub +%endmacro + +; create isr entries, where the number after the +; pseudo error code represents following interrupts +; 0: Divide By Zero Exception +; 1: Debug Exception +; 2: Non Maskable Interrupt Exception +; 3: Int 3 Exception +; 4: INTO Exception +; 5: Out of Bounds Exception +; 6: Invalid Opcode Exception +; 7: Coprocessor Not Available Exception +%assign i 0 +%rep 8 + isrstub_pseudo_error i +%assign i i+1 +%endrep + +; 8: Double Fault Exception (With Error Code!) +isrstub 8 + +; 9: Coprocessor Segment Overrun Exception +isrstub_pseudo_error 9 + +; 10: Bad TSS Exception (With Error Code!) +; 11: Segment Not Present Exception (With Error Code!) +; 12: Stack Fault Exception (With Error Code!) +; 13: General Protection Fault Exception (With Error Code!) +; 14: Page Fault Exception (With Error Code!) +%assign i 10 +%rep 5 + isrstub i +%assign i i+1 +%endrep + +; 15: Reserved Exception +; 16: Floating Point Exception +; 17: Alignment Check Exception +; 18: Machine Check Exceptio +; 19-31: Reserved +%assign i 15 +%rep 17 + isrstub_pseudo_error i +%assign i i+1 +%endrep + +; NASM macro for asynchronous interrupts (no exceptions) +%macro irqstub 1 + global irq%1 + irq%1: + push byte 0 ; pseudo error code + push byte 32+%1 + jmp common_stub +%endmacro + +; create entries for the interrupts 0 to 23 +%assign i 0 +%rep 24 + irqstub i +%assign i i+1 +%endrep + +extern irq_handler extern get_current_stack extern finish_task_switch global switch_context ALIGN 4 switch_context: + ; create on the stack a pseudo interrupt + ; afterwards, we switch to the task with iret + ; we already in kernel space => no pushing of SS required mov eax, [esp+4] ; on the stack is already the address to store the old esp pushf ; push controll register + push DWORD 0x8 ; CS + push DWORD rollback ; EIP + push DWORD 0x0 ; Interrupt number + push DWORD 0x00edbabe ; Error code pusha ; push all general purpose registers... + push 0x10 ; kernel data segment + push 0x10 ; kernel data segment - mov [eax], esp ; store old esp - call get_current_stack ; get new esp + jmp common_switch + +ALIGN 4 +rollback: + ret + +ALIGN 4 +common_stub: + pusha + push es + push ds + mov ax, 0x10 + mov es, ax + mov ds, ax + + ; use the same handler for interrupts and exceptions + push esp + call irq_handler + add esp, 4 + + cmp eax, 0 + je no_context_switch + +common_switch: + mov [eax], esp ; store old esp + call get_current_stack ; get new esp xchg eax, esp + ; set task switched flag + mov eax, cr0 + or eax, 8 + mov cr0, eax + ; call cleanup code call finish_task_switch - ; restore context +no_context_switch: + pop ds + pop es popa - popf - ret + add esp, 8 + iret -; Here is the definition of our stack. Remember that a stack actually grows -; downwards, so we declare the size of the data before declaring -; the identifier 'default_stack_pointer' -SECTION .data - resb 8192 ; This reserves 8KBytes of memory here -global default_stack_pointer -default_stack_pointer: +global boot_stack +ALIGN 4096 +boot_stack: +TIMES (KERNEL_STACK_SIZE) DB 0xcd SECTION .note.GNU-stack noalloc noexec nowrite progbits diff --git a/arch/x86/kernel/gdt.c b/arch/x86/kernel/gdt.c new file mode 100644 index 0000000..2d28f86 --- /dev/null +++ b/arch/x86/kernel/gdt.c @@ -0,0 +1,137 @@ +/* + * Copyright (c) 2010, Stefan Lankes, RWTH Aachen University + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +gdt_ptr_t gp; +static tss_t task_state_segment __attribute__ ((aligned (PAGE_SIZE))); +// currently, our kernel has full access to the ioports +static gdt_entry_t gdt[GDT_ENTRIES] = {[0 ... GDT_ENTRIES-1] = {0, 0, 0, 0, 0, 0}}; + +/* + * This is defined in entry.asm. We use this to properly reload + * the new segment registers + */ +extern void gdt_flush(void); + +/* Setup a descriptor in the Global Descriptor Table */ +void gdt_set_gate(int num, unsigned long base, unsigned long limit, + unsigned char access, unsigned char gran) +{ + configure_gdt_entry(&gdt[num], base, limit, access, gran); +} + +void configure_gdt_entry(gdt_entry_t *dest_entry, unsigned long base, unsigned long limit, + unsigned char access, unsigned char gran) +{ + /* Setup the descriptor base address */ + dest_entry->base_low = (base & 0xFFFF); + dest_entry->base_middle = (base >> 16) & 0xFF; + dest_entry->base_high = (base >> 24) & 0xFF; + + /* Setup the descriptor limits */ + dest_entry->limit_low = (limit & 0xFFFF); + dest_entry->granularity = ((limit >> 16) & 0x0F); + + /* Finally, set up the granularity and access flags */ + dest_entry->granularity |= (gran & 0xF0); + dest_entry->access = access; +} + +/* + * This will setup the special GDT + * pointer, set up the entries in our GDT, and then + * finally call gdt_flush() in our assembler file in order + * to tell the processor where the new GDT is and update the + * new segment registers + */ +void gdt_install(void) +{ + unsigned long mode, limit; + + memset(&task_state_segment, 0x00, sizeof(tss_t)); + + mode = GDT_FLAG_32_BIT; + limit = 0xFFFFFFFF; + + /* Setup the GDT pointer and limit */ + gp.limit = (sizeof(gdt_entry_t) * GDT_ENTRIES) - 1; + gp.base = (size_t) &gdt; + + /* Our NULL descriptor */ + gdt_set_gate(0, 0, 0, 0, 0); + + /* + * The second entry is our Code Segment. The base address + * is 0, the limit is 4 GByte, it uses 4KByte granularity, + * uses 32-bit opcodes, and is a Code Segment descriptor. + */ + gdt_set_gate(1, 0, limit, + GDT_FLAG_RING0 | GDT_FLAG_SEGMENT | GDT_FLAG_CODESEG | GDT_FLAG_PRESENT, + GDT_FLAG_4K_GRAN | mode); + + /* + * The third entry is our Data Segment. It's EXACTLY the + * same as our code segment, but the descriptor type in + * this entry's access byte says it's a Data Segment + */ + gdt_set_gate(2, 0, limit, + GDT_FLAG_RING0 | GDT_FLAG_SEGMENT | GDT_FLAG_DATASEG | GDT_FLAG_PRESENT, + GDT_FLAG_4K_GRAN | mode); + + /* + * Create code segement for userspace applications (ring 3) + */ + gdt_set_gate(3, 0, limit, + GDT_FLAG_RING3 | GDT_FLAG_SEGMENT | GDT_FLAG_CODESEG | GDT_FLAG_PRESENT, + GDT_FLAG_4K_GRAN | mode); + + /* + * Create data segement for userspace applications (ring 3) + */ + gdt_set_gate(4, 0, limit, + GDT_FLAG_RING3 | GDT_FLAG_SEGMENT | GDT_FLAG_DATASEG | GDT_FLAG_PRESENT, + GDT_FLAG_4K_GRAN | mode); + + /* set default values */ + task_state_segment.eflags = 0x1202; + task_state_segment.ss0 = 0x10; // data segment + task_state_segment.esp0 = 0xDEADBEEF; // invalid pseudo address + gdt_set_gate(5, (unsigned long) (&task_state_segment), sizeof(tss_t)-1, + GDT_FLAG_PRESENT | GDT_FLAG_TSS | GDT_FLAG_RING0, mode); + + /* Flush out the old GDT and install the new changes! */ + gdt_flush(); +} diff --git a/arch/x86/kernel/idt.c b/arch/x86/kernel/idt.c new file mode 100644 index 0000000..9508ab3 --- /dev/null +++ b/arch/x86/kernel/idt.c @@ -0,0 +1,96 @@ +/* + * Copyright (c) 2010, Stefan Lankes, RWTH Aachen University + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/** + * @author Stefan Lankes + * @file arch/x86/kernel/idt.c + * @brief Definitions and functions related to IDT + * + * + * This file defines the interface for interrupts as like + * structures to describe interrupt descriptor table entries.\n + * See idt.h for flag definitions. + */ + +#include +#include + +/* + * Declare an IDT of 256 entries. Although we will only use the + * first 32 entries in this tutorial, the rest exists as a bit + * of a trap. If any undefined IDT entry is hit, it normally + * will cause an "Unhandled Interrupt" exception. Any descriptor + * for which the 'presence' bit is cleared (0) will generate an + * "Unhandled Interrupt" exception + */ +static idt_entry_t idt[256] = {[0 ... 255] = {0, 0, 0, 0, 0}}; +static idt_ptr_t idtp; + +void configure_idt_entry(idt_entry_t *dest_entry, size_t base, + unsigned short sel, unsigned char flags) +{ + /* The interrupt routine's base address */ + dest_entry->base_lo = (base & 0xFFFF); + dest_entry->base_hi = (base >> 16) & 0xFFFF; + + /* The segment or 'selector' that this IDT entry will use + * is set here, along with any access flags */ + dest_entry->sel = sel; + dest_entry->always0 = 0; + dest_entry->flags = flags; +} + +/* + * Use this function to set an entry in the IDT. Alot simpler + * than twiddling with the GDT ;) + */ +void idt_set_gate(unsigned char num, size_t base, unsigned short sel, + unsigned char flags) +{ + configure_idt_entry(&idt[num], base, sel, flags); +} + +extern void isrsyscall(void); + +/* Installs the IDT */ +void idt_install(void) +{ + static int initialized = 0; + + if (!initialized) { + initialized = 1; + + /* Sets the special IDT pointer up, just like in 'gdt.c' */ + idtp.limit = (sizeof(idt_entry_t) * 256) - 1; + idtp.base = (size_t)&idt; + + /* Add any new ISRs to the IDT here using idt_set_gate */ + } + + /* Points the processor's internal register to the new IDT */ + asm volatile("lidt %0" : : "m" (idtp)); +} diff --git a/arch/x86/kernel/irq.c b/arch/x86/kernel/irq.c new file mode 100644 index 0000000..c2eaa3c --- /dev/null +++ b/arch/x86/kernel/irq.c @@ -0,0 +1,255 @@ +/* + * Copyright (c) 2010, Stefan Lankes, RWTH Aachen University + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/** + * @author Stefan Lankes + * @file arch/x86/kernel/irq.c + * @brief Function definitions for irq.h and a standard IRQ-handler + * + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +/* + * These are our own ISRs that point to our special IRQ handler + * instead of the regular 'fault_handler' function + */ +extern void irq0(void); +extern void irq1(void); +extern void irq2(void); +extern void irq3(void); +extern void irq4(void); +extern void irq5(void); +extern void irq6(void); +extern void irq7(void); +extern void irq8(void); +extern void irq9(void); +extern void irq10(void); +extern void irq11(void); +extern void irq12(void); +extern void irq13(void); +extern void irq14(void); +extern void irq15(void); +extern void irq16(void); +extern void irq17(void); +extern void irq18(void); +extern void irq19(void); +extern void irq20(void); +extern void irq21(void); +extern void irq22(void); +extern void irq23(void); + +#define MAX_HANDLERS 256 + +/** @brief IRQ handle pointers + * + * This array is actually an array of function pointers. We use + * this to handle custom IRQ handlers for a given IRQ + */ +static void* irq_routines[MAX_HANDLERS] = {[0 ... MAX_HANDLERS-1] = NULL }; + +/* This installs a custom IRQ handler for the given IRQ */ +int irq_install_handler(unsigned int irq, irq_handler_t handler) +{ + if (irq >= MAX_HANDLERS) + return -EINVAL; + + irq_routines[irq] = handler; + + return 0; +} + +/* This clears the handler for a given IRQ */ +int irq_uninstall_handler(unsigned int irq) +{ + if (irq >= MAX_HANDLERS) + return -EINVAL; + + irq_routines[irq] = NULL; + + return 0; +} + +/** @brief Remapping IRQs with a couple of IO output operations + * + * Normally, IRQs 0 to 7 are mapped to entries 8 to 15. This + * is a problem in protected mode, because IDT entry 8 is a + * Double Fault! Without remapping, every time IRQ0 fires, + * you get a Double Fault Exception, which is NOT what's + * actually happening. We send commands to the Programmable + * Interrupt Controller (PICs - also called the 8259's) in + * order to make IRQ0 to 15 be remapped to IDT entries 32 to + * 47 + */ +static int irq_remap(void) +{ + outportb(0x20, 0x11); + outportb(0xA0, 0x11); + outportb(0x21, 0x20); + outportb(0xA1, 0x28); + outportb(0x21, 0x04); + outportb(0xA1, 0x02); + outportb(0x21, 0x01); + outportb(0xA1, 0x01); + outportb(0x21, 0x0); + outportb(0xA1, 0x0); + + return 0; +} + +/** @brief Remap IRQs and install ISRs in IDT + * + * We first remap the interrupt controllers, and then we install + * the appropriate ISRs to the correct entries in the IDT.\n + * This is just like installing the exception handlers + */ +static int irq_install(void) +{ + irq_remap(); + + idt_set_gate(32, (size_t)irq0, KERNEL_CODE_SELECTOR, + IDT_FLAG_PRESENT|IDT_FLAG_RING0|IDT_FLAG_32BIT|IDT_FLAG_INTTRAP); + idt_set_gate(33, (size_t)irq1, KERNEL_CODE_SELECTOR, + IDT_FLAG_PRESENT|IDT_FLAG_RING0|IDT_FLAG_32BIT|IDT_FLAG_INTTRAP); + idt_set_gate(34, (size_t)irq2, KERNEL_CODE_SELECTOR, + IDT_FLAG_PRESENT|IDT_FLAG_RING0|IDT_FLAG_32BIT|IDT_FLAG_INTTRAP); + idt_set_gate(35, (size_t)irq3, KERNEL_CODE_SELECTOR, + IDT_FLAG_PRESENT|IDT_FLAG_RING0|IDT_FLAG_32BIT|IDT_FLAG_INTTRAP); + idt_set_gate(36, (size_t)irq4, KERNEL_CODE_SELECTOR, + IDT_FLAG_PRESENT|IDT_FLAG_RING0|IDT_FLAG_32BIT|IDT_FLAG_INTTRAP); + idt_set_gate(37, (size_t)irq5, KERNEL_CODE_SELECTOR, + IDT_FLAG_PRESENT|IDT_FLAG_RING0|IDT_FLAG_32BIT|IDT_FLAG_INTTRAP); + idt_set_gate(38, (size_t)irq6, KERNEL_CODE_SELECTOR, + IDT_FLAG_PRESENT|IDT_FLAG_RING0|IDT_FLAG_32BIT|IDT_FLAG_INTTRAP); + idt_set_gate(39, (size_t)irq7, KERNEL_CODE_SELECTOR, + IDT_FLAG_PRESENT|IDT_FLAG_RING0|IDT_FLAG_32BIT|IDT_FLAG_INTTRAP); + idt_set_gate(40, (size_t)irq8, KERNEL_CODE_SELECTOR, + IDT_FLAG_PRESENT|IDT_FLAG_RING0|IDT_FLAG_32BIT|IDT_FLAG_INTTRAP); + idt_set_gate(41, (size_t)irq9, KERNEL_CODE_SELECTOR, + IDT_FLAG_PRESENT|IDT_FLAG_RING0|IDT_FLAG_32BIT|IDT_FLAG_INTTRAP); + idt_set_gate(42, (size_t)irq10, KERNEL_CODE_SELECTOR, + IDT_FLAG_PRESENT|IDT_FLAG_RING0|IDT_FLAG_32BIT|IDT_FLAG_INTTRAP); + idt_set_gate(43, (size_t)irq11, KERNEL_CODE_SELECTOR, + IDT_FLAG_PRESENT|IDT_FLAG_RING0|IDT_FLAG_32BIT|IDT_FLAG_INTTRAP); + idt_set_gate(44, (size_t)irq12, KERNEL_CODE_SELECTOR, + IDT_FLAG_PRESENT|IDT_FLAG_RING0|IDT_FLAG_32BIT|IDT_FLAG_INTTRAP); + idt_set_gate(45, (size_t)irq13, KERNEL_CODE_SELECTOR, + IDT_FLAG_PRESENT|IDT_FLAG_RING0|IDT_FLAG_32BIT|IDT_FLAG_INTTRAP); + idt_set_gate(46, (size_t)irq14, KERNEL_CODE_SELECTOR, + IDT_FLAG_PRESENT|IDT_FLAG_RING0|IDT_FLAG_32BIT|IDT_FLAG_INTTRAP); + idt_set_gate(47, (size_t)irq15, KERNEL_CODE_SELECTOR, + IDT_FLAG_PRESENT|IDT_FLAG_RING0|IDT_FLAG_32BIT|IDT_FLAG_INTTRAP); + idt_set_gate(48, (size_t)irq16, KERNEL_CODE_SELECTOR, + IDT_FLAG_PRESENT|IDT_FLAG_RING0|IDT_FLAG_32BIT|IDT_FLAG_INTTRAP); + idt_set_gate(49, (size_t)irq17, KERNEL_CODE_SELECTOR, + IDT_FLAG_PRESENT|IDT_FLAG_RING0|IDT_FLAG_32BIT|IDT_FLAG_INTTRAP); + idt_set_gate(50, (size_t)irq18, KERNEL_CODE_SELECTOR, + IDT_FLAG_PRESENT|IDT_FLAG_RING0|IDT_FLAG_32BIT|IDT_FLAG_INTTRAP); + idt_set_gate(51, (size_t)irq19, KERNEL_CODE_SELECTOR, + IDT_FLAG_PRESENT|IDT_FLAG_RING0|IDT_FLAG_32BIT|IDT_FLAG_INTTRAP); + idt_set_gate(52, (size_t)irq20, KERNEL_CODE_SELECTOR, + IDT_FLAG_PRESENT|IDT_FLAG_RING0|IDT_FLAG_32BIT|IDT_FLAG_INTTRAP); + idt_set_gate(53, (size_t)irq21, KERNEL_CODE_SELECTOR, + IDT_FLAG_PRESENT|IDT_FLAG_RING0|IDT_FLAG_32BIT|IDT_FLAG_INTTRAP); + idt_set_gate(54, (size_t)irq22, KERNEL_CODE_SELECTOR, + IDT_FLAG_PRESENT|IDT_FLAG_RING0|IDT_FLAG_32BIT|IDT_FLAG_INTTRAP); + idt_set_gate(55, (size_t)irq23, KERNEL_CODE_SELECTOR, + IDT_FLAG_PRESENT|IDT_FLAG_RING0|IDT_FLAG_32BIT|IDT_FLAG_INTTRAP); + + return 0; +} + +int irq_init(void) +{ + idt_install(); + isrs_install(); + irq_install(); + + return 0; +} + +/** @brief Default IRQ handler + * + * Each of the IRQ ISRs point to this function, rather than + * the 'fault_handler' in 'isrs.c'. The IRQ Controllers need + * to be told when you are done servicing them, so you need + * to send them an "End of Interrupt" command. If we use the PIC + * instead of the APIC, we have two 8259 chips: The first one + * exists at 0x20, the second one exists at 0xA0. If the second + * controller (an IRQ from 8 to 15) gets an interrupt, you need to + * acknowledge the interrupt at BOTH controllers, otherwise, you + * only send an EOI command to the first controller. If you don't send + * an EOI, it won't raise any more IRQs. + * + * Note: If we enabled the APIC, we also disabled the PIC. Afterwards, + * we get no interrupts between 0 and 15. + */ +size_t** irq_handler(struct state *s) +{ + /* This is a blank function pointer */ + void (*handler) (struct state * s); + + /* + * Find out if we have a custom handler to run for this + * IRQ and then finally, run it + */ + if (BUILTIN_EXPECT(s->int_no < MAX_HANDLERS, 1)) { + handler = irq_routines[s->int_no]; + if (handler) + handler(s); + } else kprintf("Invalid interrupt number %d\n", s->int_no); + + /* + * If the IDT entry that was invoked was greater-than-or-equal to 40 + * and lower than 48 (meaning IRQ8 - 15), then we need to + * send an EOI to the slave controller of the PIC + */ + if (s->int_no >= 40) + outportb(0xA0, 0x20); + + /* + * In either case, we need to send an EOI to the master + * interrupt controller of the PIC, too + */ + outportb(0x20, 0x20); + + // timer interrupt? + if (s->int_no == 32) + return scheduler(); // switch to a new task + else if ((s->int_no >= 32) && (get_highest_priority() > current_task->prio)) + return scheduler(); + + return NULL; +} diff --git a/arch/x86/kernel/isrs.c b/arch/x86/kernel/isrs.c new file mode 100644 index 0000000..7ca7f5f --- /dev/null +++ b/arch/x86/kernel/isrs.c @@ -0,0 +1,207 @@ +/* + * Copyright (c) 2010, Stefan Lankes, RWTH Aachen University + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/** + * @author Stefan Lankes + * @file arch/x86/kernel/isrs.c + * @brief Installation of interrupt service routines and definition of fault handler. + * + * This file contains prototypes for the first 32 entries of the IDT, + * an ISR installer procedure and a fault handler.\n + */ + +#include +#include +#include +#include +#include +#include +#include + +/* + * These are function prototypes for all of the exception + * handlers: The first 32 entries in the IDT are reserved + * by Intel and are designed to service exceptions! + */ +extern void isr0(void); +extern void isr1(void); +extern void isr2(void); +extern void isr3(void); +extern void isr4(void); +extern void isr5(void); +extern void isr6(void); +extern void isr7(void); +extern void isr8(void); +extern void isr9(void); +extern void isr10(void); +extern void isr11(void); +extern void isr12(void); +extern void isr13(void); +extern void isr14(void); +extern void isr15(void); +extern void isr16(void); +extern void isr17(void); +extern void isr18(void); +extern void isr19(void); +extern void isr20(void); +extern void isr21(void); +extern void isr22(void); +extern void isr23(void); +extern void isr24(void); +extern void isr25(void); +extern void isr26(void); +extern void isr27(void); +extern void isr28(void); +extern void isr29(void); +extern void isr30(void); +extern void isr31(void); + +static void fault_handler(struct state *s); + +/* + * This is a very repetitive function... it's not hard, it's + * just annoying. As you can see, we set the first 32 entries + * in the IDT to the first 32 ISRs. We can't use a for loop + * for this, because there is no way to get the function names + * that correspond to that given entry. We set the access + * flags to 0x8E. This means that the entry is present, is + * running in ring 0 (kernel level), and has the lower 5 bits + * set to the required '14', which is represented by 'E' in + * hex. + */ +void isrs_install(void) +{ + int i; + + idt_set_gate(0, (size_t)isr0, KERNEL_CODE_SELECTOR, + IDT_FLAG_PRESENT|IDT_FLAG_RING0|IDT_FLAG_32BIT|IDT_FLAG_INTTRAP); + idt_set_gate(1, (size_t)isr1, KERNEL_CODE_SELECTOR, + IDT_FLAG_PRESENT|IDT_FLAG_RING0|IDT_FLAG_32BIT|IDT_FLAG_INTTRAP); + idt_set_gate(2, (size_t)isr2, KERNEL_CODE_SELECTOR, + IDT_FLAG_PRESENT|IDT_FLAG_RING0|IDT_FLAG_32BIT|IDT_FLAG_INTTRAP); + idt_set_gate(3, (size_t)isr3, KERNEL_CODE_SELECTOR, + IDT_FLAG_PRESENT|IDT_FLAG_RING0|IDT_FLAG_32BIT|IDT_FLAG_INTTRAP); + idt_set_gate(4, (size_t)isr4, KERNEL_CODE_SELECTOR, + IDT_FLAG_PRESENT|IDT_FLAG_RING0|IDT_FLAG_32BIT|IDT_FLAG_INTTRAP); + idt_set_gate(5, (size_t)isr5, KERNEL_CODE_SELECTOR, + IDT_FLAG_PRESENT|IDT_FLAG_RING0|IDT_FLAG_32BIT|IDT_FLAG_INTTRAP); + idt_set_gate(6, (size_t)isr6, KERNEL_CODE_SELECTOR, + IDT_FLAG_PRESENT|IDT_FLAG_RING0|IDT_FLAG_32BIT|IDT_FLAG_INTTRAP); + idt_set_gate(7, (size_t)isr7, KERNEL_CODE_SELECTOR, + IDT_FLAG_PRESENT|IDT_FLAG_RING0|IDT_FLAG_32BIT|IDT_FLAG_INTTRAP); + idt_set_gate(8, (size_t)isr8, KERNEL_CODE_SELECTOR, + IDT_FLAG_PRESENT|IDT_FLAG_RING0|IDT_FLAG_32BIT|IDT_FLAG_INTTRAP); + idt_set_gate(9, (size_t)isr9, KERNEL_CODE_SELECTOR, + IDT_FLAG_PRESENT|IDT_FLAG_RING0|IDT_FLAG_32BIT|IDT_FLAG_INTTRAP); + idt_set_gate(10, (size_t)isr10, KERNEL_CODE_SELECTOR, + IDT_FLAG_PRESENT|IDT_FLAG_RING0|IDT_FLAG_32BIT|IDT_FLAG_INTTRAP); + idt_set_gate(11, (size_t)isr11, KERNEL_CODE_SELECTOR, + IDT_FLAG_PRESENT|IDT_FLAG_RING0|IDT_FLAG_32BIT|IDT_FLAG_INTTRAP); + idt_set_gate(12, (size_t)isr12, KERNEL_CODE_SELECTOR, + IDT_FLAG_PRESENT|IDT_FLAG_RING0|IDT_FLAG_32BIT|IDT_FLAG_INTTRAP); + idt_set_gate(13, (size_t)isr13, KERNEL_CODE_SELECTOR, + IDT_FLAG_PRESENT|IDT_FLAG_RING0|IDT_FLAG_32BIT|IDT_FLAG_INTTRAP); + idt_set_gate(14, (size_t)isr14, KERNEL_CODE_SELECTOR, + IDT_FLAG_PRESENT|IDT_FLAG_RING0|IDT_FLAG_32BIT|IDT_FLAG_INTTRAP); + idt_set_gate(15, (size_t)isr15, KERNEL_CODE_SELECTOR, + IDT_FLAG_PRESENT|IDT_FLAG_RING0|IDT_FLAG_32BIT|IDT_FLAG_INTTRAP); + idt_set_gate(16, (size_t)isr16, KERNEL_CODE_SELECTOR, + IDT_FLAG_PRESENT|IDT_FLAG_RING0|IDT_FLAG_32BIT|IDT_FLAG_INTTRAP); + idt_set_gate(17, (size_t)isr17, KERNEL_CODE_SELECTOR, + IDT_FLAG_PRESENT|IDT_FLAG_RING0|IDT_FLAG_32BIT|IDT_FLAG_INTTRAP); + idt_set_gate(18, (size_t)isr18, KERNEL_CODE_SELECTOR, + IDT_FLAG_PRESENT|IDT_FLAG_RING0|IDT_FLAG_32BIT|IDT_FLAG_INTTRAP); + idt_set_gate(19, (size_t)isr19, KERNEL_CODE_SELECTOR, + IDT_FLAG_PRESENT|IDT_FLAG_RING0|IDT_FLAG_32BIT|IDT_FLAG_INTTRAP); + idt_set_gate(20, (size_t)isr20, KERNEL_CODE_SELECTOR, + IDT_FLAG_PRESENT|IDT_FLAG_RING0|IDT_FLAG_32BIT|IDT_FLAG_INTTRAP); + idt_set_gate(21, (size_t)isr21, KERNEL_CODE_SELECTOR, + IDT_FLAG_PRESENT|IDT_FLAG_RING0|IDT_FLAG_32BIT|IDT_FLAG_INTTRAP); + idt_set_gate(22, (size_t)isr22, KERNEL_CODE_SELECTOR, + IDT_FLAG_PRESENT|IDT_FLAG_RING0|IDT_FLAG_32BIT|IDT_FLAG_INTTRAP); + idt_set_gate(23, (size_t)isr23, KERNEL_CODE_SELECTOR, + IDT_FLAG_PRESENT|IDT_FLAG_RING0|IDT_FLAG_32BIT|IDT_FLAG_INTTRAP); + idt_set_gate(24, (size_t)isr24, KERNEL_CODE_SELECTOR, + IDT_FLAG_PRESENT|IDT_FLAG_RING0|IDT_FLAG_32BIT|IDT_FLAG_INTTRAP); + idt_set_gate(25, (size_t)isr25, KERNEL_CODE_SELECTOR, + IDT_FLAG_PRESENT|IDT_FLAG_RING0|IDT_FLAG_32BIT|IDT_FLAG_INTTRAP); + idt_set_gate(26, (size_t)isr26, KERNEL_CODE_SELECTOR, + IDT_FLAG_PRESENT|IDT_FLAG_RING0|IDT_FLAG_32BIT|IDT_FLAG_INTTRAP); + idt_set_gate(27, (size_t)isr27, KERNEL_CODE_SELECTOR, + IDT_FLAG_PRESENT|IDT_FLAG_RING0|IDT_FLAG_32BIT|IDT_FLAG_INTTRAP); + idt_set_gate(28, (size_t)isr28, KERNEL_CODE_SELECTOR, + IDT_FLAG_PRESENT|IDT_FLAG_RING0|IDT_FLAG_32BIT|IDT_FLAG_INTTRAP); + idt_set_gate(29, (size_t)isr29, KERNEL_CODE_SELECTOR, + IDT_FLAG_PRESENT|IDT_FLAG_RING0|IDT_FLAG_32BIT|IDT_FLAG_INTTRAP); + idt_set_gate(30, (size_t)isr30, KERNEL_CODE_SELECTOR, + IDT_FLAG_PRESENT|IDT_FLAG_RING0|IDT_FLAG_32BIT|IDT_FLAG_INTTRAP); + idt_set_gate(31, (size_t)isr31, KERNEL_CODE_SELECTOR, + IDT_FLAG_PRESENT|IDT_FLAG_RING0|IDT_FLAG_32BIT|IDT_FLAG_INTTRAP); + + // install the default handler + for(i=0; i<32; i++) + irq_install_handler(i, fault_handler); +} + +/** @brief Exception messages + * + * This is a simple string array. It contains the message that + * corresponds to each and every exception. We get the correct + * message by accessing it like this: + * exception_message[interrupt_number] + */ +static const char *exception_messages[] = { + "Division By Zero", "Debug", "Non Maskable Interrupt", + "Breakpoint", "Into Detected Overflow", "Out of Bounds", "Invalid Opcode", + "No Coprocessor", "Double Fault", "Coprocessor Segment Overrun", "Bad TSS", + "Segment Not Present", "Stack Fault", "General Protection Fault", "Page Fault", + "Unknown Interrupt", "Coprocessor Fault", "Alignment Check", "Machine Check", + "Reserved", "Reserved", "Reserved", "Reserved", "Reserved", + "Reserved", "Reserved", "Reserved", "Reserved", "Reserved", "Reserved", + "Reserved", "Reserved" }; + +/* + * All of our Exception handling Interrupt Service Routines will + * point to this function. This will tell us what exception has + * occured! Right now, we simply abort the current task. + * All ISRs disable interrupts while they are being + * serviced as a 'locking' mechanism to prevent an IRQ from + * happening and messing up kernel data structures + */ +static void fault_handler(struct state *s) +{ + if (s->int_no < 32) { + kputs(exception_messages[s->int_no]); + kprintf(" Exception (%d) at 0x%x:0x%x, error code 0x%x, eflags 0x%x\n", + s->int_no, s->cs, s->eip, s->error, s->eflags); + + outportb(0x20, 0x20); + + irq_enable(); + abort(); + } +} diff --git a/arch/x86/kernel/processor.c b/arch/x86/kernel/processor.c new file mode 100644 index 0000000..33c8b6f --- /dev/null +++ b/arch/x86/kernel/processor.c @@ -0,0 +1,72 @@ +/* + * Copyright (c) 2010, Stefan Lankes, RWTH Aachen University + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include +#include +#include +#include +#include +#include + +static uint32_t cpu_freq = 0; + +uint32_t detect_cpu_frequency(void) +{ + uint64_t start, end, diff; + uint64_t ticks, old; + + if (BUILTIN_EXPECT(cpu_freq > 0, 0)) + return cpu_freq; + + old = get_clock_tick(); + + /* wait for the next time slice */ + while((ticks = get_clock_tick()) - old == 0) + HALT; + + rmb(); + start = rdtsc(); + /* wait a second to determine the frequency */ + while(get_clock_tick() - ticks < TIMER_FREQ) + HALT; + rmb(); + end = rdtsc(); + + diff = end > start ? end - start : start - end; + cpu_freq = (uint32_t) (diff / (uint64_t) 1000000); + + return cpu_freq; +} + +uint32_t get_cpu_frequency(void) +{ + if (cpu_freq > 0) + return cpu_freq; + + return detect_cpu_frequency(); +} + diff --git a/arch/x86/kernel/tasks.c b/arch/x86/kernel/tasks.c index e72ca2d..7419e2f 100644 --- a/arch/x86/kernel/tasks.c +++ b/arch/x86/kernel/tasks.c @@ -72,15 +72,25 @@ int create_default_frame(task_t* task, entry_point_t ep, void* arg) * The stack must look like the stack of a task which was * scheduled away previously. */ - state_size = sizeof(struct state); + /* In legacy modes, this push is conditional and based on a change in current privilege level (CPL).*/ + state_size = sizeof(struct state) - 2*sizeof(size_t); stack = (size_t*) ((size_t) stack - state_size); stptr = (struct state *) stack; memset(stptr, 0x00, state_size); stptr->esp = (size_t)stack + state_size; + stptr->int_no = 0xB16B00B5; + stptr->error = 0xC03DB4B3; + + /* The instruction pointer shall be set on the first function to be called + after IRETing */ stptr->eip = (size_t)ep; + stptr->cs = 0x08; + stptr->ds = stptr->es = 0x10; stptr->eflags = 0x1202; + // the creation of a kernel tasks didn't change the IOPL level + // => useresp & ss is not required /* Set the task's stack pointer entry to the stack we have crafted right now. */ task->last_stack_pointer = (size_t*)stack; diff --git a/arch/x86/kernel/vga.c b/arch/x86/kernel/vga.c index 1107aee..c1eca9c 100644 --- a/arch/x86/kernel/vga.c +++ b/arch/x86/kernel/vga.c @@ -29,6 +29,8 @@ #include #include +#ifdef CONFIG_VGA + /* * These define our textpointer, our background and foreground * colors (attributes), and x and y cursor coordinates @@ -233,3 +235,5 @@ void vga_init(void) textmemptr = (unsigned short *)VIDEO_MEM_ADDR; vga_clear(); } + +#endif diff --git a/include/eduos/config.h.example b/include/eduos/config.h.example index df5e5aa..ca32416 100644 --- a/include/eduos/config.h.example +++ b/include/eduos/config.h.example @@ -34,12 +34,17 @@ extern "C" { #define EDUOS_VERSION "0.1" #define MAX_TASKS 16 -#define VIDEO_MEM_ADDR 0xB8000 // the video memora address +#define TIMER_FREQ 100 /* in HZ */ +#define CLOCK_TICK_RATE 1193182 /* 8254 chip's internal oscillator frequency */ +#define VIDEO_MEM_ADDR 0xB8000 // the video memora address #define CACHE_LINE 64 #define KERNEL_STACK_SIZE (8*1024) +#define PAGE_SHIFT 12 #define BYTE_ORDER LITTLE_ENDIAN +#define CONFIG_VGA + #define BUILTIN_EXPECT(exp, b) __builtin_expect((exp), (b)) //#define BUILTIN_EXPECT(exp, b) (exp) #define NORETURN __attribute__((noreturn)) diff --git a/include/eduos/stddef.h b/include/eduos/stddef.h index e488668..ca3fa70 100644 --- a/include/eduos/stddef.h +++ b/include/eduos/stddef.h @@ -46,6 +46,10 @@ extern "C" { /// represents a task identifier typedef unsigned int tid_t; +#define PAGE_SIZE (1 << PAGE_SHIFT) +#define PAGE_MASK ~(PAGE_SIZE - 1) +#define PAGE_ALIGN(addr) (((addr) + PAGE_SIZE - 1) & PAGE_MASK) + struct task; /// pointer to the current (running) task extern struct task* current_task; diff --git a/include/eduos/tasks.h b/include/eduos/tasks.h index 11515c1..92c0ae0 100644 --- a/include/eduos/tasks.h +++ b/include/eduos/tasks.h @@ -44,6 +44,16 @@ extern "C" { #endif +/** @brief Task switcher + * + * Timer-interrupted use of this function for task switching + * + * @return + * - 0 no context switch + * - !0 address of the old stack pointer + */ +size_t** scheduler(void); + /** @brief Initialize the multitasking subsystem * * This procedure sets the current task to the @@ -68,6 +78,14 @@ int multitasking_init(void); */ int create_kernel_task(tid_t* id, entry_point_t ep, void* args, uint8_t prio); +/** @brief determine the highest priority of all tasks, which are ready + * + * @return + * - return highest priority + * - if no task is ready, the function returns an invalid value (> MAX_PRIO) + */ +uint32_t get_highest_priority(void); + /** @brief Call to rescheduling * * This is a purely assembled procedure for rescheduling @@ -94,6 +112,9 @@ int wakeup_task(tid_t); */ int block_current_task(void); +/** @brief Abort current task */ +void NORETURN abort(void); + /** @brief This function shall be called by leaving kernel level tasks */ void NORETURN leave_kernel_task(void); diff --git a/include/eduos/tasks_types.h b/include/eduos/tasks_types.h index 7743c12..c122c40 100644 --- a/include/eduos/tasks_types.h +++ b/include/eduos/tasks_types.h @@ -38,6 +38,7 @@ #define __TASKS_TYPES_H__ #include +#include #include #ifdef __cplusplus @@ -95,6 +96,8 @@ typedef struct { uint32_t prio_bitmap; /// a queue for each priority task_list_t queue[MAX_PRIO-1]; + /// lock for this runqueue + spinlock_irqsave_t lock; } readyqueues_t; #ifdef __cplusplus diff --git a/include/eduos/time.h b/include/eduos/time.h new file mode 100644 index 0000000..8e60e5c --- /dev/null +++ b/include/eduos/time.h @@ -0,0 +1,56 @@ +/* + * Copyright (c) 2010, Stefan Lankes, RWTH Aachen University + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/** + * @author Stefan Lankes + * @file include/eduos/time.h + * @brief Time related functions + */ + +#ifndef __TIME_H__ +#define __TIME_H__ + +#ifdef __cplusplus +extern "C" { +#endif + +/** @brief Initialize Timer interrupts + * + * This procedure installs IRQ handlers for timer interrupts + */ +int timer_init(void); + +/** @brief Returns the current number of ticks. + * @return Current number of ticks + */ +uint64_t get_clock_tick(void); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/kernel/main.c b/kernel/main.c index 09b45f9..6c66bf6 100644 --- a/kernel/main.c +++ b/kernel/main.c @@ -28,10 +28,13 @@ #include #include #include +#include #include #include #include #include +#include +#include /* * Note that linker symbols are not variables, they have no memory allocated for @@ -48,13 +51,10 @@ static sem_t sem; static int foo(void* arg) { - int i = 0; + int i; - for(i=0; i<5; i++) { - sem_wait(&sem); + for(i=0; i<10; i++) { kprintf("hello from %s\n", (char*) arg); - reschedule(); - sem_post(&sem); } return 0; @@ -65,6 +65,9 @@ static int eduos_init(void) // initialize .bss section memset((void*)&bss_start, 0x00, ((size_t) &bss_end - (size_t) &bss_start)); + system_init(); + irq_init(); + timer_init(); koutput_init(); multitasking_init(); @@ -80,13 +83,17 @@ int main(void) kprintf("This is eduOS %s Build %u, %u\n", EDUOS_VERSION, &__BUILD_DATE, &__BUILD_TIME); kprintf("Kernel starts at %p and ends at %p\n", &kernel_start, &kernel_end); + irq_enable(); + system_calibration(); + + kprintf("Processor frequency: %u MHz\n", get_cpu_frequency()); + sem_init(&sem, 1); create_kernel_task(&id1, foo, "foo1", NORMAL_PRIO); create_kernel_task(&id2, foo, "foo2", NORMAL_PRIO); - reschedule(); while(1) { - NOP8; + HALT; } return 0; diff --git a/kernel/tasks.c b/kernel/tasks.c index 17a6abf..1418503 100644 --- a/kernel/tasks.c +++ b/kernel/tasks.c @@ -30,10 +30,9 @@ #include #include #include +#include #include -extern void* default_stack_pointer; - /** @brief Array of task structures (aka PCB) * * A task's id will be its position in this array. @@ -42,9 +41,12 @@ static task_t task_table[MAX_TASKS] = { \ [0] = {0, TASK_IDLE, NULL, NULL, 0, NULL, NULL}, \ [1 ... MAX_TASKS-1] = {0, TASK_INVALID, NULL, NULL, 0, NULL, NULL}}; -static readyqueues_t readyqueues = { task_table+0, NULL, 0, 0, {[0 ... MAX_PRIO-2] = {NULL, NULL}}}; +static spinlock_irqsave_t table_lock = SPINLOCK_IRQSAVE_INIT; + +static readyqueues_t readyqueues = {task_table+0, NULL, 0, 0, {[0 ... MAX_PRIO-2] = {NULL, NULL}}, SPINLOCK_IRQSAVE_INIT}; task_t* current_task = task_table+0; +extern const void boot_stack; /** @brief helper function for the assembly code to determine the current task * @return Pointer to the task_t structure of current task @@ -54,6 +56,11 @@ task_t* get_current_task(void) return current_task; } +uint32_t get_highest_priority(void) +{ + return msb(readyqueues.prio_bitmap); +} + int multitasking_init(void) { if (BUILTIN_EXPECT(task_table[0].status != TASK_IDLE, 0)) { @@ -62,7 +69,7 @@ int multitasking_init(void) } task_table[0].prio = IDLE_PRIO; - task_table[0].stack = default_stack_pointer - 8192; + task_table[0].stack = (void*) &boot_stack; return 0; } @@ -72,6 +79,8 @@ void finish_task_switch(void) task_t* old; uint8_t prio; + spinlock_irqsave_lock(&readyqueues.lock); + if ((old = readyqueues.old_task) != NULL) { if (old->status == TASK_INVALID) { old->stack = NULL; @@ -92,6 +101,8 @@ void finish_task_switch(void) readyqueues.prio_bitmap |= (1 << prio); } } + + spinlock_irqsave_unlock(&readyqueues.lock); } /** @brief A procedure to be called by @@ -105,9 +116,14 @@ static void NORETURN do_exit(int arg) curr_task->status = TASK_FINISHED; reschedule(); + // decrease the number of active tasks + spinlock_irqsave_lock(&readyqueues.lock); + readyqueues.nr_tasks--; + spinlock_irqsave_unlock(&readyqueues.lock); + kprintf("Kernel panic: scheduler found no valid task\n"); while(1) { - NOP8; + HALT; } } @@ -119,6 +135,11 @@ void NORETURN leave_kernel_task(void) { do_exit(result); } +/** @brief Aborting a task is like exiting it with result -1 */ +void NORETURN abort(void) { + do_exit(-1); +} + /** @brief Create a task with a specific entry point * * @param id Pointer to a tid_t struct were the id shall be set @@ -143,6 +164,8 @@ static int create_task(tid_t* id, entry_point_t ep, void* arg, uint8_t prio) if (BUILTIN_EXPECT(prio > MAX_PRIO, 0)) return -EINVAL; + spinlock_irqsave_lock(&table_lock); + for(i=0; inext = task_table+i; readyqueues.queue[prio-1].last = task_table+i; } + spinlock_irqsave_unlock(&readyqueues.lock); break; } } + spinlock_irqsave_unlock(&table_lock); + return ret; } @@ -195,6 +222,9 @@ int wakeup_task(tid_t id) task_t* task; uint32_t prio; int ret = -EINVAL; + uint8_t flags; + + flags = irq_nested_disable(); task = task_table + id; prio = task->prio; @@ -203,6 +233,7 @@ int wakeup_task(tid_t id) task->status = TASK_READY; ret = 0; + spinlock_irqsave_lock(&readyqueues.lock); // increase the number of ready tasks readyqueues.nr_tasks++; @@ -217,8 +248,11 @@ int wakeup_task(tid_t id) readyqueues.queue[prio-1].last->next = task; readyqueues.queue[prio-1].last = task; } + spinlock_irqsave_unlock(&readyqueues.lock); } + irq_nested_enable(flags); + return ret; } @@ -235,6 +269,9 @@ int block_current_task(void) tid_t id; uint32_t prio; int ret = -EINVAL; + uint8_t flags; + + flags = irq_nested_disable(); id = current_task->id; prio = current_task->prio; @@ -243,6 +280,7 @@ int block_current_task(void) task_table[id].status = TASK_BLOCKED; ret = 0; + spinlock_irqsave_lock(&readyqueues.lock); // reduce the number of ready tasks readyqueues.nr_tasks--; @@ -262,8 +300,11 @@ int block_current_task(void) // No valid task in queue => update prio_bitmap if (!readyqueues.queue[prio-1].first) readyqueues.prio_bitmap &= ~(1 << prio); + spinlock_irqsave_unlock(&readyqueues.lock); } + irq_nested_enable(flags); + return ret; } @@ -274,6 +315,8 @@ size_t** scheduler(void) orig_task = current_task; + spinlock_irqsave_lock(&readyqueues.lock); + /* signalizes that this task could be reused */ if (current_task->status == TASK_FINISHED) { current_task->status = TASK_INVALID; @@ -312,6 +355,8 @@ size_t** scheduler(void) } get_task_out: + spinlock_irqsave_unlock(&readyqueues.lock); + if (current_task != orig_task) { //kprintf("schedule from %u to %u with prio %u\n", orig_task->id, current_task->id, (uint32_t)current_task->prio); @@ -324,6 +369,10 @@ get_task_out: void reschedule(void) { size_t** stack; + uint8_t flags; + + flags = irq_nested_disable(); if ((stack = scheduler())) switch_context(stack); + irq_nested_enable(flags); } diff --git a/libkern/stdio.c b/libkern/stdio.c index d9f3a61..a725727 100644 --- a/libkern/stdio.c +++ b/libkern/stdio.c @@ -28,28 +28,39 @@ #include #include #include +#include #include +static spinlock_irqsave_t olock = SPINLOCK_IRQSAVE_INIT; + int koutput_init(void) { +#ifdef CONFIG_VGA vga_init(); +#endif return 0; } int kputchar(int c) { + spinlock_irqsave_lock(&olock); +#ifdef CONFIG_VGA vga_putchar(c); +#endif + spinlock_irqsave_unlock(&olock); return 1; } int kputs(const char *str) { +#ifdef CONFIG_VGA int i; - for(i=0; str[i] != '\0'; i++) vga_putchar((int) str[i]); return i; +#else +#endif }