support descriptor tables, interrupts, timers, allocation, paging

This commit is contained in:
Sami Samhuri 2012-01-10 08:47:07 -08:00
parent b915cd251e
commit fb0358197b
25 changed files with 1758 additions and 88 deletions

40
Vagrantfile vendored
View file

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

Binary file not shown.

11
kernel/go Executable file
View file

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

View file

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

View file

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

View file

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

View file

@ -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 */;
}

View file

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

View file

29
kernel/src/gdt.s Normal file
View file

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

149
kernel/src/interrupt.s Normal file
View file

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

51
kernel/src/isr.c Normal file
View file

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

39
kernel/src/isr.h Normal file
View file

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

Binary file not shown.

407
kernel/src/kheap.c Normal file
View file

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

100
kernel/src/kheap.h Normal file
View file

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

View file

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

View file

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

View file

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

View file

@ -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 (a<b)?1:0;
}
ordered_array_t create_ordered_array(u32int max_size, lessthan_predicate_t less_than)
{
ordered_array_t to_ret;
to_ret.array = (void*)kmalloc(max_size*sizeof(type_t));
memset(to_ret.array, 0, max_size*sizeof(type_t));
to_ret.size = 0;
to_ret.max_size = max_size;
to_ret.less_than = less_than;
return to_ret;
}
ordered_array_t place_ordered_array(void *addr, u32int max_size, lessthan_predicate_t less_than)
{
ordered_array_t to_ret;
to_ret.array = (type_t*)addr;
memset(to_ret.array, 0, max_size*sizeof(type_t));
to_ret.size = 0;
to_ret.max_size = max_size;
to_ret.less_than = less_than;
return to_ret;
}
void destroy_ordered_array(ordered_array_t *array)
{
// kfree(array->array);
}
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--;
}

View file

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

225
kernel/src/paging.c Normal file
View file

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

70
kernel/src/paging.h Normal file
View file

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

38
kernel/src/timer.c Normal file
View file

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

11
kernel/src/timer.h Normal file
View file

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