mirror of
https://github.com/samsonjs/kernel.git
synced 2026-03-25 09:25:46 +00:00
support descriptor tables, interrupts, timers, allocation, paging
This commit is contained in:
parent
b915cd251e
commit
fb0358197b
25 changed files with 1758 additions and 88 deletions
40
Vagrantfile
vendored
40
Vagrantfile
vendored
|
|
@ -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
11
kernel/go
Executable 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
|
||||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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(;;);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
155
kernel/src/descriptor_tables.c
Normal file
155
kernel/src/descriptor_tables.c
Normal 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 */;
|
||||
}
|
||||
109
kernel/src/descriptor_tables.h
Normal file
109
kernel/src/descriptor_tables.h
Normal 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();
|
||||
29
kernel/src/gdt.s
Normal file
29
kernel/src/gdt.s
Normal 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
149
kernel/src/interrupt.s
Normal 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
51
kernel/src/isr.c
Normal 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
39
kernel/src/isr.h
Normal 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
407
kernel/src/kheap.c
Normal 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
100
kernel/src/kheap.h
Normal 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
|
||||
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
|
|
|
|||
76
kernel/src/ordered_array.c
Normal file
76
kernel/src/ordered_array.c
Normal 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--;
|
||||
}
|
||||
59
kernel/src/ordered_array.h
Normal file
59
kernel/src/ordered_array.h
Normal 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
225
kernel/src/paging.c
Normal 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
70
kernel/src/paging.h
Normal 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
38
kernel/src/timer.c
Normal 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
11
kernel/src/timer.h
Normal 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
|
||||
Loading…
Reference in a new issue