From fb0358197b7347a723061990b8517f3d97e34a99 Mon Sep 17 00:00:00 2001 From: Sami Samhuri Date: Tue, 10 Jan 2012 08:47:07 -0800 Subject: [PATCH] support descriptor tables, interrupts, timers, allocation, paging --- Vagrantfile | 40 +--- kernel/floppy.img | Bin 1474560 -> 1474560 bytes kernel/go | 11 + kernel/src/Makefile | 3 +- kernel/src/common.c | 110 ++++++++- kernel/src/common.h | 19 +- kernel/src/descriptor_tables.c | 155 +++++++++++++ kernel/src/descriptor_tables.h | 109 +++++++++ kernel/src/gdt.h | 0 kernel/src/gdt.s | 29 +++ kernel/src/interrupt.s | 149 ++++++++++++ kernel/src/isr.c | 51 +++++ kernel/src/isr.h | 39 ++++ kernel/src/kernel | Bin 13652 -> 24921 bytes kernel/src/kheap.c | 407 +++++++++++++++++++++++++++++++++ kernel/src/kheap.h | 100 ++++++++ kernel/src/main.c | 53 ++++- kernel/src/monitor.c | 90 ++++++-- kernel/src/monitor.h | 2 +- kernel/src/ordered_array.c | 76 ++++++ kernel/src/ordered_array.h | 59 +++++ kernel/src/paging.c | 225 ++++++++++++++++++ kernel/src/paging.h | 70 ++++++ kernel/src/timer.c | 38 +++ kernel/src/timer.h | 11 + 25 files changed, 1758 insertions(+), 88 deletions(-) create mode 100755 kernel/go create mode 100644 kernel/src/descriptor_tables.c create mode 100644 kernel/src/descriptor_tables.h delete mode 100644 kernel/src/gdt.h create mode 100644 kernel/src/gdt.s create mode 100644 kernel/src/interrupt.s create mode 100644 kernel/src/isr.c create mode 100644 kernel/src/isr.h create mode 100644 kernel/src/kheap.c create mode 100644 kernel/src/kheap.h create mode 100644 kernel/src/ordered_array.c create mode 100644 kernel/src/ordered_array.h create mode 100644 kernel/src/paging.c create mode 100644 kernel/src/paging.h create mode 100644 kernel/src/timer.c create mode 100644 kernel/src/timer.h diff --git a/Vagrantfile b/Vagrantfile index 69b9471..e15c6a2 100644 --- a/Vagrantfile +++ b/Vagrantfile @@ -11,11 +11,11 @@ Vagrant::Config.run do |config| # config.vm.box_url = "http://domain.com/path/to/above.box" # Boot with a GUI so you can see the screen. (Default is headless) - # config.vm.boot_mode = :gui + config.vm.boot_mode = :gui # Assign this VM to a host only network IP, allowing you to access it # via the IP. - # config.vm.network "33.33.33.10" + config.vm.network "33.33.33.10" # Forward a port from the guest to the host, which allows for outside # computers to access the VM, whereas host only networking does not. @@ -24,40 +24,8 @@ Vagrant::Config.run do |config| # Share an additional folder to the guest VM. The first argument is # an identifier, the second is the path on the guest to mount the # folder, and the third is the path on the host to the actual folder. - # config.vm.share_folder "v-data", "/vagrant_data", "../data" + config.vm.share_folder "v-kernel", "/kernel", ".", :nfs => true - # Enable provisioning with chef solo, specifying a cookbooks path (relative - # to this Vagrantfile), and adding some recipes and/or roles. - # - # config.vm.provision :chef_solo do |chef| - # chef.cookbooks_path = "cookbooks" - # chef.add_recipe "mysql" - # chef.add_role "web" - # - # # You may also specify custom JSON attributes: - # chef.json = { :mysql_password => "foo" } - # end + config.ssh.forward_x11 = true - # Enable provisioning with chef server, specifying the chef server URL, - # and the path to the validation key (relative to this Vagrantfile). - # - # The Opscode Platform uses HTTPS. Substitute your organization for - # ORGNAME in the URL and validation key. - # - # If you have your own Chef Server, use the appropriate URL, which may be - # HTTP instead of HTTPS depending on your configuration. Also change the - # validation key to validation.pem. - # - # config.vm.provision :chef_server do |chef| - # chef.chef_server_url = "https://api.opscode.com/organizations/ORGNAME" - # chef.validation_key_path = "ORGNAME-validator.pem" - # end - # - # If you're using the Opscode platform, your validator client is - # ORGNAME-validator, replacing ORGNAME with your organization name. - # - # IF you have your own Chef Server, the default validation client name is - # chef-validator, unless you changed the configuration. - # - # chef.validation_client_name = "ORGNAME-validator" end diff --git a/kernel/floppy.img b/kernel/floppy.img index 68d3d640054985abc1bd9c653c76564eadfa0fbf..68faeeb6c4a6de000fa6444f11d2a3553d9eefc8 100644 GIT binary patch delta 31636 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 R?NFARQkI)~e{RR_{}-W%18x8S delta 35724 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 TpWkWy{PgwnJ3leMOUC~Ja^!v9 diff --git a/kernel/go b/kernel/go new file mode 100755 index 0000000..3ce37dc --- /dev/null +++ b/kernel/go @@ -0,0 +1,11 @@ +#!/bin/sh + +cd src +make +success=$? +cd .. +if [ $success -eq 0 ]; then + sudo ./update_image.sh + sudo ./run_bochs.sh + grep -F 'CPU0' bochs.out +fi diff --git a/kernel/src/Makefile b/kernel/src/Makefile index 3013e19..94b871a 100644 --- a/kernel/src/Makefile +++ b/kernel/src/Makefile @@ -1,4 +1,5 @@ -SOURCES=boot.o main.o common.o monitor.o +SOURCES=boot.o main.o common.o monitor.o descriptor_tables.o isr.o interrupt.o \ + gdt.o timer.o kheap.o paging.o ordered_array.o CFLAGS=-nostdlib -nostdinc -fno-builtin -fno-stack-protector LDFLAGS=-T link.ld diff --git a/kernel/src/common.c b/kernel/src/common.c index 7ea5246..b51328c 100644 --- a/kernel/src/common.c +++ b/kernel/src/common.c @@ -2,35 +2,121 @@ // From JamesM's kernel development tutorials. #include "common.h" +#include "monitor.h" // Write a byte out to the specified port. void outb(u16int port, u8int value) { - asm volatile ("outb %1, %0" : : "dN" (port), "a" (value)); - } - u8int inb(u16int port) { - u8int ret; - asm volatile("inb %1, %0" : "=a" (ret) : "dN" (port)); - return ret; - } - u16int inw(u16int port) { - u16int ret; - asm volatile ("inw %1, %0" : "=a" (ret) : "dN" (port)); - return ret; - +} + +// Copy len bytes from src to dest. +void memcpy(u8int *dest, const u8int *src, u32int len) +{ + const u8int *sp = (const u8int *)src; + u8int *dp = (u8int *)dest; + for(; len != 0; len--) *dp++ = *sp++; +} + +// Write len copies of val into dest. +void memset(u8int *dest, u8int val, u32int len) +{ + u8int *temp = (u8int *)dest; + for ( ; len != 0; len--) *temp++ = val; +} + +// Compare two strings. Should return -1 if +// str1 < str2, 0 if they are equal or 1 otherwise. +int strcmp(char *str1, char *str2) +{ + int i = 0; + int failed = 0; + while(str1[i] != '\0' && str2[i] != '\0') + { + if(str1[i] != str2[i]) + { + failed = 1; + break; + } + i++; + } + // why did the loop exit? + if( (str1[i] == '\0' && str2[i] != '\0') || (str1[i] != '\0' && str2[i] == '\0') ) + failed = 1; + + return failed; +} + +// Copy the NULL-terminated string src into dest, and +// return dest. +char *strcpy(char *dest, const char *src) +{ + do + { + *dest++ = *src++; + } + while (*src != 0); +} + +// Concatenate the NULL-terminated string src onto +// the end of dest, and return dest. +char *strcat(char *dest, const char *src) +{ + while (*dest != 0) + { + *dest = *dest++; + } + + do + { + *dest++ = *src++; + } + while (*src != 0); + return dest; +} + +extern void panic(const char *message, const char *file, u32int line) +{ + // We encountered a massive problem and have to stop. + asm volatile("cli"); // Disable interrupts. + + monitor_write("PANIC("); + monitor_write(message); + monitor_write(") at "); + monitor_write(file); + monitor_write(":"); + monitor_write_dec(line); + monitor_write("\n"); + // Halt by going into an infinite loop. + for(;;); +} + +extern void panic_assert(const char *file, u32int line, const char *desc) +{ + // An assertion failed, and we have to panic. + asm volatile("cli"); // Disable interrupts. + + monitor_write("ASSERTION-FAILED("); + monitor_write(desc); + monitor_write(") at "); + monitor_write(file); + monitor_write(":"); + monitor_write_dec(line); + monitor_write("\n"); + // Halt by going into an infinite loop. + for(;;); } diff --git a/kernel/src/common.h b/kernel/src/common.h index 250b9c6..2d907b3 100644 --- a/kernel/src/common.h +++ b/kernel/src/common.h @@ -7,23 +7,26 @@ // Some nice typedefs, to standardise sizes across platforms. // These typedefs are written for 32-bit X86. typedef unsigned int u32int; - typedef int s32int; - typedef unsigned short u16int; - typedef short s16int; - typedef unsigned char u8int; - typedef char s8int; - void outb(u16int port, u8int value); - u8int inb(u16int port); - u16int inw(u16int port); +void memcpy(u8int *dest, const u8int *src, u32int len); +void memset(u8int *dest, u8int val, u32int len); +int strcmp(char *str1, char *str2); +char *strcpy(char *dest, const char *src); +char *strcat(char *dest, const char *src); + +#define PANIC(msg) panic(msg, __FILE__, __LINE__); +#define ASSERT(b) ((b) ? (void)0 : panic_assert(__FILE__, __LINE__, #b)) + +extern void panic(const char *message, const char *file, u32int line); +extern void panic_assert(const char *file, u32int line, const char *desc); #endif diff --git a/kernel/src/descriptor_tables.c b/kernel/src/descriptor_tables.c new file mode 100644 index 0000000..030d64b --- /dev/null +++ b/kernel/src/descriptor_tables.c @@ -0,0 +1,155 @@ +// +// descriptor_tables.c - Initialises the GDT and IDT, and defines the +// default ISR and IRQ handler. +// Based on code from Bran's kernel development tutorials. +// Rewritten for JamesM's kernel development tutorials. +// + +#include "common.h" +#include "descriptor_tables.h" +#include "isr.h" + +// Lets us access our ASM functions from our C code. +extern void gdt_flush(u32int); +extern void idt_flush(u32int); + +// Internal function prototypes. +static void init_gdt(); +static void gdt_set_gate(s32int,u32int,u32int,u8int,u8int); + +static void init_idt(); +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; + +// Extern the ISR handler array so we can nullify them on startup> +extern isr_t interrupt_handlers[]; + + +// Initialisation routine - zeroes all the interrupt service routines, +// initialises the GDT and IDT. +void init_descriptor_tables() +{ + init_gdt(); + init_idt(); + + // Zero out interrupt handlers. + memset(&interrupt_handlers, 0, 256 * sizeof(isr_t)); +} + +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); +} + +// Set 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, 256 * sizeof(idt_entry_t)); + + // Remap the IRQ table + outb(0x20, 0x11); + outb(0xA0, 0x11); + outb(0x21, 0x20); + outb(0xA1, 0x28); + outb(0x21, 0x04); + outb(0xA1, 0x02); + outb(0x21, 0x01); + outb(0xA1, 0x01); + outb(0x21, 0x0); + outb(0xA1, 0x0); + + + 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_set_gate(32, (u32int)irq0, 0x08, 0x8E); + idt_set_gate(33, (u32int)irq1, 0x08, 0x8E); + idt_set_gate(34, (u32int)irq2, 0x08, 0x8E); + idt_set_gate(35, (u32int)irq3, 0x08, 0x8E); + idt_set_gate(36, (u32int)irq4, 0x08, 0x8E); + idt_set_gate(37, (u32int)irq5, 0x08, 0x8E); + idt_set_gate(38, (u32int)irq6, 0x08, 0x8E); + idt_set_gate(39, (u32int)irq7, 0x08, 0x8E); + idt_set_gate(40, (u32int)irq8, 0x08, 0x8E); + idt_set_gate(41, (u32int)irq9, 0x08, 0x8E); + idt_set_gate(42, (u32int)irq10, 0x08, 0x8E); + idt_set_gate(43, (u32int)irq11, 0x08, 0x8E); + idt_set_gate(44, (u32int)irq12, 0x08, 0x8E); + idt_set_gate(45, (u32int)irq13, 0x08, 0x8E); + idt_set_gate(46, (u32int)irq14, 0x08, 0x8E); + idt_set_gate(47, (u32int)irq15, 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; + // We must uncomment the OR below when we get to using user-mode. + // It sets the interrupt gate's privilege level to 3. + idt_entries[num].flags = flags /* | 0x60 */; +} diff --git a/kernel/src/descriptor_tables.h b/kernel/src/descriptor_tables.h new file mode 100644 index 0000000..bdcd5fc --- /dev/null +++ b/kernel/src/descriptor_tables.h @@ -0,0 +1,109 @@ +// +// descriptor_tables.h - Defines the interface for initialising the GDT and IDT. +// Also defines needed structures. +// Based on code from Bran's kernel development tutorials. +// Rewritten for JamesM's kernel development tutorials. +// + +#include "common.h" + +// Initialisation function is publicly accessible. +void init_descriptor_tables(); + +// This structure contains the value of one GDT entry. +// We use the attribute 'packed' to tell GCC not to change +// any of the alignment in the structure. +struct gdt_entry_struct +{ + u16int limit_low; // The lower 16 bits of the limit. + u16int base_low; // The lower 16 bits of the base. + u8int base_middle; // The next 8 bits of the base. + u8int access; // Access flags, determine what ring this segment can be used in. + u8int granularity; + u8int base_high; // The last 8 bits of the base. +} __attribute__((packed)); + +typedef struct gdt_entry_struct gdt_entry_t; + +// This struct describes a GDT pointer. It points to the start of +// our array of GDT entries, and is in the format required by the +// lgdt instruction. +struct gdt_ptr_struct +{ + u16int limit; // The upper 16 bits of all selector limits. + u32int base; // The address of the first gdt_entry_t struct. +} + __attribute__((packed)); +typedef struct gdt_ptr_struct gdt_ptr_t; + +// A struct describing an interrupt gate. +struct idt_entry_struct +{ + u16int base_lo; // The lower 16 bits of the address to jump to when this interrupt fires. + u16int sel; // Kernel segment selector. + u8int always0; // This must always be zero. + u8int flags; // More flags. See documentation. + u16int base_hi; // The upper 16 bits of the address to jump to. +} __attribute__((packed)); +typedef struct idt_entry_struct idt_entry_t; + +// A struct describing a pointer to an array of interrupt handlers. +// This is in a format suitable for giving to 'lidt'. +struct idt_ptr_struct +{ + u16int limit; + u32int base; // The address of the first element in our idt_entry_t array. +} __attribute__((packed)); +typedef struct idt_ptr_struct idt_ptr_t; + +// These extern directives let us access the addresses of our 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(); + +// IRQs +extern void irq0 (); +extern void irq1 (); +extern void irq2 (); +extern void irq3 (); +extern void irq4 (); +extern void irq5 (); +extern void irq6 (); +extern void irq7 (); +extern void irq8 (); +extern void irq9 (); +extern void irq10(); +extern void irq11(); +extern void irq12(); +extern void irq13(); +extern void irq14(); +extern void irq15(); diff --git a/kernel/src/gdt.h b/kernel/src/gdt.h deleted file mode 100644 index e69de29..0000000 diff --git a/kernel/src/gdt.s b/kernel/src/gdt.s new file mode 100644 index 0000000..785fea0 --- /dev/null +++ b/kernel/src/gdt.s @@ -0,0 +1,29 @@ +; +; Gdt.s -- contains global descriptor table and interrupt descriptor table +; setup code. +; Based on code from Bran's kernel development tutorials. +; Rewritten for JamesM's kernel development tutorials. + +[GLOBAL gdt_flush] ; Allows the C code to call gdt_flush(). + +gdt_flush: + mov eax, [esp+4] ; Get the pointer to the GDT, passed as a parameter. + lgdt [eax] ; Load the new GDT pointer + + mov ax, 0x10 ; 0x10 is the offset in the 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 the offset to our code segment: Far jump! +.flush: + ret + + +[GLOBAL idt_flush] ; Allows the C code to call idt_flush(). + +idt_flush: + mov eax, [esp+4] ; Get the pointer to the IDT, passed as a parameter. + lidt [eax] ; Load the IDT pointer. + ret diff --git a/kernel/src/interrupt.s b/kernel/src/interrupt.s new file mode 100644 index 0000000..3540dce --- /dev/null +++ b/kernel/src/interrupt.s @@ -0,0 +1,149 @@ +; +; interrupt.s -- Contains interrupt service routine wrappers. +; Based on Bran's kernel development tutorials. +; Rewritten for JamesM's kernel development tutorials. + +; This macro creates a stub for an ISR which does NOT pass it's own +; error code (adds a dummy errcode byte). +%macro ISR_NOERRCODE 1 ; define a macro, taking one parameter + [GLOBAL isr%1] ; %1 accesses the first parameter. + isr%1: + cli ; Disable interrupts + push byte 0 ; Push a dummy error code (if ISR0 doesn't push its own error code) + push byte %1 ; Push the interrupt number (%1) + jmp isr_common_stub ; Go to our common handler. +%endmacro + +%macro ISR_ERRCODE 1 + [GLOBAL isr%1] + isr%1: + cli ; Disable interrupts + push byte %1 ; Push the interrupt number (%1) + jmp isr_common_stub ; Go to our common handler. +%endmacro + +; This macro creates a stub for an IRQ - the first parameter is +; the IRQ number, the second is the ISR number it is remapped to. +%macro IRQ 2 + global irq%1 + irq%1: + cli + push byte 0 + push byte %2 + jmp irq_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_NOERRCODE 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_NOERRCODE 30 +ISR_NOERRCODE 31 + +IRQ 0, 32 +IRQ 1, 33 +IRQ 2, 34 +IRQ 3, 35 +IRQ 4, 36 +IRQ 5, 37 +IRQ 6, 38 +IRQ 7, 39 +IRQ 8, 40 +IRQ 9, 41 +IRQ 10, 42 +IRQ 11, 43 +IRQ 12, 44 +IRQ 13, 45 +IRQ 14, 46 +IRQ 15, 47 + + +; In isr.c +[EXTERN isr_handler] + +; This is our common ISR stub. It 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 ; Pushes edi,esi,ebp,esp,ebx,edx,ecx,eax + + 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 eax ; reload the original data segment descriptor + mov ds, ax + mov es, ax + mov fs, ax + mov gs, ax + + popa ; Pops edi,esi,ebp... + add esp, 8 ; Cleans up the pushed error code and pushed ISR number + sti + iret ; pops 5 things at once: CS, EIP, EFLAGS, SS, and ESP + + +; +; In isr.c +extern irq_handler + +; This is our common IRQ stub. It saves the processor state, sets +; up for kernel mode segments, calls the C-level fault handler, +; and finally restores the stack frame. +irq_common_stub: + pusha ; Pushes edi,esi,ebp,esp,ebx,edx,ecx,eax + + 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 irq_handler + + pop ebx ; reload the original data segment descriptor + mov ds, bx + mov es, bx + mov fs, bx + mov gs, bx + + popa ; Pops edi,esi,ebp... + add esp, 8 ; Cleans up the pushed error code and pushed ISR number + sti + iret ; pops 5 things at once: CS, EIP, EFLAGS, SS, and ESP diff --git a/kernel/src/isr.c b/kernel/src/isr.c new file mode 100644 index 0000000..ba13991 --- /dev/null +++ b/kernel/src/isr.c @@ -0,0 +1,51 @@ +// +// isr.c -- High level interrupt service routines and interrupt request handlers. +// Part of this code is modified from Bran's kernel development tutorials. +// Rewritten for JamesM's kernel development tutorials. +// + +#include "common.h" +#include "isr.h" +#include "monitor.h" + +isr_t interrupt_handlers[256]; + +void register_interrupt_handler(u8int n, isr_t handler) +{ + interrupt_handlers[n] = handler; +} + +// This gets called from our ASM interrupt handler stub. +void isr_handler(registers_t regs) +{ + monitor_write("recieved interrupt: "); + monitor_write_dec(regs.int_no); + monitor_put('\n'); + + if (interrupt_handlers[regs.int_no] != 0) + { + isr_t handler = interrupt_handlers[regs.int_no]; + handler(regs); + } +} + +// This gets called from our ASM interrupt handler stub. +void irq_handler(registers_t regs) +{ + // Send an EOI (end of interrupt) signal to the PICs. + // If this interrupt involved the slave. + if (regs.int_no >= 40) + { + // Send reset signal to slave. + outb(0xA0, 0x20); + } + // Send reset signal to master. (As well as slave, if necessary). + outb(0x20, 0x20); + + if (interrupt_handlers[regs.int_no] != 0) + { + isr_t handler = interrupt_handlers[regs.int_no]; + handler(regs); + } + +} diff --git a/kernel/src/isr.h b/kernel/src/isr.h new file mode 100644 index 0000000..51c6044 --- /dev/null +++ b/kernel/src/isr.h @@ -0,0 +1,39 @@ +// +// isr.h -- Interface and structures for high level interrupt service routines. +// Part of this code is modified from Bran's kernel development tutorials. +// Rewritten for JamesM's kernel development tutorials. +// + +#include "common.h" + +// A few defines to make life a little easier +#define IRQ0 32 +#define IRQ1 33 +#define IRQ2 34 +#define IRQ3 35 +#define IRQ4 36 +#define IRQ5 37 +#define IRQ6 38 +#define IRQ7 39 +#define IRQ8 40 +#define IRQ9 41 +#define IRQ10 42 +#define IRQ11 43 +#define IRQ12 44 +#define IRQ13 45 +#define IRQ14 46 +#define IRQ15 47 + +typedef struct registers +{ + u32int ds; // Data segment selector + u32int edi, esi, ebp, esp, ebx, edx, ecx, eax; // Pushed by pusha. + u32int int_no, err_code; // Interrupt number and error code (if applicable) + u32int eip, cs, eflags, useresp, ss; // Pushed by the processor automatically. +} registers_t; + +// Enables registration of callbacks for interrupts or IRQs. +// For IRQs, to ease confusion, use the #defines above as the +// first parameter. +typedef void (*isr_t)(registers_t); +void register_interrupt_handler(u8int n, isr_t handler); diff --git a/kernel/src/kernel b/kernel/src/kernel index 6e0c68e18c6909dc1d4f74f2bfd4f197eefce979..76e10133e1a3f7bf406fa9b1d18b75d810ba80a6 100755 GIT binary patch literal 24921 zcmeHPdvsLQx!;paV8GECFh~%T0fGb}7+%Ut8x|bJR%#T|ViCt7nLtWjPG)$xnhG7T z8IMqFv9-4MUJ!5VRl1~>()hTTAci2c2E|*d)<#V08D^{lW=X`9+~04XeI}E@bakzJ z|G0NGXTj|Kefzh+{q1jmd!PL{I}d2L-E6bjg!N>IOhL*c++qg8WkqJn3~(|=(j+>1^O+}Z-IUb^jo0c0{s^FA6p<}&o4%1LdqAn-`SpA&lkEy{oQW42e9Ov z!DW5yJbsEJe-5`N!tny*9)}RdOGr3m&qY)iD{_R0EX>U@p2_WDE;5;;EYK{I_JGdC zwt#UA0(4}G*mDgRHV44ZBmV`HKZ^XbW+79^-)ZtagT&cPTxiCxWyUL@j8SefZfD{IGyci`U%YSl5jSqb&(Yu7 z4=v2iLxvqXLY-wzZXo9HtI}3+hhFT^HD_6*xT{|4bp58d%e7DI{OjRYX~}rDt~tsg zTHN&;Egm@#uh+Vf+I`W)vX22g58!!~?1tEg)(Hs_tt&QMhxC2mb=AAWT9+-Xb=ty} zZdv`$n^9MZZN#Kxh7OwAz+LB#3WCuSW8 z4ni_Fdtz6!L<=G|HwG`21Yw&SL$9W!gnFf1nUWHOJ(OtV8K^``{pUGgqkxeZnmyr2KFm+90-$c}s+$`C$z?V$6l>D4z%L2cXBKdK! z(IchkJlDP~8#=6OUu&ebsV&2XAA95gxI%Ipx7st{JfFc;?U%7d?YYMx%2@ul?ru2L zXJt(WtkwwMwIk_mCKV!ya90X^vV!-44g=eAnP%;C7d-g$rZz{o#e*`>Db?oXtSz@3 z>6)F~5!8#jY;SJOcALcFu5eS4?Xa!I^&3$Z$)E>ss_S|H+kxLchI|_liRJXl2gT3z zAm1SJPtDQ{IZB&6G+({e-mHBhnzaP=`UG}?$0x!kU4vTdwbNjohN{r>%*MKQM`*M% z8N4@JMZ<>eNm?@HjJr}w<1k% z#UdxdZ7z(T;kvj84cJ)ssR)e(7~L!G+PCpC8h3vq%Zov=nC+2pWrsan*C95%7(ST0 zT=jVjnjAvIbxsi)-XI6F5n#bVUC$k0LZuVKsMGeqDQhGO$7jK-z;uYvWze{Q8rQRk z`wwXe?qSE)=vqQ`C|RFJySuw1T07*vh-7g#)E;Si@wZ2h9*wk|J=t_BQ*~ThW90RT z2O`BC)RHy&MAqmN)TjeBviH09x#bS^a#;QKpa~H>A z*cHayKG-uC$5L;DV{-#5OB?(gRa1Qko{RTrPQ9FQNJqx>Bm1?nu!l)CopRS_zm}Uq z=YXDI(Qcw?J&AS$jqwtIYb+LC7TIr;a_w8(8U7$K==Gjd46j|F#v|40(r6L^vU^~| zy$E(?;77(}Q)23v+LXm-OdH8WWh`cn5RL zhWpclsEqMzf;OiINf&#Bpl#_v(zn(S^muxZbgG*PdNMr-lU`wrA!v7c5R!$_1!%** z^dJtC#_tJgOb?>TjOPhzNe>!C&{l%pOb?Qy@M?nINe{Y&k`@qjGCfExEUqT#RC>^* zl=L}F+HfX4h?6nnI6>#qgV@cD=LoV7OY2V@;Eaa|x-312oyzbNl#?Dbl%PU_#-;~d zPS8k#CZ`8w6Z9Eeui^UipkV|ZBd9Pv=n8^%6ZGZuAguI+@m+$JrUzm9DU6i_-IE?P zf}k4-TAdzrB|$j^1=52?67(s$Xv4SCgE*}*en-%Q=|NW!^dAI0k{)z5L7NGBB0Xp{ zL0*D(rU!WlnnTdD=|N)%8b;8s(}Ttml)zBd@Jf2nID(E4)SMoaOVG~<`fYm9c!D+& zbUZ!i8iMX7=-u?72?X6h(8=^5zH5x31Vz(>CKB{9ei{<#LDv%0LeSatph*PnBIta2 z&}4$XO;FYqX$Opag6<;7nI1HSAbtYUaCv&rbp*+|2;cIYi_F5npkDq?eKDpZm|Fx9F;=g#8e9bsyGS(CSD!{Il7jMV3e-qA@H!wg}KOHfB?(V z83-op+AIVGx;78N3|%WkFiY1KBAAESes`;-J#vRTC#=opJjnyap!N;~#!o*s=S7Og zz^DhWNAdqdRe^HWbMBksnV%_^1?eObjuM4wl&E2B@V*vRO=s@SOLb``rS z`jCoU9o?v6qhlLbyeA17lLU=Tg2tJk+*p9v@v$`wu8Ea0m=IgRATRbc1`}hqGq^Uk zh{2@Td$tRw$@V*V}x;tSIxv6bZfQ#+iAxJ12#@} zF@)=Eu%4g`4+!`w+WIQ0?olWHp0gpqG+K=1mfUa8_ zgO&O)#^l~(V`OQrgL%xM#F&o>q+B+;MX*pG=9IWNLDUJNpd#AD03M-iI5(*rw<7qAHwxVnehMZP4n1jbQGa&tI%i9U=; zQjSc=a3@1-+DH@zBW2PYS+SH#9L6NxN`u;*t;^*cdJhE3z06bAMh-St&G9o?L4|0! zI3h*H&r%Sn#jrRfBy2&xh7BVR3et-(7CZGqx4AyXp4Lg6AZSp&4zD=5ghEhTfWXgSfy)b9?fwfXls}i&%MbJ$x$F519`%71FD^=by&_oOVH5^FGU!H7u}s}C;cs~X*OYVMQYi> z8t&+=ZKNnWe9*J}9<|YhH&0*HkZGN)jeV+V7Jo`@7Dm|_9OGF4oJkr6QDvA-b=d6~ zAl6}<9_>C*8mhO=&3flEJ1ajeP4!Tstey`GD^}}zR;yA~%Q08$2sB?%EvTjZZV}Sz zwd0304$2}HGGET1wmNr>M2#9Kx*^+UIKR>o$Uuq(TE5xOo)Igf>!LsI3<{ zWNjR?54av}lq~~~U1;suwAX8G)_`>h#!;z+V?290nm{(yOx09BM|~|1{_#g_DrYa; zEegjsj)l9AIC%1k#8#~Z-N3neq-=MzV1|IdNx%9S4H1sRge?(hel)TG(bmWU=zTo0 zz{%#vNR6tb=`Yyib3}?TCL!X43(AN)QUog}Fm-FMFT%Wtv9|i6t02a-EwB^0r5&Mt zIu40vADJ~2k>)}>tSh%tnTRHR0oqaI3iDKG$)@;bR8V@sb}H4F9np4RfZmRIXU~|s zgISMAgFY3md#c;Dr4rqWEky;4AI4o%<_I4ru50NI-KA@d5$!k(ZyDrez4##QbX?l$xM`l_aK}uoF>Jt=Cv@$E z1wNq{1Bcd=`ZE)ZumPJKv872~3whP-dfIzB{uN@r{<6OKVC2Zew<7Q0fCfxi)_c-( z-@~g%meZzdZF(`1Aim8k@B*XD7r-)%%+Vjw*jxuBZP5iaBHGis_H;zsjb9WT(e_A_ z?=gq$aOdUghADD%dV{WUrs7?THQTU6UHrM3%Q#*Y;Xj_d+JP zd-dWSvM^Y6H%*68Mr+Wu1`F4q7w?rt?3t_Wz=VP44ki#Dn1G*Kz=WwJJaP})KOvj4$oQiZv_sO1ppq5?7%83M^d%PgQ7Ox#drT0RgO$XV3%)=0lL_9ulZGH&L zdiEv*`nJMRW#hM6jSr7()y0kMqvn`&w;F2}qn)j>ChJ4?mE*l|>lN;+DY!qLB(9b9 zYu4UHZ*P_Lk=|&Q5xPlwCeH2(?elD*0+vp9K{LZ)?NY8i`;yAQBZh z9&QA_jXrCNbZyy%T7#xcyni=y4oV} z9hjjbX@-szGsrR9g;z!|GjyzlhpMVd#3Ud#*1c;Bp~owJD-C!Ad&zvs9$d&HAI?c|__;3YPYC4(0^JPgbjyHXfVQbO(4%!c!i^y4= zAAS_+9CQMw%?I7f@A1l4@&B&yv#n!#i|?g6M;fVmg3}MG-l0@aKy_QIj!JgCB$a(1 z%Kjyi7}F|MwI&U2>oBjRcRmsB><)g#YMFVE@}iW&BhwH0w(9JIAE$M0X$;}xi9}Od z7AKxK1dNR|&*nNtuiekXznvRlRj4~xORPh4xUJLEYz|r0f+KXl;luk6ho(3xKsb`$-m(U2`NtD+MY~#jNMD7e!)}-lw&z& zLdY9A*7Si>cbiL~L9a*L@DVj3G!>H0fivAM5yTfJT~y>MQxPjyYMxo9CE>tW^f`C2 zs#blO^E=o#+C@K-%*cVJQ(4l2)&!lJ68EF`ss()@1pDRlppyWzVetMl41f*S^`)*lHztLAFd}qZTYZRf`*7l^${C zT5!_=&VXmP27YO&GcT<=&zkZskozahwR+^5YQD&VcTYfN?YZ*oV}4wjfYzICN;CBV+f|M}yV+ijJ+x<9(XSxCvW` z5xA~9lKprxvotqSQ_UTG2fcwsB_d7X!vm26#)eBooR1{)^(i!~ftCpOe6TRf<-x?U z4cm#Bfn$%4OG#7}FFY_Z(_1^FBFla3H~8+>SV2mu$-gCObj)$A6Ujl#Vvkf~GhF zCbE3NwC}o;6XfzJl*Pe9%OR=drURtLEIAy@dwhG_KEX$?!j*BrR^H z;^ly9v(7|fArqGinS@-(bYLNak4I?J19rYuq*KW9WFhYKg*<;Er4WcmA(*QDLxrTS z!wa1la^-q%z%)5N*eB9H_T1qhjl(@P;S3|5e6_~cj)zV3MNa;zw;Wy-h&j|Mqx%@X zr#Z@G&Pd`R__|X!ClGd3E88)`hiomPSWKE?HGQPXS|#PnDS;!B5xdUPB&A?k_1@4# zB!g=;T1F|c&RoIUlO?Kdi`TJa^)IW?p~8tl2#= z-^!9we;MxWW8&+Z;}MfREAczHL`F;TyU1TrQ9apH<6Bi;waT-uI#5wMX0Rw&FN)@W z_0|P>Vxq?v^oTiPu$aGOiFW7GTW|kr{>}4my-iz~Cj$PGa{pR?si(Xu=nn)!H9-hm zT3)h-MF>&lU+1kYzu)h<$+Oz;tI5B~UsdY$m6itlwY8pn#j6ea0zr!_)(|+QL!%hJ6nJjD(NiES4SO+>wbUQTzo`;7 zDj{x>HowTbc>XQ7E)Zqa)u{T#)5?+J3swg_mOkZGrT+C(XcAH6TjlqZ`9c-JF`hh+ zsDVxVRWNh_W#v~_Rc!ExP%Q*kR+sw8MVy8i0ch@r)tLZnIdoAVsm4EZkLj;_zXkd&&~Jf$3-nu{-va#>=(j+>1^O+}Z-M`p z7Pw`>f;pbN;+3JQV8}CL%8V(~^Jj!4HFKg#3o)g3LuJsn5@9f4hO4bu(7!$?rj+`E zJ~3rwZLOG6Qe9bz1?fL*i;+O`d=e+UcyM0A6QBY~$RCn?#^673IE_I%xP=I+f0XI{{84$yVA-;W_(l@vqo#gtg!m+&m%N7MLhQ@}Q$3bEi|!p#0`n^r?I3AYGV3epRE>$Z zTRCL9U7YTgM|P5;Zm0Q)S@I#xyE4{8dMt>SGx3Q(!VE~`?EuY=kAoFWoAAmlHLwBk zC4{`N3KK~B%LuK9^6mj~$l(@tRE6?Zf&Ru|xA+?jM!E*{kwI?pJ%ps|5Pp5ATjEGR z1loEi?-7eYNSfrA@cpm(nD)9dh5o5ZVPN6;6|(u*#FHwz-0Ph z(1XUgg;&x1ZhbZAuPAy9=wE?;P|^9It%ve%1pVCsZjlYDoCLh*v59r6ypRWT=d51vXf%@?KdeU!$=FKfW#J>ZYNBE|^Owk6K z*9ookzkq%)#s5x&=4DDN{VZsHsbS^s2K@uH$7ldpUMBpKH(4#b12n(1u<%!c=6l4d z&jiqi&>srHX8tLld5Me<^_>oyx8W>$7HIB`oAl}Kd7y6_?iPOlgZUSMmVX@qfgAQ@ zKS{+e0nOXLd?(TJ-Nh%lEiU&k+2>DSt-(e9+YAL(r3va}qibX#;e|M7Qv( z^e)i6V$Fy0ZBUFC5iNQE=(q5GnxOIzrQ&FBGd)9$0R0}uv;AOD{&>*5PH&B0`Jj1~ z)WoCxLGuc(Mb8F(J{KQFDfwRkJrw@)0{|%RPSAT{pIa2Y3bZf9-a*j3vu(-S06MjO z9svD1%F|y#s;8&{0l-}!nX2137YpAE&Au6pSavDj(|aZeg!(U{x5)@4f)?! z>92w2#b7?P=Mm7nB5KjC%wNb9v;04R=Epq$1)B2PL8tn66m(5W`+v&(kZk^9yade) z+ZKHu^wa3?o45ru@3&ht{~%Na{FCo#>hA;1TgF!UD$uKtjzFf@ zA@V53*BV7X3YynnE&Pu_^UkbA{}i;t;}%b1oTGfP5(h|AYDJ~5yb9+toOV|;g5Po+ z9m&{Qzqce5sI3l&+LAzZMTIC??F-aSnLWEe#ikZanabF*p$63@-Wr^pttzj@Nt`!XH7`!aN-O*U zAtBNVHNikKi%*o-24?VQ+70}jI^Ajwuhdbjg3hP_>=(Fi>NE+Izf)&QPJ#TLAxdzT z7xa61@JP&TvS*1(f2GtB(x(fvS-l&?nzDc&^1z&(ENoo>2Z+eNp8V-k%{G*+Ac0dw z?Yi<{$!e*Jx3nCGv1ohL9!dcWQ7FvxBCjc`)L$}9Isg`SfpUK>&p)e5eSuQhFg!2x z;vBNHyoA0{Q{gM|a|xI{V1>w8rV+#;t|8b%e~M zB?2t0rygs|Oa0Yen2VKS%lhEPf%~qP734u}QYso&c)4ozddY@7)E};GRTH^jRdzH{ zQ!UGz1}Q*I70dE`}GmA@iIt%~aEHK7`BFIN=Rq2NlX*K{ctoslbP z=nnUmUE4HgpIlHu`pUGV`D#L_iC1PuV$x69Y3ZThO_Po}75bxVm-{NpYyETpoZU+s zRnp%pt1)8qR#4W_>#M~vGOP%9LNTm6-A;A13%Undr#2wXjcyRQFR6{GpV__4J__7a zxJKYU3`laI@~-FcG$iwYTgX;n9m*;~wW~2yluB2hn)C}gqN&$Z+2hRKgbtI;LzN4l yv&?J$S{?*L#Y0trok|zpVzFmUc-st%>%no!W<%{<(-0S@vf_7qXeMKS=|o zjZTpaON$SJFFt4m5u~Uu+fpR#rX^`hiqR?viq@LSw7Z(LEd6uM`kk3On{6n9Sn%au zm^tS==iGD8{qD@d%-uPxY~LzL5_4Jub1_1G=4I`Wue8~oc351jm3i1a%MUjiop5k5 zkDIJ)esYnI`+*k5DW+g1OtKr!GK^uFd!HZ3v( zF#;>Itoftv26VbLwxz|r0Og|UZ>ivDwOU1@TU`ayBj@r4+;DwIu5EBpxLgK#??%BcKq;j%|R=f{}$cxjt_z40-nf^4k#?1%J*Bpa+q-yB`!wZV>xUf3m_ zm(Iy2S(nkED;4R@fy!aL9y#zZ_W)?f1 zyhp!$$9BGLJLhK_M=j1-u@A9L86St71wAhvr-drM2JebPd$aS23GV!-)swJLzzU5(}3)DX3{8}zBoQUZk)S)rEuNF z?@%dkTv&};^ey*np<7X?Rp2LdYl|ir5DW+g1OtKr!GK^uFd!HZ3_J`51|MT>>z{kR zeZ$5VIyz^ya3mU2ySAy_-P&4zpO);7t$Y}xi5!9f!GK^uFd!HZ3A5oAb@UDnJho zD~@(z+W;OAw-#o~M{cV@q3u(rk&ljrIILXUu@rF|xK4s%sFB1UB5Lj3^^Cm(18s}W z$3gW`8I3sV^R?gui#8_-e03_&+(sFwKaavrsg3!_G+#8=eIpQdJ3TDFo871qM@}P5 z|7&m>VV?sa2|o=vju{w`4agRN3`$;hjAP=2wwS2`2@#tiX(yMqfC=x0e9z@&DI{W)V$G2U08%j`swjjP7)e6rAH}Eoo`JYABjXrnDqW zMUz^0H`}is3`O^clc_-G#!ePfqYmn9cRGf8)V}U`kILdbk(w>qt%j4#&K2sfnfsFQ zv>HOv`z`3&$9m&2RSPB5gqG}QYEO)X=ocvzA~iv^vjbWmBO9+cWM@NBOv|Jr%=#Il j+0_CLWYS1r*+X_VOXp=BWLEB|7E@U&9ZpiL1?=frame*0x1000 + (u32int)addr&0xFFF; + } + return (u32int)addr; + } + else + { + if (align == 1 && (placement_address & 0xFFFFF000) ) + { + // Align the placement address; + placement_address &= 0xFFFFF000; + placement_address += 0x1000; + } + if (phys) + { + *phys = placement_address; + } + u32int tmp = placement_address; + placement_address += sz; + return tmp; + } +} + +void kfree(void *p) +{ + free(p, kheap); +} + +u32int kmalloc_a(u32int sz) +{ + return kmalloc_int(sz, 1, 0); +} + +u32int kmalloc_p(u32int sz, u32int *phys) +{ + return kmalloc_int(sz, 0, phys); +} + +u32int kmalloc_ap(u32int sz, u32int *phys) +{ + return kmalloc_int(sz, 1, phys); +} + +u32int kmalloc(u32int sz) +{ + return kmalloc_int(sz, 0, 0); +} + +static void expand(u32int new_size, heap_t *heap) +{ + // Sanity check. + ASSERT(new_size > heap->end_address - heap->start_address); + + // Get the nearest following page boundary. + if (new_size&0xFFFFF000 != 0) + { + new_size &= 0xFFFFF000; + new_size += 0x1000; + } + + // Make sure we are not overreaching ourselves. + ASSERT(heap->start_address+new_size <= heap->max_address); + + // This should always be on a page boundary. + u32int old_size = heap->end_address-heap->start_address; + + u32int i = old_size; + while (i < new_size) + { + alloc_frame( get_page(heap->start_address+i, 1, kernel_directory), + (heap->supervisor)?1:0, (heap->readonly)?0:1); + i += 0x1000 /* page size */; + } + heap->end_address = heap->start_address+new_size; +} + +static u32int contract(u32int new_size, heap_t *heap) +{ + // Sanity check. + ASSERT(new_size < heap->end_address-heap->start_address); + + // Get the nearest following page boundary. + if (new_size&0x1000) + { + new_size &= 0x1000; + new_size += 0x1000; + } + + // Don't contract too far! + if (new_size < HEAP_MIN_SIZE) + new_size = HEAP_MIN_SIZE; + + u32int old_size = heap->end_address-heap->start_address; + u32int i = old_size - 0x1000; + while (new_size < i) + { + free_frame(get_page(heap->start_address+i, 0, kernel_directory)); + i -= 0x1000; + } + + heap->end_address = heap->start_address + new_size; + return new_size; +} + +static s32int find_smallest_hole(u32int size, u8int page_align, heap_t *heap) +{ + // Find the smallest hole that will fit. + u32int iterator = 0; + while (iterator < heap->index.size) + { + header_t *header = (header_t *)lookup_ordered_array(iterator, &heap->index); + // If the user has requested the memory be page-aligned + if (page_align > 0) + { + // Page-align the starting point of this header. + u32int location = (u32int)header; + s32int offset = 0; + if ((location+sizeof(header_t)) & 0xFFFFF000 != 0) + offset = 0x1000 /* page size */ - (location+sizeof(header_t))%0x1000; + s32int hole_size = (s32int)header->size - offset; + // Can we fit now? + if (hole_size >= (s32int)size) + break; + } + else if (header->size >= size) + break; + iterator++; + } + // Why did the loop exit? + if (iterator == heap->index.size) + return -1; // We got to the end and didn't find anything. + else + return iterator; +} + +static s8int header_t_less_than(void*a, void *b) +{ + return (((header_t*)a)->size < ((header_t*)b)->size)?1:0; +} + +heap_t *create_heap(u32int start, u32int end_addr, u32int max, u8int supervisor, u8int readonly) +{ + heap_t *heap = (heap_t*)kmalloc(sizeof(heap_t)); + + // All our assumptions are made on startAddress and endAddress being page-aligned. + ASSERT(start%0x1000 == 0); + ASSERT(end_addr%0x1000 == 0); + + // Initialise the index. + heap->index = place_ordered_array( (void*)start, HEAP_INDEX_SIZE, &header_t_less_than); + + // Shift the start address forward to resemble where we can start putting data. + start += sizeof(type_t)*HEAP_INDEX_SIZE; + + // Make sure the start address is page-aligned. + if (start & 0xFFFFF000 != 0) + { + start &= 0xFFFFF000; + start += 0x1000; + } + // Write the start, end and max addresses into the heap structure. + heap->start_address = start; + heap->end_address = end_addr; + heap->max_address = max; + heap->supervisor = supervisor; + heap->readonly = readonly; + + // We start off with one large hole in the index. + header_t *hole = (header_t *)start; + hole->size = end_addr-start; + hole->magic = HEAP_MAGIC; + hole->is_hole = 1; + insert_ordered_array((void*)hole, &heap->index); + + return heap; +} + +void *alloc(u32int size, u8int page_align, heap_t *heap) +{ + + // Make sure we take the size of header/footer into account. + u32int new_size = size + sizeof(header_t) + sizeof(footer_t); + // Find the smallest hole that will fit. + s32int iterator = find_smallest_hole(new_size, page_align, heap); + + if (iterator == -1) // If we didn't find a suitable hole + { + // Save some previous data. + u32int old_length = heap->end_address - heap->start_address; + u32int old_end_address = heap->end_address; + + // We need to allocate some more space. + expand(old_length+new_size, heap); + u32int new_length = heap->end_address-heap->start_address; + + // Find the endmost header. (Not endmost in size, but in location). + iterator = 0; + // Vars to hold the index of, and value of, the endmost header found so far. + u32int idx = -1; u32int value = 0x0; + while (iterator < heap->index.size) + { + u32int tmp = (u32int)lookup_ordered_array(iterator, &heap->index); + if (tmp > value) + { + value = tmp; + idx = iterator; + } + iterator++; + } + + // If we didn't find ANY headers, we need to add one. + if (idx == -1) + { + header_t *header = (header_t *)old_end_address; + header->magic = HEAP_MAGIC; + header->size = new_length - old_length; + header->is_hole = 1; + footer_t *footer = (footer_t *) (old_end_address + header->size - sizeof(footer_t)); + footer->magic = HEAP_MAGIC; + footer->header = header; + insert_ordered_array((void*)header, &heap->index); + } + else + { + // The last header needs adjusting. + header_t *header = lookup_ordered_array(idx, &heap->index); + header->size += new_length - old_length; + // Rewrite the footer. + footer_t *footer = (footer_t *) ( (u32int)header + header->size - sizeof(footer_t) ); + footer->header = header; + footer->magic = HEAP_MAGIC; + } + // We now have enough space. Recurse, and call the function again. + return alloc(size, page_align, heap); + } + + header_t *orig_hole_header = (header_t *)lookup_ordered_array(iterator, &heap->index); + u32int orig_hole_pos = (u32int)orig_hole_header; + u32int orig_hole_size = orig_hole_header->size; + // Here we work out if we should split the hole we found into two parts. + // Is the original hole size - requested hole size less than the overhead for adding a new hole? + if (orig_hole_size-new_size < sizeof(header_t)+sizeof(footer_t)) + { + // Then just increase the requested size to the size of the hole we found. + size += orig_hole_size-new_size; + new_size = orig_hole_size; + } + + // If we need to page-align the data, do it now and make a new hole in front of our block. + if (page_align && orig_hole_pos&0xFFFFF000) + { + u32int new_location = orig_hole_pos + 0x1000 /* page size */ - (orig_hole_pos&0xFFF) - sizeof(header_t); + header_t *hole_header = (header_t *)orig_hole_pos; + hole_header->size = 0x1000 /* page size */ - (orig_hole_pos&0xFFF) - sizeof(header_t); + hole_header->magic = HEAP_MAGIC; + hole_header->is_hole = 1; + footer_t *hole_footer = (footer_t *) ( (u32int)new_location - sizeof(footer_t) ); + hole_footer->magic = HEAP_MAGIC; + hole_footer->header = hole_header; + orig_hole_pos = new_location; + orig_hole_size = orig_hole_size - hole_header->size; + } + else + { + // Else we don't need this hole any more, delete it from the index. + remove_ordered_array(iterator, &heap->index); + } + + // Overwrite the original header... + header_t *block_header = (header_t *)orig_hole_pos; + block_header->magic = HEAP_MAGIC; + block_header->is_hole = 0; + block_header->size = new_size; + // ...And the footer + footer_t *block_footer = (footer_t *) (orig_hole_pos + sizeof(header_t) + size); + block_footer->magic = HEAP_MAGIC; + block_footer->header = block_header; + + // We may need to write a new hole after the allocated block. + // We do this only if the new hole would have positive size... + if (orig_hole_size - new_size > 0) + { + header_t *hole_header = (header_t *) (orig_hole_pos + sizeof(header_t) + size + sizeof(footer_t)); + hole_header->magic = HEAP_MAGIC; + hole_header->is_hole = 1; + hole_header->size = orig_hole_size - new_size; + footer_t *hole_footer = (footer_t *) ( (u32int)hole_header + orig_hole_size - new_size - sizeof(footer_t) ); + if ((u32int)hole_footer < heap->end_address) + { + hole_footer->magic = HEAP_MAGIC; + hole_footer->header = hole_header; + } + // Put the new hole in the index; + insert_ordered_array((void*)hole_header, &heap->index); + } + + // ...And we're done! + return (void *) ( (u32int)block_header+sizeof(header_t) ); +} + +void free(void *p, heap_t *heap) +{ + // Exit gracefully for null pointers. + if (p == 0) + return; + + // Get the header and footer associated with this pointer. + header_t *header = (header_t*) ( (u32int)p - sizeof(header_t) ); + footer_t *footer = (footer_t*) ( (u32int)header + header->size - sizeof(footer_t) ); + + // Sanity checks. + ASSERT(header->magic == HEAP_MAGIC); + ASSERT(footer->magic == HEAP_MAGIC); + + // Make us a hole. + header->is_hole = 1; + + // Do we want to add this header into the 'free holes' index? + char do_add = 1; + + // Unify left + // If the thing immediately to the left of us is a footer... + footer_t *test_footer = (footer_t*) ( (u32int)header - sizeof(footer_t) ); + if (test_footer->magic == HEAP_MAGIC && + test_footer->header->is_hole == 1) + { + u32int cache_size = header->size; // Cache our current size. + header = test_footer->header; // Rewrite our header with the new one. + footer->header = header; // Rewrite our footer to point to the new header. + header->size += cache_size; // Change the size. + do_add = 0; // Since this header is already in the index, we don't want to add it again. + } + + // Unify right + // If the thing immediately to the right of us is a header... + header_t *test_header = (header_t*) ( (u32int)footer + sizeof(footer_t) ); + if (test_header->magic == HEAP_MAGIC && + test_header->is_hole) + { + header->size += test_header->size; // Increase our size. + test_footer = (footer_t*) ( (u32int)test_header + // Rewrite it's footer to point to our header. + test_header->size - sizeof(footer_t) ); + footer = test_footer; + // Find and remove this header from the index. + u32int iterator = 0; + while ( (iterator < heap->index.size) && + (lookup_ordered_array(iterator, &heap->index) != (void*)test_header) ) + iterator++; + + // Make sure we actually found the item. + ASSERT(iterator < heap->index.size); + // Remove it. + remove_ordered_array(iterator, &heap->index); + } + + // If the footer location is the end address, we can contract. + if ( (u32int)footer + sizeof(footer_t) == heap->end_address) { + u32int old_length = heap->end_address-heap->start_address; + u32int new_length = contract( (u32int)header - heap->start_address, heap); + // Check how big we will be after resizing. + if (header->size - (old_length-new_length) > 0) { + // We will still exist, so resize us. + header->size -= old_length-new_length; + footer = (footer_t*) ( (u32int)header + header->size - sizeof(footer_t) ); + footer->magic = HEAP_MAGIC; + footer->header = header; + } else { + // We will no longer exist :(. Remove us from the index. + u32int iterator = 0; + while ( (iterator < heap->index.size) && + (lookup_ordered_array(iterator, &heap->index) != (void*)test_header) ) + { + iterator++; + } + // If we didn't find ourselves, we have nothing to remove. + if (iterator < heap->index.size) { + remove_ordered_array(iterator, &heap->index); + } + } + } + + // If required, add us to the index. + if (do_add == 1) { + insert_ordered_array((void*)header, &heap->index); + } +} diff --git a/kernel/src/kheap.h b/kernel/src/kheap.h new file mode 100644 index 0000000..eb6819f --- /dev/null +++ b/kernel/src/kheap.h @@ -0,0 +1,100 @@ +// kheap.h -- Interface for kernel heap functions, also provides +// a placement malloc() for use before the heap is +// initialised. +// Written for JamesM's kernel development tutorials. + +#ifndef KHEAP_H +#define KHEAP_H + +#include "common.h" +#include "ordered_array.h" + +#define KHEAP_START 0xC0000000 +#define KHEAP_INITIAL_SIZE 0x100000 + +#define HEAP_INDEX_SIZE 0x20000 +#define HEAP_MAGIC 0x123890AB +#define HEAP_MIN_SIZE 0x70000 + +/** + Size information for a hole/block +**/ +typedef struct +{ + u32int magic; // Magic number, used for error checking and identification. + u8int is_hole; // 1 if this is a hole. 0 if this is a block. + u32int size; // size of the block, including the end footer. +} header_t; + +typedef struct +{ + u32int magic; // Magic number, same as in header_t. + header_t *header; // Pointer to the block header. +} footer_t; + +typedef struct +{ + ordered_array_t index; + u32int start_address; // The start of our allocated space. + u32int end_address; // The end of our allocated space. May be expanded up to max_address. + u32int max_address; // The maximum address the heap can be expanded to. + u8int supervisor; // Should extra pages requested by us be mapped as supervisor-only? + u8int readonly; // Should extra pages requested by us be mapped as read-only? +} heap_t; + +/** + Create a new heap. +**/ +heap_t *create_heap(u32int start, u32int end, u32int max, u8int supervisor, u8int readonly); + +/** + Allocates a contiguous region of memory 'size' in size. If page_align==1, it creates that block starting + on a page boundary. +**/ +void *alloc(u32int size, u8int page_align, heap_t *heap); + +/** + Releases a block allocated with 'alloc'. +**/ +void free(void *p, heap_t *heap); + +/** + Allocate a chunk of memory, sz in size. If align == 1, + the chunk must be page-aligned. If phys != 0, the physical + location of the allocated chunk will be stored into phys. + + This is the internal version of kmalloc. More user-friendly + parameter representations are available in kmalloc, kmalloc_a, + kmalloc_ap, kmalloc_p. +**/ +u32int kmalloc_int(u32int sz, int align, u32int *phys); + +/** + Allocate a chunk of memory, sz in size. The chunk must be + page aligned. +**/ +u32int kmalloc_a(u32int sz); + +/** + Allocate a chunk of memory, sz in size. The physical address + is returned in phys. Phys MUST be a valid pointer to u32int! +**/ +u32int kmalloc_p(u32int sz, u32int *phys); + +/** + Allocate a chunk of memory, sz in size. The physical address + is returned in phys. It must be page-aligned. +**/ +u32int kmalloc_ap(u32int sz, u32int *phys); + +/** + General allocation function. +**/ +u32int kmalloc(u32int sz); + +/** + General deallocation function. +**/ +void kfree(void *p); + +#endif // KHEAP_H diff --git a/kernel/src/main.c b/kernel/src/main.c index e2232ba..d703359 100644 --- a/kernel/src/main.c +++ b/kernel/src/main.c @@ -1,18 +1,61 @@ // main.c -- Defines the C-code kernel entry point, calls initialisation routines. // Made for JamesM's tutorials +#include "descriptor_tables.h" #include "monitor.h" +#include "paging.h" +#include "timer.h" int main(struct multiboot *mboot_ptr) { - monitor_clear(); - monitor_write("Hello, world!"); - - monitor_newline(); - monitor_write_dec(42); + // Initialise all the ISRs and segmentation + init_descriptor_tables(); + // Initialise the screen (by clearing it) + monitor_clear(); + + // Exercise kmalloc and kfree + u32int a = kmalloc(8); // allocated via placement address + + initialise_paging(); + + // allocated on the heap + u32int b = kmalloc(8); + u32int c = kmalloc(8); + monitor_write("a: "); + monitor_write_hex(a); + monitor_write(", b: "); + monitor_write_hex(b); + monitor_write("\nc: "); + monitor_write_hex(c); + + kfree(c); + kfree(b); + u32int d = kmalloc(12); + monitor_write(", d: "); + monitor_write_hex(d); monitor_newline(); + + // Write out a sample string + monitor_write("Hello, paging world!\n"); + + // Write a number in decimal + monitor_write_dec(42); + monitor_newline(); + + // Write a number in hex + monitor_write("0x"); monitor_write_hex(0xdeadbeef); + monitor_newline(); + + asm volatile("int $0x3"); + asm volatile("int $0x4"); + +// asm volatile("sti"); +// init_timer(50); + + u32int *ptr = (u32int*)0xA0000000; + u32int cause_a_page_fault = *ptr; return 0xdeadbeef; } diff --git a/kernel/src/monitor.c b/kernel/src/monitor.c index 4ab4d98..fa21faa 100644 --- a/kernel/src/monitor.c +++ b/kernel/src/monitor.c @@ -1,7 +1,13 @@ +// monitor.c -- Defines functions for writing to the monitor. +// heavily based on Bran's kernel development tutorials, +// but rewritten for JamesM's kernel tutorials. + #include "monitor.h" // The VGA framebuffer starts at 0xB8000. u16int *video_memory = (u16int *)0xB8000; + +// Stores the cursor position. u8int cursor_x = 0; u8int cursor_y = 0; @@ -11,14 +17,10 @@ static void move_cursor() // The screen is 80 characters wide... u16int cursorLocation = cursor_y * 80 + cursor_x; - outb(0x3D4, 14); - // Tell the VGA board we are setting the high cursor byte. - outb(0x3D5, cursorLocation >> 8); - // Send the high cursor byte. - outb(0x3D4, 15); - // Tell the VGA board we are setting the low cursor byte. - outb(0x3D5, cursorLocation); - // Send the low cursor byte. + outb(0x3D4, 14); // Tell the VGA board we are setting the high cursor byte. + outb(0x3D5, cursorLocation >> 8); // Send the high cursor byte. + outb(0x3D4, 15); // Tell the VGA board we are setting the low cursor byte. + outb(0x3D5, cursorLocation); // Send the low cursor byte. } // Scrolls the text on the screen up by one line. @@ -127,7 +129,7 @@ void monitor_clear() } // Outputs a null-terminated ASCII string to the monitor. -void monitor_write(char *c) +void monitor_write(const char *c) { int i = 0; while (c[i]) { @@ -137,24 +139,27 @@ void monitor_write(char *c) void monitor_newline() { - while (cursor_x > 0) monitor_put(' '); + monitor_put('\n'); } void monitor_write_hex(u32int n) { - static char hex_chars[] = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', - 'a', 'b', 'c', 'd', 'e', 'f' }; - u32int mask = 0xf0000000; - int i = 28; - while (i >= 0) { - monitor_put(hex_chars[(n & mask) >> i]); - mask >>= 4; - i -= 4; - } + static char chars[] = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', + 'a', 'b', 'c', 'd', 'e', 'f' }; + s32int tmp; + char onlyZeroes = 1; + int i; + for (i = 28; i >= 0; i -= 4) { + tmp = (n >> i) & 0xf; + if (tmp == 0 && onlyZeroes) { + continue; + } + onlyZeroes = 0; + monitor_put(chars[tmp]); + } } -u32int pow(base, exp) -{ +u32int pow(base, exp) { u32int n = base; if (exp == 0) return 1; while (exp > 1) { @@ -166,9 +171,44 @@ u32int pow(base, exp) void monitor_write_dec(u32int n) { - static char dec_chars[] = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9' }; - int i = 7; - while (i >= 0) { - monitor_put(dec_chars[(n / pow(10, i--)) % 10]); + static char chars[] = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9' }; + char onlyZeroes = 1; + int i; + int tmp; + if (n == 0) { + monitor_put('0'); + return; + } + for (i = 7; i >= 0; --i) { + tmp = (n / pow(10, i)) % 10; + if (tmp == 0 && onlyZeroes) { + continue; + } + onlyZeroes = 0; + monitor_put(chars[tmp]); } } + +void monitor_write_dec2(u32int n) { + + if (n == 0) { + monitor_put('0'); + return; + } + + s32int acc = n; + char c[32]; + int i = 0; + for (; acc > 0; ++i, acc /=10) { + c[i] = '0' + acc % 10; + } + c[i] = 0; + + char c2[32]; + c2[i--] = 0; + int j = 0; + while(i >= 0) { + c2[i--] = c[j++]; + } + monitor_write(c2); +} diff --git a/kernel/src/monitor.h b/kernel/src/monitor.h index 53e6804..41e9bbc 100644 --- a/kernel/src/monitor.h +++ b/kernel/src/monitor.h @@ -13,7 +13,7 @@ void monitor_put(char c); void monitor_clear(); // Output a null-terminated ASCII string to the monitor. -void monitor_write(char *c); +void monitor_write(const char *c); // Move the cursor to the next line void monitor_newline(); diff --git a/kernel/src/ordered_array.c b/kernel/src/ordered_array.c new file mode 100644 index 0000000..7ed6639 --- /dev/null +++ b/kernel/src/ordered_array.c @@ -0,0 +1,76 @@ +// ordered_array.c -- Implementation for creating, inserting and deleting +// from ordered arrays. +// Written for JamesM's kernel development tutorials. + +#include "ordered_array.h" + +s8int standard_lessthan_predicate(type_t a, type_t b) +{ + return (aarray); +} + +void insert_ordered_array(type_t item, ordered_array_t *array) +{ + ASSERT(array->less_than); + u32int iterator = 0; + while (iterator < array->size && array->less_than(array->array[iterator], item)) + iterator++; + if (iterator == array->size) // just add at the end of the array. + array->array[array->size++] = item; + else + { + type_t tmp = array->array[iterator]; + array->array[iterator] = item; + while (iterator < array->size) + { + iterator++; + type_t tmp2 = array->array[iterator]; + array->array[iterator] = tmp; + tmp = tmp2; + } + array->size++; + } +} + +type_t lookup_ordered_array(u32int i, ordered_array_t *array) +{ + ASSERT(i < array->size); + return array->array[i]; +} + +void remove_ordered_array(u32int i, ordered_array_t *array) +{ + while (i < array->size) + { + array->array[i] = array->array[i+1]; + i++; + } + array->size--; +} diff --git a/kernel/src/ordered_array.h b/kernel/src/ordered_array.h new file mode 100644 index 0000000..bb2b8f4 --- /dev/null +++ b/kernel/src/ordered_array.h @@ -0,0 +1,59 @@ +// ordered_array.h -- Interface for creating, inserting and deleting +// from ordered arrays. +// Written for JamesM's kernel development tutorials. + +#ifndef ORDERED_ARRAY_H +#define ORDERED_ARRAY_H + +#include "common.h" + +/** + This array is insertion sorted - it always remains in a sorted state (between calls). + It can store anything that can be cast to a void* -- so a u32int, or any pointer. +**/ +typedef void* type_t; +/** + A predicate should return nonzero if the first argument is less than the second. Else + it should return zero. +**/ +typedef s8int (*lessthan_predicate_t)(type_t,type_t); +typedef struct +{ + type_t *array; + u32int size; + u32int max_size; + lessthan_predicate_t less_than; +} ordered_array_t; + +/** + A standard less than predicate. +**/ +s8int standard_lessthan_predicate(type_t a, type_t b); + +/** + Create an ordered array. +**/ +ordered_array_t create_ordered_array(u32int max_size, lessthan_predicate_t less_than); +ordered_array_t place_ordered_array(void *addr, u32int max_size, lessthan_predicate_t less_than); + +/** + Destroy an ordered array. +**/ +void destroy_ordered_array(ordered_array_t *array); + +/** + Add an item into the array. +**/ +void insert_ordered_array(type_t item, ordered_array_t *array); + +/** + Lookup the item at index i. +**/ +type_t lookup_ordered_array(u32int i, ordered_array_t *array); + +/** + Deletes the item at location i from the array. +**/ +void remove_ordered_array(u32int i, ordered_array_t *array); + +#endif // ORDERED_ARRAY_H diff --git a/kernel/src/paging.c b/kernel/src/paging.c new file mode 100644 index 0000000..d98d52a --- /dev/null +++ b/kernel/src/paging.c @@ -0,0 +1,225 @@ +// paging.c -- Defines the interface for and structures relating to paging. +// Written for JamesM's kernel development tutorials. + +#include "paging.h" +#include "kheap.h" + +// The kernel's page directory +page_directory_t *kernel_directory=0; + +// The current page directory; +page_directory_t *current_directory=0; + +// A bitset of frames - used or free. +u32int *frames; +u32int nframes; + +// Defined in kheap.c +extern u32int placement_address; +extern heap_t *kheap; + +// Macros used in the bitset algorithms. +#define INDEX_FROM_BIT(a) (a/(8*4)) +#define OFFSET_FROM_BIT(a) (a%(8*4)) + +// Static function to set a bit in the frames bitset +static void set_frame(u32int frame_addr) +{ + u32int frame = frame_addr/0x1000; + u32int idx = INDEX_FROM_BIT(frame); + u32int off = OFFSET_FROM_BIT(frame); + frames[idx] |= (0x1 << off); +} + +// Static function to clear a bit in the frames bitset +static void clear_frame(u32int frame_addr) +{ + u32int frame = frame_addr/0x1000; + u32int idx = INDEX_FROM_BIT(frame); + u32int off = OFFSET_FROM_BIT(frame); + frames[idx] &= ~(0x1 << off); +} + +// Static function to test if a bit is set. +static u32int test_frame(u32int frame_addr) +{ + u32int frame = frame_addr/0x1000; + u32int idx = INDEX_FROM_BIT(frame); + u32int off = OFFSET_FROM_BIT(frame); + return (frames[idx] & (0x1 << off)); +} + +// Static function to find the first free frame. +static u32int first_frame() +{ + u32int i, j; + for (i = 0; i < INDEX_FROM_BIT(nframes); i++) + { + if (frames[i] != 0xFFFFFFFF) // nothing free, exit early. + { + // at least one bit is free here. + for (j = 0; j < 32; j++) + { + u32int toTest = 0x1 << j; + if ( !(frames[i]&toTest) ) + { + return i*4*8+j; + } + } + } + } +} + +// Function to allocate a frame. +void alloc_frame(page_t *page, int is_kernel, int is_writeable) +{ + if (page->frame != 0) + { + return; + } + else + { + u32int idx = first_frame(); + if (idx == (u32int)-1) + { + // PANIC! no free frames!! + } + set_frame(idx*0x1000); + page->present = 1; + page->rw = (is_writeable)?1:0; + page->user = (is_kernel)?0:1; + page->frame = idx; + } +} + +// Function to deallocate a frame. +void free_frame(page_t *page) +{ + u32int frame; + if (!(frame=page->frame)) + { + return; + } + else + { + clear_frame(frame); + page->frame = 0x0; + } +} + +void initialise_paging() +{ + // The size of physical memory. For the moment we + // assume it is 16MB big. + u32int mem_end_page = 0x1000000; + + nframes = mem_end_page / 0x1000; + frames = (u32int*)kmalloc(INDEX_FROM_BIT(nframes)); + memset(frames, 0, INDEX_FROM_BIT(nframes)); + + // Let's make a page directory. + kernel_directory = (page_directory_t*)kmalloc_a(sizeof(page_directory_t)); + memset(kernel_directory, 0, sizeof(page_directory_t)); + current_directory = kernel_directory; + + // Map some pages in the kernel heap area. + // Here we call get_page but not alloc_frame. This causes page_table_t's + // to be created where necessary. We can't allocate frames yet because they + // they need to be identity mapped first below, and yet we can't increase + // placement_address between identity mapping and enabling the heap! + int i = 0; + for (i = KHEAP_START; i < KHEAP_START+KHEAP_INITIAL_SIZE; i += 0x1000) + get_page(i, 1, kernel_directory); + + // We need to identity map (phys addr = virt addr) from + // 0x0 to the end of used memory, so we can access this + // transparently, as if paging wasn't enabled. + // NOTE that we use a while loop here deliberately. + // inside the loop body we actually change placement_address + // by calling kmalloc(). A while loop causes this to be + // computed on-the-fly rather than once at the start. + // Allocate a lil' bit extra so the kernel heap can be + // initialised properly. + i = 0; + while (i < placement_address+0x1000) + { + // Kernel code is readable but not writeable from userspace. + alloc_frame( get_page(i, 1, kernel_directory), 0, 0); + i += 0x1000; + } + + // Now allocate those pages we mapped earlier. + for (i = KHEAP_START; i < KHEAP_START+KHEAP_INITIAL_SIZE; i += 0x1000) + alloc_frame( get_page(i, 1, kernel_directory), 0, 0); + + // Before we enable paging, we must register our page fault handler. + register_interrupt_handler(14, page_fault); + + // Now, enable paging! + switch_page_directory(kernel_directory); + + // Initialise the kernel heap. + kheap = create_heap(KHEAP_START, KHEAP_START+KHEAP_INITIAL_SIZE, 0xCFFFF000, 0, 0); +} + +void switch_page_directory(page_directory_t *dir) +{ + current_directory = dir; + asm volatile("mov %0, %%cr3":: "r"(&dir->tablesPhysical)); + u32int cr0; + asm volatile("mov %%cr0, %0": "=r"(cr0)); + cr0 |= 0x80000000; // Enable paging! + asm volatile("mov %0, %%cr0":: "r"(cr0)); +} + +page_t *get_page(u32int address, int make, page_directory_t *dir) +{ + // Turn the address into an index. + address /= 0x1000; + // Find the page table containing this address. + u32int table_idx = address / 1024; + + if (dir->tables[table_idx]) // If this table is already assigned + { + return &dir->tables[table_idx]->pages[address%1024]; + } + else if(make) + { + u32int tmp; + dir->tables[table_idx] = (page_table_t*)kmalloc_ap(sizeof(page_table_t), &tmp); + memset(dir->tables[table_idx], 0, 0x1000); + dir->tablesPhysical[table_idx] = tmp | 0x7; // PRESENT, RW, US. + return &dir->tables[table_idx]->pages[address%1024]; + } + else + { + return 0; + } +} + + +void page_fault(registers_t regs) +{ + // A page fault has occurred. + // The faulting address is stored in the CR2 register. + u32int faulting_address; + asm volatile("mov %%cr2, %0" : "=r" (faulting_address)); + + // The error code gives us details of what happened. + int present = !(regs.err_code & 0x1); // Page not present + int rw = regs.err_code & 0x2; // Write operation? + int us = regs.err_code & 0x4; // Processor was in user-mode? + int reserved = regs.err_code & 0x8; // Overwritten CPU-reserved bits of page entry? + int id = regs.err_code & 0x10; // Caused by an instruction fetch? + + // Output an error message. + monitor_write("Page fault! ( "); + if (present) {monitor_write("present ");} + if (rw) {monitor_write("read-only ");} + if (us) {monitor_write("user-mode ");} + if (reserved) {monitor_write("reserved ");} + monitor_write(") at 0x"); + monitor_write_hex(faulting_address); + monitor_write("\n"); + PANIC("Page fault"); +} diff --git a/kernel/src/paging.h b/kernel/src/paging.h new file mode 100644 index 0000000..5ed61eb --- /dev/null +++ b/kernel/src/paging.h @@ -0,0 +1,70 @@ +// paging.h -- Defines the interface for and structures relating to paging. +// Written for JamesM's kernel development tutorials. + +#ifndef PAGING_H +#define PAGING_H + +#include "common.h" +#include "isr.h" + +typedef struct page +{ + u32int present : 1; // Page present in memory + u32int rw : 1; // Read-only if clear, readwrite if set + u32int user : 1; // Supervisor level only if clear + u32int accessed : 1; // Has the page been accessed since last refresh? + u32int dirty : 1; // Has the page been written to since last refresh? + u32int unused : 7; // Amalgamation of unused and reserved bits + u32int frame : 20; // Frame address (shifted right 12 bits) +} page_t; + +typedef struct page_table +{ + page_t pages[1024]; +} page_table_t; + +typedef struct page_directory +{ + /** + Array of pointers to pagetables. + **/ + page_table_t *tables[1024]; + /** + Array of pointers to the pagetables above, but gives their *physical* + location, for loading into the CR3 register. + **/ + u32int tablesPhysical[1024]; + + /** + The physical address of tablesPhysical. This comes into play + when we get our kernel heap allocated and the directory + may be in a different location in virtual memory. + **/ + u32int physicalAddr; +} page_directory_t; + +/** + Sets up the environment, page directories etc and + enables paging. +**/ +void initialise_paging(); + +/** + Causes the specified page directory to be loaded into the + CR3 register. +**/ +void switch_page_directory(page_directory_t *new); + +/** + Retrieves a pointer to the page required. + If make == 1, if the page-table in which this page should + reside isn't created, create it! +**/ +page_t *get_page(u32int address, int make, page_directory_t *dir); + +/** + Handler for page faults. +**/ +void page_fault(registers_t regs); + +#endif diff --git a/kernel/src/timer.c b/kernel/src/timer.c new file mode 100644 index 0000000..bcaffa6 --- /dev/null +++ b/kernel/src/timer.c @@ -0,0 +1,38 @@ +// timer.c -- Initialises the PIT, and handles clock updates. +// Written for JamesM's kernel development tutorials. + +#include "timer.h" +#include "isr.h" +#include "monitor.h" + +u32int tick = 0; + +static void timer_callback(registers_t regs) +{ + tick++; + monitor_write("Tick: "); + monitor_write_dec(tick); + monitor_write("\n"); +} + +void init_timer(u32int frequency) +{ + // Firstly, register our timer callback. + register_interrupt_handler(IRQ0, &timer_callback); + + // The value we send to the PIT is the value to divide it's input clock + // (1193180 Hz) by, to get our required frequency. Important to note is + // that the divisor must be small enough to fit into 16-bits. + u32int divisor = 1193180 / frequency; + + // Send the command byte. + outb(0x43, 0x36); + + // Divisor has to be sent byte-wise, so split here into upper/lower bytes. + u8int l = (u8int)(divisor & 0xFF); + u8int h = (u8int)( (divisor>>8) & 0xFF ); + + // Send the frequency divisor. + outb(0x40, l); + outb(0x40, h); +} diff --git a/kernel/src/timer.h b/kernel/src/timer.h new file mode 100644 index 0000000..2aa1af5 --- /dev/null +++ b/kernel/src/timer.h @@ -0,0 +1,11 @@ +// timer.h -- Defines the interface for all PIT-related functions. +// Written for JamesM's kernel development tutorials. + +#ifndef TIMER_H +#define TIMER_H + +#include "common.h" + +void init_timer(u32int frequency); + +#endif