add preemptive multitasking and interrupt handling

This commit is contained in:
Stefan Lankes 2013-11-27 22:49:30 +01:00
parent 1285a8c4fe
commit 87801a1706
25 changed files with 1700 additions and 33 deletions

View file

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

163
arch/x86/include/asm/gdt.h Normal file
View file

@ -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 <eduos/stddef.h>
#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

138
arch/x86/include/asm/idt.h Normal file
View file

@ -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 <eduos/stddef.h>
/// 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

View file

@ -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 <eduos/stddef.h>
#include <eduos/tasks_types.h>
#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

View file

@ -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 <eduos/stddef.h>
#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

View file

@ -37,6 +37,7 @@
#define __ARCH_PROCESSOR_H__
#include <eduos/stddef.h>
#include <asm/gdt.h>
#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
}

View file

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

View file

@ -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 <eduos/stddef.h>
#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

View file

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

View file

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

137
arch/x86/kernel/gdt.c Normal file
View file

@ -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 <eduos/stdio.h>
#include <eduos/string.h>
#include <eduos/stdlib.h>
#include <eduos/tasks.h>
#include <eduos/errno.h>
#include <eduos/processor.h>
#include <eduos/time.h>
#include <asm/gdt.h>
#include <asm/tss.h>
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();
}

96
arch/x86/kernel/idt.c Normal file
View file

@ -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 <eduos/string.h>
#include <asm/idt.h>
/*
* 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));
}

255
arch/x86/kernel/irq.c Normal file
View file

@ -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 <eduos/stdio.h>
#include <eduos/string.h>
#include <eduos/tasks.h>
#include <eduos/errno.h>
#include <asm/irq.h>
#include <asm/idt.h>
#include <asm/isrs.h>
#include <asm/io.h>
/*
* 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;
}

207
arch/x86/kernel/isrs.c Normal file
View file

@ -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 <eduos/stdio.h>
#include <eduos/tasks.h>
#include <asm/irqflags.h>
#include <asm/isrs.h>
#include <asm/irq.h>
#include <asm/idt.h>
#include <asm/io.h>
/*
* 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();
}
}

View file

@ -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 <eduos/stddef.h>
#include <eduos/stdio.h>
#include <eduos/string.h>
#include <eduos/time.h>
#include <eduos/processor.h>
#include <eduos/tasks.h>
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();
}

View file

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

View file

@ -29,6 +29,8 @@
#include <asm/io.h>
#include <asm/vga.h>
#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

View file

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

View file

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

View file

@ -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);

View file

@ -38,6 +38,7 @@
#define __TASKS_TYPES_H__
#include <eduos/stddef.h>
#include <eduos/spinlock_types.h>
#include <asm/tasks_types.h>
#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

56
include/eduos/time.h Normal file
View file

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

View file

@ -28,10 +28,13 @@
#include <eduos/stddef.h>
#include <eduos/stdio.h>
#include <eduos/string.h>
#include <eduos/time.h>
#include <eduos/tasks.h>
#include <eduos/processor.h>
#include <eduos/tasks.h>
#include <eduos/semaphore.h>
#include <asm/irq.h>
#include <asm/irqflags.h>
/*
* 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;

View file

@ -30,10 +30,9 @@
#include <eduos/stdio.h>
#include <eduos/tasks.h>
#include <eduos/tasks_types.h>
#include <eduos/spinlock.h>
#include <eduos/errno.h>
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; i<MAX_TASKS; i++) {
if (task_table[i].status == TASK_INVALID) {
task_table[i].id = i;
@ -157,6 +180,7 @@ static int create_task(tid_t* id, entry_point_t ep, void* arg, uint8_t prio)
ret = create_default_frame(task_table+i, ep, arg);
// add task in the readyqueues
spinlock_irqsave_lock(&readyqueues.lock);
readyqueues.prio_bitmap |= (1 << prio);
readyqueues.nr_tasks++;
if (!readyqueues.queue[prio-1].first) {
@ -169,10 +193,13 @@ static int create_task(tid_t* id, entry_point_t ep, void* arg, uint8_t prio)
readyqueues.queue[prio-1].last->next = 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);
}

View file

@ -28,28 +28,39 @@
#include <eduos/stdio.h>
#include <eduos/string.h>
#include <eduos/stdarg.h>
#include <eduos/spinlock.h>
#include <asm/vga.h>
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
}