commit f6ba6aa117d8a25ee7b6848c0e5343ef57e648f8 Author: Jenny Curle Date: Mon Apr 1 01:38:50 2019 +0100 Legacy OS files. Have an i686-elf-gcc & -ld in your PATH, and the Makefile will take care of the rest. diff --git a/boot.s b/boot.s new file mode 100644 index 0000000..dece606 --- /dev/null +++ b/boot.s @@ -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! diff --git a/common.c b/common.c new file mode 100644 index 0000000..75d463c --- /dev/null +++ b/common.c @@ -0,0 +1,72 @@ +#include "headers/common.h" +#include "headers/screen.h" +#include + +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 + + +// 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 */; +} + + + + diff --git a/gdt.s b/gdt.s new file mode 100644 index 0000000..36cc6ac --- /dev/null +++ b/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 diff --git a/headers/common.h b/headers/common.h new file mode 100644 index 0000000..b4e3eb0 --- /dev/null +++ b/headers/common.h @@ -0,0 +1,26 @@ +#ifndef COMMON_H +#define COMMON_H +#include + + + +//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); diff --git a/headers/descriptor_tables.h b/headers/descriptor_tables.h new file mode 100644 index 0000000..1f9f37a --- /dev/null +++ b/headers/descriptor_tables.h @@ -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 + + +// 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(); + diff --git a/headers/isr.h b/headers/isr.h new file mode 100644 index 0000000..27d270b --- /dev/null +++ b/headers/isr.h @@ -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 + +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); + diff --git a/headers/keyboard.h b/headers/keyboard.h new file mode 100644 index 0000000..7b358a3 --- /dev/null +++ b/headers/keyboard.h @@ -0,0 +1,5 @@ +#pragma once + +#include "common.h" + +void init_keyboard(void); diff --git a/headers/kheap.h b/headers/kheap.h new file mode 100644 index 0000000..d617fab --- /dev/null +++ b/headers/kheap.h @@ -0,0 +1,16 @@ +#pragma once +#include "common.h" +#include +#include + + +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); diff --git a/headers/paging.h b/headers/paging.h new file mode 100644 index 0000000..07aaad5 --- /dev/null +++ b/headers/paging.h @@ -0,0 +1,47 @@ +#pragma once + +#include "common.h" +#include "isr.h" +#include + +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); diff --git a/headers/screen.h b/headers/screen.h new file mode 100644 index 0000000..fedf48e --- /dev/null +++ b/headers/screen.h @@ -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 diff --git a/headers/serial.h b/headers/serial.h new file mode 100644 index 0000000..5f0858d --- /dev/null +++ b/headers/serial.h @@ -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 diff --git a/headers/timer.h b/headers/timer.h new file mode 100644 index 0000000..ba0dfff --- /dev/null +++ b/headers/timer.h @@ -0,0 +1,6 @@ +#pragma once + +#include "common.h" +#include + +void init_timer(uint32_t frequency); diff --git a/interrupt.s b/interrupt.s new file mode 100644 index 0000000..1ef3aad --- /dev/null +++ b/interrupt.s @@ -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 + diff --git a/iso/boot/grub/menu.lst b/iso/boot/grub/menu.lst new file mode 100644 index 0000000..387d721 --- /dev/null +++ b/iso/boot/grub/menu.lst @@ -0,0 +1,5 @@ +defualt=0 +timeout=0 + +title ProjectRED +kernel /boot/kernel.elf diff --git a/iso/boot/grub/stage2_eltorito b/iso/boot/grub/stage2_eltorito new file mode 100644 index 0000000..9e1617c Binary files /dev/null and b/iso/boot/grub/stage2_eltorito differ diff --git a/isr.c b/isr.c new file mode 100644 index 0000000..163a05c --- /dev/null +++ b/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 + +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; +} diff --git a/keyboard.c b/keyboard.c new file mode 100644 index 0000000..0f6075e --- /dev/null +++ b/keyboard.c @@ -0,0 +1,48 @@ +#include "headers/isr.h" +#include "headers/common.h" +#include "headers/screen.h" +#include +#include +// 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 +} diff --git a/kheap.c b/kheap.c new file mode 100644 index 0000000..37faec3 --- /dev/null +++ b/kheap.c @@ -0,0 +1,40 @@ +#include "headers/kheap.h" +#include +#include + +// 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); +} diff --git a/link.ld b/link.ld new file mode 100644 index 0000000..bbb2ee0 --- /dev/null +++ b/link.ld @@ -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 = .; +} diff --git a/main.c b/main.c new file mode 100644 index 0000000..4e3160b --- /dev/null +++ b/main.c @@ -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 +#include + + +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; +} diff --git a/makefile b/makefile new file mode 100644 index 0000000..ea2d8a6 --- /dev/null +++ b/makefile @@ -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 diff --git a/paging.c b/paging.c new file mode 100644 index 0000000..7713690 --- /dev/null +++ b/paging.c @@ -0,0 +1,218 @@ + #include "headers/paging.h" +#include "headers/kheap.h" +#include "headers/screen.h" +#include "headers/serial.h" +#include + +// 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"); +} + + + + + diff --git a/screen.c b/screen.c new file mode 100644 index 0000000..161286f --- /dev/null +++ b/screen.c @@ -0,0 +1,277 @@ +#include "headers/screen.h" +#include "headers/common.h" + +#include +#include +#include +#include + +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; + + + + while(x < 4001) { // max each char is 2 apart + vga_memory[x-160] = vga_memory[x]; //Move every character up a line + vga_memory[x-159] = vga_memory[x+1]; //Move the character attributes too + x += 2; //Move to next character + } + x = 3840; //Start of the last line + + for(i = 1920; i < 2001; i++) { // Memory locations for the characters in the last line + vga_memory[x] = 0; //Set current character to 0 (null) + x += 2; // Move to next character + } + + i = 3840; // set i to start of last line + row = 24; // set row to 25 so when a \n is printed the screen will scroll +} + + +void itoa(int num, char *string2) {// convert from int to ascii eqiv + + int j = 0; // counter + calc = 0; //Result of a calculation + bool flag_negative = false; + + + if(num == 0) { // catch 0 + string2[0] = '0'; + string2[1] = '\0'; // null term + return; + } + + if(num < 0) { + flag_negative = true; + num = -num; //invert it so it calcs + } + + while(num != 0) {// while we can still divide + + calc = num % 10; // get raminder in calc + num = num / 10; // div by 10 + calc += 48; // convert to ascii + //printchar(calc); + string2[j] = calc; + j++; // inc amount of chars to print + + } + if(flag_negative) { + string2[j] = '-'; + j += 1; + } + + for(int i=0; i> 4; // shr val four so that it loses char + if(temp >= 10) { + temp += 7; // if higher than 10 convert to letter + } + temp += 48; //convert to ascii + string[j] = temp; + } +} + +// Fill the string pointer with 0 (null) characters +void zerostring(char *string) { + unsigned int len = strlen(string); + for(unsigned int i = 0; i <= len; i++) + { + string[i] = '\0'; + } +} + + +unsigned int strlen(const char *string) { + unsigned int len = 0; + while(string[len]) + { + len++; + } + + return len; +} + +// print out registers for debugging +/*void writeregs(void) +{ + unsigned int Register = 0; + + + + +}*/ diff --git a/serial.c b/serial.c new file mode 100644 index 0000000..137b557 --- /dev/null +++ b/serial.c @@ -0,0 +1,171 @@ +#include "headers/common.h" +#include "headers/screen.h" // for conversion functions(maybye put in seperate header) +#include + +#define SERIAL_DATA_PORT(base) (base) +#define SERIAL_FIFO_COMMAND_PORT(base) (base + 2) +#define SERIAL_LINE_COMMAND_PORT(base) (base + 3) +#define SERIAL_MODEM_COMMAND_PORT(base) (base + 4) +#define SERIAL_LINE_STATUS_PORT(base) (base + 5) +#define SERIAL_COM1_BASE 0x3F8 // com1 base port + +// SERIAL_LINE_ENABLE_DLAB: +// tells the serial port to expect high byte first +// then the low order byte follows + +#define SERIAL_LINE_ENABLE_DLAB 0x80 + + +/** 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 +*/ + +void serial_configure_baud_rate(unsigned short com, unsigned short divisor) +{ + outb(SERIAL_LINE_COMMAND_PORT(com), + SERIAL_LINE_ENABLE_DLAB); + outb(SERIAL_DATA_PORT(com), + (divisor >> 8) & 0x00ff); + outb(SERIAL_DATA_PORT(com), + divisor & 0x00ff); +} + +// configures line of a serial port +void serial_configure_line(unsigned short com) +{ + /* Bit: | 7 | 6 | 5 4 3 | 2 | 1 0 | + * Content: | d | b | prty | s | dl | + * Value: | 0 | 0 | 0 0 0 | 0 | 1 1 | = 0x03 + */ + outb(SERIAL_LINE_COMMAND_PORT(com), 0x03); + +} + + + +// 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 +*/ +void serial_configure_buffers(unsigned short com) +{ + outb(SERIAL_FIFO_COMMAND_PORT(com), 0xc7); + +} + +// 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 | +void serial_configure_modem(unsigned short com) +{ + outb(SERIAL_MODEM_COMMAND_PORT(com), 0x3); + +} + +/** 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 +*/ +int serial_is_trasmit_fifo_empty(unsigned short com) +{ + return inb(SERIAL_LINE_STATUS_PORT(com)) & 0x20; +} + +void serial_write(unsigned short com, char data) +{ + + while(serial_is_trasmit_fifo_empty(com) == 0); // wait till write finished + outb(com, data); +} + + +void serial_print(char *string) +{ + int j = 0; + while(string[j] != 0) + { + serial_write(0x3f8, string[j]); + j++; + } +} + +void serial_printf(const char *format, ...) // printf style func for serial port +{ + int j = 0; + int num; // number to hold our stuff :) + char string2[10] = {0}; // string for printing ints :) + /* list stuff :) */ + va_list ap; + va_start(ap, format); + + + while(format[j] != 0) + { + if(format[j] == '%') // if a format specifier + { + if(format[j+1] == 'd') + { + num = va_arg(ap, int); + itoa(num, string2); + serial_print(string2); + zerostring(string2); + j += 2; + } + + else if(format[j+1] == 'x') + { + num = va_arg(ap, int); + tohex(num, string2); + serial_print(string2); + zerostring(string2); + j += 2; + } + + else if(format[j+1] == 's') + { + serial_print(va_arg(ap, char* )); + j += 2; + } + + else // unknown format string + { + serial_print("[ERROR]attempted to parse unknown format string\n"); + return; // prevent undefined behaviour + } + } + + else // is a regular character + { + serial_write(0x3f8,format[j]); + j++; + } + } + va_end(ap); // clean up variable length list +} + +// init our serial com1 port for debuggin purpoes(rest are general use functions for other ports) +#define PORT 0x3f8 /* COM1 */ + +void init_serial() +{ + outb(PORT + 1, 0x00); // Disable all interrupts + outb(PORT + 3, 0x80); // Enable DLAB (set baud rate divisor) + outb(PORT + 0, 0x03); // Set divisor to 3 (lo byte) 38400 baud + outb(PORT + 1, 0x00); // (hi byte) + outb(PORT + 3, 0x03); // 8 bits, no parity, one stop bit + outb(PORT + 2, 0xC7); // Enable FIFO, clear them, with 14-byte threshold + outb(PORT + 4, 0x0B); // IRQs enabled, RTS/DSR set +} diff --git a/timer.c b/timer.c new file mode 100644 index 0000000..9346269 --- /dev/null +++ b/timer.c @@ -0,0 +1,39 @@ +#include "headers/timer.h" +#include "headers/isr.h" +#include "headers/screen.h" +#include + +uint32_t tick = 0; +extern uint8_t current_color; +static void timer_callback(registers_t *regs) +{ + regs->eax++; // get it to compile fix this + // later(regs is not really needed) + tick++; + static int j = 0; + j = j%15; + current_color = j++; + kprintf("%d\n", tick); +} + +void init_timer(uint32_t frequency) +{ + // firstly register our timer callback + register_interrupt_handler(IRQ0, &timer_callback); + + // the vaue sent to the pit is its clock divisor + // (1193180 Hz) the divisor must be small enough + // to fit into 16 bits + uint32_t divisor = 1193180 / frequency; + + // send the command byte + outb(0x43, 0x36); + + // divsor is set in bytes so serialize it + uint8_t l = (uint8_t)(divisor & 0xff); + uint8_t h = (uint8_t)( (divisor>>8) & 0xff); + + // send the freq divisor + outb(0x40, l); + outb(0x40, h); +}