Legacy OS files. Have an i686-elf-gcc & -ld in your PATH,

and the Makefile will take care of the rest.
master
Jenny Curle 5 years ago
commit f6ba6aa117

@ -0,0 +1,46 @@
;
; boot.s -- Kernel start location. Also defines multiboot header.
; Based on Bran's kernel development tutorial file start.asm
;
MBOOT_PAGE_ALIGN equ 1<<0 ; Load kernel and modules on a page boundary
MBOOT_MEM_INFO equ 1<<1 ; Provide your kernel with memory info
MBOOT_HEADER_MAGIC equ 0x1BADB002 ; Multiboot Magic value
; NOTE: We do not use MBOOT_AOUT_KLUDGE. It means that GRUB does not
; pass us a symbol table.
MBOOT_HEADER_FLAGS equ MBOOT_PAGE_ALIGN | MBOOT_MEM_INFO
MBOOT_CHECKSUM equ -(MBOOT_HEADER_MAGIC + MBOOT_HEADER_FLAGS)
[BITS 32] ; All instructions should be 32-bit.
[GLOBAL mboot] ; Make 'mboot' accessible from C.
[EXTERN code] ; Start of the '.text' section.
[EXTERN bss] ; Start of the .bss section.
[EXTERN end] ; End of the last loadable section.
mboot:
dd MBOOT_HEADER_MAGIC ; GRUB will search for this value on each
; 4-byte boundary in your kernel file
dd MBOOT_HEADER_FLAGS ; How GRUB should load your file / settings
dd MBOOT_CHECKSUM ; To ensure that the above values are correct
dd mboot ; Location of this descriptor
dd code ; Start of kernel '.text' (code) section.
dd bss ; End of kernel '.data' section.
dd end ; End of kernel.
dd start ; Kernel entry point (initial EIP).
[GLOBAL start] ; Kernel entry point.
[EXTERN kernel_main] ; This is the entry point of our C code
start:
; Load multiboot information:
push ebx
; Execute the kernel:
cli ; Disable interrupts.
call kernel_main ; call our main() function.
jmp $ ; Enter an infinite loop, to stop the processor
; executing whatever rubbish is in the memory
; after our kernel!

@ -0,0 +1,72 @@
#include "headers/common.h"
#include "headers/screen.h"
#include <stdint.h>
void memcpy(void *dest, void *src, unsigned int n)
{
// Typecast src and dest addresses to (char *)
char *csrc = (char *)src;
char *cdest = (char *)dest;
// Copy contents of src[] to dest[]
for (unsigned int i=0; i<n; i++)
cdest[i] = csrc[i];
}
void memset(void *s, int c, unsigned int n)
{
unsigned char* p=s;
while(n--)
*p++ = (unsigned char)c;
}
unsigned char inb(unsigned short port) {
/* a handy c wrapper funciton that reads a byte from the specificed port
"=a" (result) means: put al register in variable RESULT when finished
"d" (port) means: load EDX with port */
unsigned char result;
__asm__("in al, dx" : "=a" (result) : "d" (port));
return result;
}
void outb(unsigned short port, unsigned char data) {
// "a" (data) means: load EAX with data
// "d" (port) means: load EDX with port
__asm__("out dx, al" : : "a" (data), "d" (port));
}
unsigned short inw(unsigned short port) {
unsigned short result;
__asm__("in ax, dx" : "=a" (result) : "d" (port));
return result;
}
void outw(unsigned short port, unsigned short data) {
__asm__("out dx, ax" : : "a" (data), "d" (port));
}
void panic(const char *message, const char *file, uint32_t line) {
// We encountered a massive problem and have to stop.
asm volatile("cli"); // Disable interrupts.
//Print stack trace
kprintf("PANIC(%s) at %s : %d\n",message,file,line);
//Halt
for(;;);
}
void panic_assert(const char *file, uint32_t line, const char *desc) {
// An assertion failed, and we have to panic.
asm volatile("cli"); // Disable interrupts.
kprintf("ASSERTION-FAILED(%s) at %s : %d\n",desc,file,line);
// Halt by going into an infinite loop.
for(;;);
}

@ -0,0 +1,161 @@
//
// 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 "headers/common.h"
#include "headers/descriptor_tables.h"
#include "headers/isr.h"
#include <stdint.h>
// Lets us access our ASM functions from our C code.
extern void gdt_flush(uint32_t);
extern void idt_flush(uint32_t);
// Internal function prototypes.
static void init_gdt();
static void gdt_set_gate(int,uint32_t,uint32_t,uint8_t,uint8_t);
static void init_idt();
static void idt_set_gate(uint8_t,uint32_t,uint16_t,uint8_t);
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 it can nullified on start
extern isr_t interrupt_handlers[];
// Initialisation routine - zeroes all the interrupt service routines,
// initialises the GDT and IDT.
void init_descriptor_tables()
{
// Initialise the global descriptor table.
init_gdt();
init_idt();
//nullify all the interrupt handlers
memset(&interrupt_handlers,0,sizeof(isr_t)*256);
}
static void init_gdt()
{
gdt_ptr.limit = (sizeof(gdt_entry_t) * 5) - 1;
gdt_ptr.base = (uint32_t)&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((uint32_t)&gdt_ptr);
}
// Set the value of one GDT entry.
static void gdt_set_gate(int num, uint32_t base,uint32_t limit, uint8_t access, uint8_t 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 = (uint32_t)&idt_entries;
memset(&idt_entries, 0, sizeof(idt_entry_t)*256);
// 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, (uint32_t)isr0 , 0x08, 0x8E);
idt_set_gate( 1, (uint32_t)isr1 , 0x08, 0x8E);
idt_set_gate( 2, (uint32_t)isr2 , 0x08, 0x8E);
idt_set_gate( 3, (uint32_t)isr3 , 0x08, 0x8E);
idt_set_gate( 4, (uint32_t)isr4 , 0x08, 0x8E);
idt_set_gate( 5, (uint32_t)isr5 , 0x08, 0x8E);
idt_set_gate( 6, (uint32_t)isr6 , 0x08, 0x8E);
idt_set_gate( 7, (uint32_t)isr7 , 0x08, 0x8E);
idt_set_gate( 8, (uint32_t)isr8 , 0x08, 0x8E);
idt_set_gate( 9, (uint32_t)isr9 , 0x08, 0x8E);
idt_set_gate(10, (uint32_t)isr10, 0x08, 0x8E);
idt_set_gate(11, (uint32_t)isr11, 0x08, 0x8E);
idt_set_gate(12, (uint32_t)isr12, 0x08, 0x8E);
idt_set_gate(13, (uint32_t)isr13, 0x08, 0x8E);
idt_set_gate(14, (uint32_t)isr14, 0x08, 0x8E);
idt_set_gate(15, (uint32_t)isr15, 0x08, 0x8E);
idt_set_gate(16, (uint32_t)isr16, 0x08, 0x8E);
idt_set_gate(17, (uint32_t)isr17, 0x08, 0x8E);
idt_set_gate(18, (uint32_t)isr18, 0x08, 0x8E);
idt_set_gate(19, (uint32_t)isr19, 0x08, 0x8E);
idt_set_gate(20, (uint32_t)isr20, 0x08, 0x8E);
idt_set_gate(21, (uint32_t)isr21, 0x08, 0x8E);
idt_set_gate(22, (uint32_t)isr22, 0x08, 0x8E);
idt_set_gate(23, (uint32_t)isr23, 0x08, 0x8E);
idt_set_gate(24, (uint32_t)isr24, 0x08, 0x8E);
idt_set_gate(25, (uint32_t)isr25, 0x08, 0x8E);
idt_set_gate(26, (uint32_t)isr26, 0x08, 0x8E);
idt_set_gate(27, (uint32_t)isr27, 0x08, 0x8E);
idt_set_gate(28, (uint32_t)isr28, 0x08, 0x8E);
idt_set_gate(29, (uint32_t)isr29, 0x08, 0x8E);
idt_set_gate(30, (uint32_t)isr30, 0x08, 0x8E);
idt_set_gate(31, (uint32_t)isr31, 0x08, 0x8E);
idt_set_gate(32,(uint32_t)irq0,0x08,0x8E);
idt_set_gate(33,(uint32_t)irq1,0x08,0x8E);
idt_set_gate(34,(uint32_t)irq2,0x08,0x8E);
idt_set_gate(35,(uint32_t)irq3,0x08,0x8E);
idt_set_gate(36,(uint32_t)irq4,0x08,0x8E);
idt_set_gate(37,(uint32_t)irq5,0x08,0x8E);
idt_set_gate(38,(uint32_t)irq6,0x08,0x8E);
idt_set_gate(39,(uint32_t)irq7,0x08,0x8E);
idt_set_gate(40,(uint32_t)irq8,0x08,0x8E);
idt_set_gate(41,(uint32_t)irq9,0x08,0x8E);
idt_set_gate(42,(uint32_t)irq10,0x08,0x8E);
idt_set_gate(43,(uint32_t)irq11,0x08,0x8E);
idt_set_gate(44,(uint32_t)irq12,0x08,0x8E);
idt_set_gate(45,(uint32_t)irq13,0x08,0x8E);
idt_set_gate(46,(uint32_t)irq14,0x08,0x8E);
idt_set_gate(47,(uint32_t)irq15,0x08,0x8E);
idt_flush((uint32_t)&idt_ptr);
}
static void idt_set_gate(uint8_t num, uint32_t base, uint16_t sel, uint8_t 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 */;
}

28
gdt.s

@ -0,0 +1,28 @@
;
; 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

@ -0,0 +1,26 @@
#ifndef COMMON_H
#define COMMON_H
#include <stdint.h>
//extern void memcpy(void *dest, void *src, unsigned int n);
//extern void memset(void *s, int c, unsigned int n);
//void outb(u16int port, u8int value);
//u8int inb(u16int port);
//u16int inw(u16int port);
extern void memcpy(void *dest, void *src, unsigned int n);
extern void memset(void *s, int c, unsigned int n);
extern unsigned char inb(unsigned short port);
extern void outb(unsigned short port, unsigned char data);
extern unsigned short inw(unsigned short port);
extern void outw(unsigned short port, unsigned short data);
#endif
#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, uint32_t line);
extern void panic_assert(const char *file, uint32_t line, const char *desc);

@ -0,0 +1,114 @@
//
// 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"
#include <stdint.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
{
uint16_t limit_low; // The lower 16 bits of the limit.
uint16_t base_low; // The lower 16 bits of the base.
uint8_t base_middle; // The next 8 bits of the base.
uint8_t access; // Access flags, determine what ring this segment can be used in.
uint8_t granularity;
uint8_t 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
{
uint16_t limit; // The upper 16 bits of all selector limits.
uint32_t 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
{
uint16_t base_lo; // The lower 16 bits of the address to jump to when this interrupt fires.
uint16_t sel; // Kernel segment selector.
uint8_t always0; // This must always be zero.
uint8_t flags; // More flags. See documentation.
uint16_t 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
{
uint16_t limit;
uint32_t 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();
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();

@ -0,0 +1,40 @@
//
// 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"
#include <stdint.h>
typedef struct registers
{
uint32_t ds; // Data segment selector
uint32_t edi, esi, ebp, esp, ebx, edx, ecx, eax; // Pushed by pusha.
uint32_t int_no, err_code; // Interrupt number and error code (if applicable)
uint32_t eip, cs, eflags, useresp, ss; // Pushed by the processor automatically.
} registers_t;
#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
// enables registeration of callbacks for interrupts or irqs
// for irqs to ease confusin use the #defines ast the first param
typedef void (*isr_t)(registers_t*);
void register_interrupt_handler(uint8_t n, isr_t handler);

@ -0,0 +1,5 @@
#pragma once
#include "common.h"
void init_keyboard(void);

@ -0,0 +1,16 @@
#pragma once
#include "common.h"
#include <stdint.h>
#include <stddef.h>
uint32_t kmalloc(size_t sz, int align, uint32_t *phys);
//Allocate a chunk of memory, with size sz.
uint32_t kmalloc_a(size_t sz);
//Allocate a chunk of memory, with size sz. The physical address is returned as phys.
uint32_t kmalloc_p(size_t sz, uint32_t *phys);
//Allocate a chunk of memory, with size sz. The physical address is returned as phys. The memory must be page aligned.
uint32_t kmalloc_ap(size_t sz, uint32_t *phys);

@ -0,0 +1,47 @@
#pragma once
#include "common.h"
#include "isr.h"
#include <stdint.h>
typedef struct page
{
uint32_t present : 1; // page present in memory
uint32_t rw : 1; // ro if clear, rw if set
uint32_t user : 1; // supervisor level only if clear
uint32_t accessed : 1; // has been accessed since last referesh
uint32_t dirty : 1; // has been written to since last refresh
uint32_t unused : 7; // reserved and unused bits
uint32_t frame : 20; // frame address (right shift 12 bits)
} page_t;
typedef struct page_table
{
page_t pages[1024];
} page_table_t;
typedef struct
{
// array of pointers to pagetables
page_table_t *tables[1024];
// array of pointers to tables above but at the *physical* loc
// so they can be loaded into cr3
uint32_t tablesPhysical[1024];
uint32_t physicalAddr;
} page_directory_t;
// sets up paging
void initialize_paging(void);
// 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 requires
// if make == 1, if the pagetable in which page
// should reside inst there create it
page_t *get_page(uint32_t address, int make, page_directory_t *dir);
// handler for page faults
void page_fault(registers_t *regs);

@ -0,0 +1,45 @@
#ifndef __SCREEN_H
#define __SCREEN_H
#define VIDEO_ADDRESS 0xb8000
#define GREEN_ON_BLACK 0x02
#define FB_COMMAND_PORT 0x3D4
#define FB_DATA_PORT 0x3D5
// The I/O port commands
#define FB_HIGH_BYTE_COMMAND 14
#define FB_LOW_BYTE_COMMAND 15
enum vga_colors {
BLACK = 0,
BLUE = 1,
GREEN = 2,
CYAN = 3,
RED = 4,
MAGENTA = 5,
BROWN = 6,
LIGHT_GREY = 7,
DARK_GREY = 8,
LIGHT_BLUE = 9,
LIGHT_GREEN = 10,
LIGHT_CYAN = 11,
LIGHT_RED = 12,
LIGHT_MAGENTA = 13,
LIGHT_BROWN = 14,
WHITE = 15,
};
extern void set_current_color(enum vga_colors color);
extern void printchar(const char character);
extern void print(const char string[]);
extern void handle_scrolling();
extern void clear();
extern void set_cursor(int row, int col);
extern void itoa(int num, char *string2);
extern void tohex(int val, char *string);
extern void kprintf(const char *string, ...); // simple printf style function
void zerostring(char *string); // zero out a string needs refactor
extern unsigned int strlen(const char *string);
// end funciton prototypes
#endif

@ -0,0 +1,62 @@
#ifndef __SERIAL_H
#define __SERIAL_H
#define SERIAL_COM1_BASE 0x3F8 // com1 base port
extern void init_serial();
/** serial_configure_baud_rate:
* Sets the speed of the data being sent. The default speed of a serial
* port is 115200 bits/s. The argument is a divisor of that number, hence
* the resulting speed becomes (115200 / divisor) bits/s.
*
* @param com The COM port to configure
* @param divisor The divisor
*/
extern void serial_configure_baud_rate(unsigned short com, unsigned short divisor);
// configures line of a serial port
extern void serial_configure_line(unsigned short com);
// Bit: | 7 6 | 5 | 4 | 3 | 2 | 1 | 0 |
// Content: | lvl | bs | r | dma | clt | clr | e |
// 0xc7
/*
*Enables FIFO
*Clear both receiver and transmission FIFO queues
*Use 14 bytes as size of queue
*/
extern void serial_configure_buffers(unsigned short com);
// write data into the serial port
extern void serial_write(unsigned short com, char data);
// configure the modem of our serial port
// Bit: | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 |
// Content: | r | r | af | lb | ao2 | ao1 | rts | dtr |
extern void serial_configure_modem(unsigned short com);
/** serial_is_transmit_fifo_empty:
* Checks whether the transmit FIFO queue is empty or not for the given COM
* port.
*
* @param com The COM port
* @return 0 if the transmit FIFO queue is not empty
* 1 if the transmit FIFO queue is empty
*/
extern int serial_is_trasmit_fifo_empty(unsigned short com);
extern void serial_print(char *string); // print out over a serial port
// printf style func for serial port
void serial_printf(char *format, ...);
#endif

@ -0,0 +1,6 @@
#pragma once
#include "common.h"
#include <stdint.h>
void init_timer(uint32_t frequency);

@ -0,0 +1,159 @@
;
; 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
global isr%1
isr%1:
cli ; Disable interrupts firstly.
push byte 0 ; Push a dummy error code.
push byte %1 ; Push the interrupt number.
jmp isr_common_stub ; Go to our common handler code.
%endmacro
; This macro creates a stub for an ISR which passes it's own
; error code.
%macro ISR_ERRCODE 1
global isr%1
isr%1:
cli ; Disable interrupts.
push byte %1 ; Push the interrupt number
jmp isr_common_stub
%endmacro
%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_ERRCODE 17
ISR_NOERRCODE 18
ISR_NOERRCODE 19
ISR_NOERRCODE 20
ISR_NOERRCODE 21
ISR_NOERRCODE 22
ISR_NOERRCODE 23
ISR_NOERRCODE 24
ISR_NOERRCODE 25
ISR_NOERRCODE 26
ISR_NOERRCODE 27
ISR_NOERRCODE 28
ISR_NOERRCODE 29
ISR_ERRCODE 30
ISR_NOERRCODE 31
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
push esp ; registers_t *r
; c following sys v requires df to be clear on func entry
cld
call isr_handler
pop eax ; remove our pointer
pop eax ; 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
iret ; pops 5 things at once: CS, EIP, EFLAGS, SS, and ESP
; In isr.c
extern irq_handler
; 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 ; push edi,esi,ebp,esp,ebx,edx,ecx,eax
mov ax, ds ; lower 16-bits of eax = ds
push eax ; save the segment descriptor
mov ax, 0x10 ; load the kernel data segment descriptor
mov ds, ax
mov es, ax
mov fs, ax
mov gs, ax
push esp
cld
call irq_handler
pop ebx ; remove esp
pop ebx ; reload the original ds descriptor different than the isr code
mov ds, bx
mov es, bx
mov fs, bx
mov gs, bx
popa
add esp, 8 ;; cleans up the pushed error code and isr number
iret ; pops cs eip eflags ss and esp

@ -0,0 +1,5 @@
defualt=0
timeout=0
title ProjectRED
kernel /boot/kernel.elf

Binary file not shown.

50
isr.c

@ -0,0 +1,50 @@
//
// 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 "headers/common.h"
#include "headers/isr.h"
#include "headers/screen.h"
#include <stdint.h>
isr_t interrupt_handlers[256];
// This gets called from our ASM interrupt handler stub.
void isr_handler(registers_t *regs)
{
//kprintf("recieved interrupt: %d\n",regs->int_no);
if(interrupt_handlers[regs->int_no] != 0)
{
isr_t handler = interrupt_handlers[regs->int_no];
handler(regs);
}
}
void irq_handler(registers_t *regs)
{
//kprintf("recieved irq: %d\n",regs->int_no);
// send an EOI signal to pics if this
// interrupt involved the slave
if(regs->int_no >= 40)
{
outb(0xA0,0x20);
}
// send reset siginal to master. (as well as slave, if neccessary).
outb(0x20,0x20);
if(interrupt_handlers[regs->int_no] != 0)
{
isr_t handler = interrupt_handlers[regs->int_no];
handler(regs);
}
}
void register_interrupt_handler(uint8_t n, isr_t handler)
{
interrupt_handlers[n] = handler;
}

@ -0,0 +1,48 @@
#include "headers/isr.h"
#include "headers/common.h"
#include "headers/screen.h"
#include <stdbool.h>
#include <stdint.h>
// make an array for the keystate
// aswell as a struct for the state of toggle keys
// and other ones such as shift ctrl etc
// add polling of status bits to recieve input properly
extern uint8_t current_color;
static void keyboard_callback(registers_t *regs)
{
regs->eax++; // lul
uint8_t scancode = inb(0x60); // read from the 8042 PS/2 data port
static int i = 0;
static int j = 0;
j = j%15;
current_color = j++;
kprintf("keyboard event 0x%x : %d\n",scancode,scancode);
i++;
// check for key presses like shift
// else use a lookup table to print it dependant on the state
}
void init_keyboard(void)
{
// register our keyboard callback
register_interrupt_handler(IRQ1,&keyboard_callback);
// poll until buffer is empty
bool full = true;
while(full)
{
full = inb(0x64) & 1; // poll bit 1 of status reg
}
// send a scancode change request
outb(0x60,0xF0); //scan code change
outb(0x60,0x2); // set 2
}

@ -0,0 +1,40 @@
#include "headers/kheap.h"
#include <stdint.h>
#include <stddef.h>
// end is defined in the linker
extern uint32_t end;
uint32_t placement_address = (uint32_t)&end;
uint32_t kmalloc(size_t sz, int align, uint32_t *phys)
{
if( align == 1 && (placement_address & 0xFFFFF000)) // if not page aligned
{
placement_address &= 0xFFFFF000;
placement_address += 0x1000;
}
if(phys)
{
*phys = placement_address;
}
uint32_t tmp = placement_address;
placement_address += sz;
return tmp;
}
uint32_t kmalloc_a(size_t sz)
{
return kmalloc(sz,1,0);
}
uint32_t kmalloc_p(size_t sz, uint32_t *phys)
{
return kmalloc(sz,0,phys);
}
uint32_t kmalloc_ap(size_t sz, uint32_t *phys)
{
return kmalloc(sz,1,phys);
}

@ -0,0 +1,33 @@
/* Link.ld -- Linker script for the kernel - ensure everything goes in the */
/* Correct place. */
/* Original file taken from Bran's Kernel Development */
/* tutorials: http://www.osdever.net/bkerndev/index.php. */
ENTRY(start)
SECTIONS
{
.text 0x100000 :
{
code = .; _code = .; __code = .;
*(.text)
. = ALIGN(4096);
}
.data :
{
data = .; _data = .; __data = .;
*(.data)
*(.rodata)
. = ALIGN(4096);
}
.bss :
{
bss = .; _bss = .; __bss = .;
*(.bss)
. = ALIGN(4096);
}
end = .; _end = .; __end = .;
}

@ -0,0 +1,71 @@
// add something that instructs the compiler to ignore unused vars
// for ones that we aernt using yet
#include "headers/descriptor_tables.h"
#include "headers/screen.h"
#include "headers/serial.h"
#include "headers/timer.h"
#include "headers/keyboard.h"
#include "headers/paging.h"
#include <stdint.h>
#include <stddef.h>
void gpt(registers_t *regs)
{
regs->eax++;
PANIC("General protection fault");
}
extern uint8_t current_color;
int kernel_main(void)
{
register_interrupt_handler(13, &gpt);
//Prepare the screen, and make sure the colors are set proper.
clear();
set_current_color(WHITE);
print("Intializing kernel...\n");
//Initialize and test the serial port.
init_serial();
serial_print("Serial logging started!\n");
print("Serial Initialized.\n");
// Initialise all the ISRs and segmentation
init_descriptor_tables();
print("Descriptor tables Initialized.\n");
//Prepare paging. TODO: Finish Michael's implementation
//initialize_paging();
print("Paging ready.\n");
//Get the timer ready
//init_timer(0);
//Prepare the keyboard for taking input.#
asm("sti");
init_keyboard(); //<--- come back to after basic memory allocation
print("Keyboard ready.\n");
//This should cause a page fault and thus a panic.
volatile uint32_t *ptr = (uint32_t*)NULL;
kprintf("(0x%x)*ptr = %x\n",ptr,&ptr);
//Print our fancy colored message.
set_current_color(GREEN);
print("(c)");
set_current_color(WHITE);
print("Project");
set_current_color(RED);
print("RED");
set_current_color(WHITE);
print(", 2019\n");
//Hang.
for(;;) {}
return 0;
}

@ -0,0 +1,52 @@
C_SOURCES = $(wildcard *.c)
ASM_SOURCES = $(wildcard *.s)
HEADERS = $(wildcard headers/*.h)
#OBJECTS = loader.o ${C_SOURCES:.c=.o}
#OBJECTS = ${C_SOURCES:.c=.o ASM_SOURCES:.s=.o}
OBJECTS = ${ASM_SOURCES:.s=.o} ${C_SOURCES:.c=.o}
#OBJECTS = ${wildcard *.o}
CC = i686-elf-gcc
CFLAGS = -masm=intel -m32 -ffreestanding -fno-builtin -fno-stack-protector \
-isystem=/usr/include -Wall -Wextra -Werror -c
LDFLAGS = -T link.ld -melf_i386
AS = nasm
ASFLAGS = -f elf32
everything: all
rm -f os.iso
cp kernel.elf iso/boot/kernel.elf
genisoimage -R \
-b boot/grub/stage2_eltorito \
-no-emul-boot \
-A os \
-input-charset utf8 \
-quiet \
-boot-info-table \
-o os.iso \
iso
all: kernel.elf
kernel.elf: $(OBJECTS)
i686-elf-ld $(LDFLAGS) $(OBJECTS) -o kernel.elf
os.iso: kernel.elf
cp kernel.elf iso/boot/kernel.elf
genisoimage -R \
-b boot/grub/stage2_eltorito \
-no-emul-boot \
-A os \
-input-charset utf8 \
-quiet \
-boot-info-table \
-o os.iso \
iso
%.o: %.c ${HEADERS}
$(CC) $(CFLAGS) $< -o $@
%.o: %.s
$(AS) $(ASFLAGS) $< -o $@
clean:
rm *.o

@ -0,0 +1,218 @@
#include "headers/paging.h"
#include "headers/kheap.h"
#include "headers/screen.h"
#include "headers/serial.h"
#include <stdint.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
uint32_t *frames;
uint32_t nframes;
// defined in kheap.c
extern uint32_t placement_address;
// Macros for bitset algo's
#define INDEX_FROM_BIT(a) (a/(8*4))
#define OFFSET_FROM_BIT(a) (a%(8*4))
// set a bit in the frames bitset
static void set_frame(uint32_t frame_addr)
{
uint32_t frame = frame_addr/0x1000;
uint32_t idx = INDEX_FROM_BIT(frame);
uint32_t off = OFFSET_FROM_BIT(frame);
frames[idx] &= ~(0x1 << off);
}
// clear frame in a bitset
static void clear_frame(uint32_t frame_addr)
{
uint32_t frame = frame_addr/0x1000;
uint32_t idx = INDEX_FROM_BIT(frame);
uint32_t off = OFFSET_FROM_BIT(frame);
frames[idx] &= ~(0x1 << off);
}
/*// test if a bit is set
static u32int test_frame(uint32_t frame_addr)
{
uint32_t frame = frame_addr/0x1000;
uint32_t idx = INDEX_FROM_BIT(frame);
uint32_t off = OFFSET_FROM_BIT(frame);
return (frames[idx] & (0x1 << off));
} */
// find the first free frame
static uint32_t first_frame()
{
uint32_t i, j;
for(i = 0; i < INDEX_FROM_BIT(nframes); i++)
{
if(frames[i] != 0xFFFFFFFF) // nothing free exit early
{
// atleast one bit is free
for(j = 0; j < 32; j++)
{
uint32_t toTest = 0x1 << j;
if(!(frames[i]&toTest))
{
return i*4*8+j;
}
}
}
}
return -1;
}
// alloc a frame
void alloc_frame(page_t *page, int is_kernel, int is_writeable)
{
if(page->frame != 0)
{
return; // frame allready alloced
}
else
{
uint32_t idx = first_frame(); // idx now index for first free frame
if(idx == (uint32_t)-1)
{
PANIC("No free frames!");
}
set_frame(idx*0x1000); // frame is now ours
page->present = 1; // mark as present
page->rw = (is_writeable)?1:0; // should page be w
page->user = (is_kernel)?0:1; // should page be user-mode
page->frame = idx;
}
}
// function to dealloc a frame
void free_frame(page_t * page)
{
uint32_t frame;
if(!(frame=page->frame))
{
return; // page didnt actually have a alloced frame
}
else
{
clear_frame(frame); // free the frame
page->frame = 0x0; // page has no frame
}
}
void initialize_paging()
{
//size of physcial mem for now we assume it is 16MB
uint32_t mem_end_page = 0x1000000;
nframes = mem_end_page / 0x1000;
frames = (uint32_t*)kmalloc(INDEX_FROM_BIT(nframes),0,0);
memset(frames,0,INDEX_FROM_BIT(nframes));
// lets make a page directory <--- cont here
kernel_directory = (page_directory_t*)kmalloc_a(sizeof(page_directory_t));
memset(kernel_directory, 0, sizeof(page_directory_t));
current_directory = kernel_directory;
// my need to identify map (phys addr = virt addr from
// 0x0 to end of used mem so this can be accessed transparently
// as if paging isnt enabled
uint32_t i = 0;
while(i < placement_address)
{
// read but not writeable from user space
alloc_frame(get_page(i,1,kernel_directory),0,0);
i += 0x1000;
}
// before we enable paging set our page fault handler
register_interrupt_handler(14, &page_fault);
// enable paging! <-- should have a seperate function
// for the first time this is done and not constantly renable it
switch_page_directory(kernel_directory);
print("Paging enabled!\n");
}
void switch_page_directory(page_directory_t *dir)
{
current_directory = dir;
asm volatile("mov cr3, %0":: "r"(&dir->tablesPhysical));
uint32_t cr0;
asm volatile("mov %0, cr0": "=r"(cr0));
serial_printf("cr0 = %x\n",cr0);
cr0 |= 0x80000000; // Enable paging!
asm volatile("mov cr0, %0":: "r"(cr0));
}
page_t *get_page(uint32_t address, int make, page_directory_t *dir)
{
// turn address into an index
address /= 0x1000;
// find the page table that contains this address
uint32_t table_idx = address / 1024;
if(dir->tables[table_idx]) // if this page is allready assigned
{
return &dir->tables[table_idx]->pages[address%1024];
}
else if(make)
{
uint32_t 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;
}
}
// fix issue with page fault passing see
// Problem: Interrupt handlers corrupt interrupted state
// & Problem 3: regs var must called by reference instead of by value in the irq and isr handlers
void page_fault(registers_t *regs)
{
// a page fault has occured
// faulting address is in cr2
uint32_t faulting_address;
asm volatile("mov %0, cr2" : "=r" (faulting_address));
// the error codes 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
print("Page fault! ( ");
if(present) { print("present "); }
else if(rw) { print("read-only "); }
else if(us) { print("user-mode "); }
else if(id) { print("instr-fetch "); }
else if(reserved) {print("reserved "); }
kprintf(") at 0x%x\n",faulting_address);
PANIC("Page fault");
}

@ -0,0 +1,277 @@
#include "headers/screen.h"
#include "headers/common.h"
#include <stdbool.h>
#include <stdarg.h>
#include <stdint.h>
#include <stddef.h>
volatile uint16_t* vga_buffer = (uint16_t*) 0xB8000;
uint8_t current_color;
void set_current_color(enum vga_colors color) {
current_color = color;
}
/* Entries of the VGA Buffer take the form BBBBFFFFCCCCCCCC
* BBBB = Background Color
* FFFF = Foreground Color
* CCCCCCCC = Character code
*/
//Get the 8 color bits from the above enumerator
static inline uint8_t vga_entry_color(enum vga_colors fg, enum vga_colors bg) {
return fg | bg << 4; //Shift the background to the left, then OR with fg to add them together.
}
//Apply the color bits to make a printed character
static inline uint16_t vga_entry(unsigned char uc, uint8_t color) {
return (uint16_t) uc | (uint16_t) color << 8; //Same here - shift the color to the left, then OR with the character to append.
}
int i; // offset to framebuffer
uint8_t row; // current row between 1-25
uint8_t col; // current col between 1-80
short calc = 0; // random var for various calcs must be initialized or cleaned after func calls
// ^ attempt at packing may need calc to be an int however...
// simple printf style function(needs testing with errenous data)
// add tab support and %p specifier
// also add support for printing the register state
void kprintf(const char *string, ...) // look up how this is properly done
{
int j = 0;
int num = 0; // number to hold our stuff :)
char string2[11] = {0}; // string for printing ints :)
/* list stuff :) */
va_list ap;
// find how many things we are parsing
va_start(ap, string);
while(string[j] != 0)
{
if(string[j] == '%') // if a format specifier
{
if(string[j+1] == 'd') // print a decimal
{
num = va_arg(ap, int);
itoa(num, string2); // account for negatives...
print(string2);
zerostring(string2);
j += 2;
}
else if(string[j+1] == 'x') // print as hex
{
num = va_arg(ap, int);
tohex(num, string2);
print(string2);
zerostring(string2);
j += 2;
}
else if(string[j+1] == 's') // print a string
{
print(va_arg(ap, char*));
j += 2;
}
else if(string[j+1] == 'c') // print a char
{
char c = va_arg(ap,int);
printchar(c);
j += 2;
}
else // unknown format string
{
//serial_printf("[ERROR]attempted to parse unknown format string\n");
return; // prevent undefined behaviour
}
}
else // is a regular character
{
printchar(string[j]);
j++;
}
}
va_end(ap); // clean up variable length list
}
void printchar(char character) {
switch (character) {
case '\n': { //For newline, move to next row and focus on the first character.
col = 0;
row++;
break;
}
default: { // All other characters simply get written to the buffer.
const size_t i = (row * 80) + col; //i = index
vga_buffer[i] = ((uint16_t) current_color << 8) | character;
col++;
}
}
if(col > 79) // keep track of row and column for scrolling and newlines
{
col = 0;
row++;
}
if(row > 25)
{
handle_scrolling();
}
}
void print(const char *string) {
unsigned int len = strlen(string); // may be better to pass len as an arg
unsigned int j = 0;
while(j < len)
{
printchar(string[j]);
j++;
}
}
// Fill screen with spaces, effectively clearing the screen.
void clear() {
unsigned char *vga_memory = (unsigned char *) vga_buffer;
for(int j = 0; j < 4001; j = j + 2) // 80 * 25 * 2 =4000(end of framebuffer)
{
vga_memory[j] = 0x20; // assign a space to current pos
vga_memory[j + 1] = 0x00; // clear the attribute
}
row = 0;
col = 0;
i = 0;
}
// note learn how inline asm works properly
void set_cursor(int row, int col) {
unsigned short position=(row*80) + col;
outb(0x3D4, 0X0F);
outb(0x3D5, (unsigned char)(position&0xff));
outb(0x3D4, 0x0E);
outb(0x3D5, (unsigned char)((position>>8)&0xFF));
}
// scroll the screen when it gets to the last line
void handle_scrolling(){
int x = 160; // start of 2nd line
unsigned char *vga_memory = (unsigned char *) vga_buffer;