Skip to content

Commit

Permalink
Add GDT, IDT and interrupt handlers
Browse files Browse the repository at this point in the history
  • Loading branch information
jonpas committed Jan 18, 2018
1 parent 69f48b4 commit e8243ae
Show file tree
Hide file tree
Showing 9 changed files with 353 additions and 4 deletions.
25 changes: 25 additions & 0 deletions copy.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
Welcome to Tyr (0x547972), the one-handed OS!























4 changes: 2 additions & 2 deletions src/boot.asm
Original file line number Diff line number Diff line change
Expand Up @@ -32,11 +32,11 @@ mboot:
section .text

[GLOBAL start] ; Kernel entry point
[EXTERN main] ; C code entry point
[EXTERN kernel_main] ; C code entry point

start:
; Load multiboot information
push ebx ; Save mboot information structure
cli ; Disable interrupts
call main ; Call 'main()' C function
call kernel_main ; Call 'kernel_main()' C function
jmp $ ; Continue loop
104 changes: 104 additions & 0 deletions src/descriptor_tables.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
#include "descriptor_tables.h"

// Get access to ASM functions from C code
extern void gdt_flush(u32int);
extern void idt_flush(u32int);

// Internal function prototypes
static void init_gdt();
static void init_idt();
static void gdt_set_gate(s32int,u32int,u32int,u8int,u8int);
static void idt_set_gate(u8int,u32int,u16int,u8int);

gdt_entry_t gdt_entries[5];
gdt_ptr_t gdt_ptr;
idt_entry_t idt_entries[256];
idt_ptr_t idt_ptr;

// Initialization routine - zeroes all the interrupt service routines, initializes GDT and IDT
void init_descriptor_tables() {
// Initialize global descriptor table
init_gdt();

// Initialize interrupt descriptor table
init_idt();
}

static void init_gdt() {
gdt_ptr.limit = (sizeof(gdt_entry_t) * 5) - 1;
gdt_ptr.base = (u32int)&gdt_entries;

gdt_set_gate(0, 0, 0, 0, 0); // Null segment
gdt_set_gate(1, 0, 0xFFFFFFFF, 0x9A, 0xCF); // Code segment
gdt_set_gate(2, 0, 0xFFFFFFFF, 0x92, 0xCF); // Data segment
gdt_set_gate(3, 0, 0xFFFFFFFF, 0xFA, 0xCF); // User mode code segment
gdt_set_gate(4, 0, 0xFFFFFFFF, 0xF2, 0xCF); // User mode data segment

gdt_flush((u32int)&gdt_ptr);
}

// Sets the value of one GDT entry
static void gdt_set_gate(s32int num, u32int base, u32int limit, u8int access, u8int gran) {
gdt_entries[num].base_low = (base & 0xFFFF);
gdt_entries[num].base_middle = (base >> 16) & 0xFF;
gdt_entries[num].base_high = (base >> 24) & 0xFF;

gdt_entries[num].limit_low = (limit & 0xFFFF);
gdt_entries[num].granularity = (limit >> 16) & 0x0F;

gdt_entries[num].granularity |= gran & 0xF0;
gdt_entries[num].access = access;
}

static void init_idt() {
idt_ptr.limit = sizeof(idt_entry_t) * 256 - 1;
idt_ptr.base = (u32int)&idt_entries;

memset(&idt_entries, 0, sizeof(idt_entry_t) * 256);

idt_set_gate( 0, (u32int)isr0 , 0x08, 0x8E);
idt_set_gate( 1, (u32int)isr1 , 0x08, 0x8E);
idt_set_gate( 2, (u32int)isr2 , 0x08, 0x8E);
idt_set_gate( 3, (u32int)isr3 , 0x08, 0x8E);
idt_set_gate( 4, (u32int)isr4 , 0x08, 0x8E);
idt_set_gate( 5, (u32int)isr5 , 0x08, 0x8E);
idt_set_gate( 6, (u32int)isr6 , 0x08, 0x8E);
idt_set_gate( 7, (u32int)isr7 , 0x08, 0x8E);
idt_set_gate( 8, (u32int)isr8 , 0x08, 0x8E);
idt_set_gate( 9, (u32int)isr9 , 0x08, 0x8E);
idt_set_gate(10, (u32int)isr10, 0x08, 0x8E);
idt_set_gate(11, (u32int)isr11, 0x08, 0x8E);
idt_set_gate(12, (u32int)isr12, 0x08, 0x8E);
idt_set_gate(13, (u32int)isr13, 0x08, 0x8E);
idt_set_gate(14, (u32int)isr14, 0x08, 0x8E);
idt_set_gate(15, (u32int)isr15, 0x08, 0x8E);
idt_set_gate(16, (u32int)isr16, 0x08, 0x8E);
idt_set_gate(17, (u32int)isr17, 0x08, 0x8E);
idt_set_gate(18, (u32int)isr18, 0x08, 0x8E);
idt_set_gate(19, (u32int)isr19, 0x08, 0x8E);
idt_set_gate(20, (u32int)isr20, 0x08, 0x8E);
idt_set_gate(21, (u32int)isr21, 0x08, 0x8E);
idt_set_gate(22, (u32int)isr22, 0x08, 0x8E);
idt_set_gate(23, (u32int)isr23, 0x08, 0x8E);
idt_set_gate(24, (u32int)isr24, 0x08, 0x8E);
idt_set_gate(25, (u32int)isr25, 0x08, 0x8E);
idt_set_gate(26, (u32int)isr26, 0x08, 0x8E);
idt_set_gate(27, (u32int)isr27, 0x08, 0x8E);
idt_set_gate(28, (u32int)isr28, 0x08, 0x8E);
idt_set_gate(29, (u32int)isr29, 0x08, 0x8E);
idt_set_gate(30, (u32int)isr30, 0x08, 0x8E);
idt_set_gate(31, (u32int)isr31, 0x08, 0x8E);

idt_flush((u32int)&idt_ptr);
}

static void idt_set_gate(u8int num, u32int base, u16int sel, u8int flags) {
idt_entries[num].base_lo = base & 0xFFFF;
idt_entries[num].base_hi = (base >> 16) & 0xFFFF;

idt_entries[num].sel = sel;
idt_entries[num].always0 = 0;

// Uncomment the OR below when user-mode is implemented, it sets the interrupt gate's privilege level to 3
idt_entries[num].flags = flags /* | 0x60 */;
}
82 changes: 82 additions & 0 deletions src/descriptor_tables.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
// Defines the interface and structures for initialising the GDT and IDT

#include "common.h"

// Value of one GDT entry
// Aattribute 'packed' for GCC not to change any of the alignment in the structure
struct gdt_entry_struct {
u16int limit_low; // Lower 16 bits of the limit
u16int base_low; // Lower 16 bits of the base
u8int base_middle; // Next 8 bits of the base
u8int access; // Access flags, determine what ring this segment can be used in
u8int granularity;
u8int base_high; // Last 8 bits of the base
} __attribute__((packed));

typedef struct gdt_entry_struct gdt_entry_t;

// GDT pointer pointing to the start of array of GDT entries
// In format required by 'lgdt' instruction
struct gdt_ptr_struct {
u16int limit; // Upper 16 bits of all selector limits
u32int base; // Address of the first gdt_entry_t struct
} __attribute__((packed));

typedef struct gdt_ptr_struct gdt_ptr_t;

// Interrupt gate
struct idt_entry_struct {
u16int base_lo; // Lower 16 bits of the address to jump to when this interrupt fires
u16int sel; // Kernel segment selector
u8int always0; // Must always be zero
u8int flags;
u16int base_hi; // Upper 16 bits of the address to jump to
} __attribute__((packed));

typedef struct idt_entry_struct idt_entry_t;

// Pointer to an array of interrupt handlers
// In format suitable for 'lidt'
struct idt_ptr_struct {
u16int limit;
u32int base; // Address of the first element in 'idt_entry_t' array
} __attribute__((packed));

typedef struct idt_ptr_struct idt_ptr_t;

// Extern directives for accessing the addresses of ASM ISR handlers
extern void isr0();
extern void isr1();
extern void isr2();
extern void isr3();
extern void isr4();
extern void isr5();
extern void isr6();
extern void isr7();
extern void isr8();
extern void isr9();
extern void isr10();
extern void isr11();
extern void isr12();
extern void isr13();
extern void isr14();
extern void isr15();
extern void isr16();
extern void isr17();
extern void isr18();
extern void isr19();
extern void isr20();
extern void isr21();
extern void isr22();
extern void isr23();
extern void isr24();
extern void isr25();
extern void isr26();
extern void isr27();
extern void isr28();
extern void isr29();
extern void isr30();
extern void isr31();

// Initialization function
void init_descriptor_tables();
25 changes: 25 additions & 0 deletions src/gdt.asm
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
; Sets up global descriptor table and interrupt descriptor table.

[GLOBAL gdt_flush] ; Make 'gdt_flush' accessible from C

gdt_flush:
mov eax, [esp+4] ; Get pointer to GDT, passed as a parameter
lgdt [eax] ; Load new GDT pointer

mov ax, 0x10 ; 0x10 is offset in GDT to our data segment
mov ds, ax ; Load all data segment selectors
mov es, ax
mov fs, ax
mov gs, ax
mov ss, ax
jmp 0x08:.flush ; 0x08 is offset to our code segment
.flush:
ret


[GLOBAL idt_flush] ; Make 'idt_flush' accessible from C

idt_flush:
mov eax, [esp+4] ; Get pointer to the IDT, passed as a parameter
lidt [eax] ; Load IDT pointer
ret
82 changes: 82 additions & 0 deletions src/interrupt.asm
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
; Contains interrupt service routine wrappers.

; Macro creating a stub for an ISR which does NOT pass it's own error code (adds a dummy errcode byte)
%macro ISR_NOERRCODE 1
[GLOBAL isr%1]
isr%1:
cli ; Disable interrupts firstly
push byte 0 ; Push a dummy error code
push byte %1 ; Push the interrupt number
jmp isr_common_stub ; Go to common handler code
%endmacro

; Macro creating a stub for an ISR which passes it's own error code.
%macro ISR_ERRCODE 1
[GLOBAL isr%1]
isr%1:
cli ; Disable interrupts
push byte %1 ; Push the interrupt number
jmp isr_common_stub
%endmacro

ISR_NOERRCODE 0
ISR_NOERRCODE 1
ISR_NOERRCODE 2
ISR_NOERRCODE 3
ISR_NOERRCODE 4
ISR_NOERRCODE 5
ISR_NOERRCODE 6
ISR_NOERRCODE 7
ISR_ERRCODE 8
ISR_NOERRCODE 9
ISR_ERRCODE 10
ISR_ERRCODE 11
ISR_ERRCODE 12
ISR_ERRCODE 13
ISR_ERRCODE 14
ISR_NOERRCODE 15
ISR_NOERRCODE 16
ISR_ERRCODE 17
ISR_NOERRCODE 18
ISR_NOERRCODE 19
ISR_NOERRCODE 20
ISR_NOERRCODE 21
ISR_NOERRCODE 22
ISR_NOERRCODE 23
ISR_NOERRCODE 24
ISR_NOERRCODE 25
ISR_NOERRCODE 26
ISR_NOERRCODE 27
ISR_NOERRCODE 28
ISR_NOERRCODE 29
ISR_ERRCODE 30
ISR_NOERRCODE 31

[EXTERN isr_handler] ; In isr.c

; Common ISR stub, saves the processor state, sets up for kernel mode segments,
; calls the C-level fault handler, and finally restores the stack frame
isr_common_stub:
pusha ; Push registers

mov ax, ds ; Lower 16-bits of eax = ds
push eax ; Save the data segment descriptor

mov ax, 0x10 ; load the kernel data segment descriptor
mov ds, ax
mov es, ax
mov fs, ax
mov gs, ax

call isr_handler

pop ebx ; Reload the original data segment descriptor
mov ds, bx
mov es, bx
mov fs, bx
mov gs, bx

popa ; Pop registers
add esp, 8 ; Clean up the pushed error code and pushed ISR number
sti
iret ; Pop CS, EIP, EFLAGS, SS, and ESP
9 changes: 9 additions & 0 deletions src/isr.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
#include "isr.h"
#include "monitor.h"

// Called from ASM interrupt handler stub
void isr_handler(registers_t regs) {
monitor_write("recieved interrupt: ");
monitor_write_dec(regs.int_no);
monitor_put('\n');
}
19 changes: 19 additions & 0 deletions src/isr.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
// Interface and structures for high level interrupt service routines

#include "common.h"

typedef struct registers {
// Data segment selector
u32int ds;

// Pushed by pusha
// 'useless_value' instead of 'esp', because it has to do with the current stack context, not what was interrupted (proper 'esp' below)
u32int edi, esi, ebp, useless_value, ebx, edx, ecx, eax;

// Interrupt number and error code (if applicable)
u32int int_no, err_code;

// Pushed by the processor automatically
// Proper 'esp' here
u32int eip, cs, eflags, esp, ss;
} registers_t;
7 changes: 5 additions & 2 deletions src/main.c
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,14 @@

struct multiboot;

int main(struct multiboot *mboot_ptr) {
int kernel_main(struct multiboot *mboot_ptr) {
monitor_clear();
monitor_write("Welcome to Tyr (");
monitor_write_hex(0x547972);
monitor_write("), the one-handed OS!");
monitor_write("), the one-handed OS!\n");

asm volatile("int $0x3");
//asm volatile("int $0x4");

return 0;
}

0 comments on commit e8243ae

Please sign in to comment.