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 68d3d64..68faeeb 100644 Binary files a/kernel/floppy.img and b/kernel/floppy.img differ 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 6e0c68e..76e1013 100755 Binary files a/kernel/src/kernel and b/kernel/src/kernel differ diff --git a/kernel/src/kheap.c b/kernel/src/kheap.c new file mode 100644 index 0000000..60ac764 --- /dev/null +++ b/kernel/src/kheap.c @@ -0,0 +1,407 @@ +// kheap.c -- Kernel heap functions, also provides +// a placement malloc() for use before the heap is +// initialised. +// Written for JamesM's kernel development tutorials. + +#include "kheap.h" +#include "paging.h" + +// end is defined in the linker script. +extern u32int end; +u32int placement_address = (u32int)&end; +extern page_directory_t *kernel_directory; +heap_t *kheap=0; + +u32int kmalloc_int(u32int sz, int align, u32int *phys) +{ + if (kheap != 0) + { + void *addr = alloc(sz, (u8int)align, kheap); + if (phys != 0) + { + page_t *page = get_page((u32int)addr, 0, kernel_directory); + *phys = page->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