diff --git a/kernel/arch/i386/boot.S b/kernel/arch/i386/boot.S index b97ef4b..7f12a4a 100644 --- a/kernel/arch/i386/boot.S +++ b/kernel/arch/i386/boot.S @@ -82,6 +82,17 @@ isr\num: jmp isr_common_stub .endm +// 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 num to +.global irq\num +irq\num: + cli + push $0x0 + push $0x\to + jmp irq_common_stub +.endm + ISR_NOERRCODE 0 // Divide by sero ISR_NOERRCODE 1 // Debug ISR_NOERRCODE 2 // Non-maskable Interrupt @@ -114,27 +125,69 @@ 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 isr_common_stub: pusha mov %ds,%ax push %eax mov $0x10,%ax - mov %eax,%ds - mov %eax,%es - mov %eax,%fs - mov %eax,%gs + mov %ax,%ds + mov %ax,%es + mov %ax,%fs + mov %ax,%gs call isr_handler pop %eax - mov %eax,%ds - mov %eax,%es - mov %eax,%fs - mov %eax,%gs + mov %ax,%ds + mov %ax,%es + mov %ax,%fs + mov %ax,%gs popa add $0x8,%esp sti iret - +//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 %ds, %ax //Lower 16-bits of eax = ds. + push %eax //save the data segment descriptor + + mov $0x10, %ax //load the kernel data segment descriptor + mov %ax, %ds + mov %ax, %es + mov %ax, %fs + mov %ax, %gs + + call irq_handler + + pop %ebx //reload the original data segment descriptor + mov %bx, %ds + mov %bx, %es + mov %bx, %fs + mov %bx, %gs + + popa //Pops edi,esi,ebp... + add $0x8, %esp //Cleans up the pushed error code and pushed ISR number + sti + iret diff --git a/kernel/kernel/early_kernel.c b/kernel/kernel/early_kernel.c index c81301f..0734e92 100644 --- a/kernel/kernel/early_kernel.c +++ b/kernel/kernel/early_kernel.c @@ -1,9 +1,12 @@ #include #include +#include #include "serial.h" #include "gdt.c" #include "interrupts.c" +#include "timer.c" +#include "paging.h" void kernel_early_main(void) { gdt_init(); @@ -11,7 +14,9 @@ void kernel_early_main(void) { serial_printf("gdt initialized!\n"); idt_init(); serial_printf("idt initialized!\n"); - //asm volatile("int $0x3"); - //asm volatile("int $0x4"); + init_timer(50); // Initialise timer to 50Hz + serial_printf("timer initialized!\n"); + init_paging(); + serial_printf("paging initialized!\n"); terminal_initialize(); } diff --git a/kernel/kernel/gdt.c b/kernel/kernel/gdt.c index f19a7c2..5b8da6f 100644 --- a/kernel/kernel/gdt.c +++ b/kernel/kernel/gdt.c @@ -20,7 +20,7 @@ typedef struct gdt_ptr_struct gdt_ptr_t; gdt_entry_t gdt_entries[5]; gdt_ptr_t gdt_ptr; -extern void gdt_flush(struct gdt_ptr* gdt_ptr_addr); +extern void gdt_flush(uint32_t); static void gdt_set_gate(int32_t num, uint32_t base, uint32_t limit, uint8_t access, uint8_t gran) { diff --git a/kernel/kernel/interrupts.c b/kernel/kernel/interrupts.c index 5265ebd..a2a77fc 100644 --- a/kernel/kernel/interrupts.c +++ b/kernel/kernel/interrupts.c @@ -1,3 +1,22 @@ +#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 + +//#include "asm_helper.h" + struct idt_entry_struct { uint16_t base_lo; // The lower 16 bits of the ISR's address @@ -29,88 +48,170 @@ void idt_set_gate(uint8_t num, uint32_t base, uint16_t sel, uint8_t flags) { void idt_init(void); void idt_init() { - idt_ptr.base = (uintptr_t)&idt_entries; - idt_ptr.limit = (uint16_t)sizeof(idt_entry_t) * 256 - 1; + 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); - - extern isr0; - extern isr1; - extern isr2; - extern isr3; - extern isr4; - extern isr5; - extern isr6; - extern isr7; - extern isr8; - extern isr9; - extern isr10; - extern isr11; - extern isr12; - extern isr13; - extern isr14; - extern isr15; - extern isr16; - extern isr17; - extern isr18; - extern isr19; - extern isr20; - extern isr21; - extern isr22; - extern isr23; - extern isr24; - extern isr25; - extern isr26; - extern isr27; - extern isr28; - extern isr29; - extern isr30; - extern isr31; + // 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); + + extern int isr0; + extern int isr1; + extern int isr2; + extern int isr3; + extern int isr4; + extern int isr5; + extern int isr6; + extern int isr7; + extern int isr8; + extern int isr9; + extern int isr10; + extern int isr11; + extern int isr12; + extern int isr13; + extern int isr14; + extern int isr15; + extern int isr16; + extern int isr17; + extern int isr18; + extern int isr19; + extern int isr20; + extern int isr21; + extern int isr22; + extern int isr23; + extern int isr24; + extern int isr25; + extern int isr26; + extern int isr27; + extern int isr28; + extern int isr29; + extern int isr30; + extern int isr31; + + extern int irq0; + extern int irq1; + extern int irq2; + extern int irq3; + extern int irq4; + extern int irq5; + extern int irq6; + extern int irq7; + extern int irq8; + extern int irq9; + extern int irq10; + extern int irq11; + extern int irq12; + extern int irq13; + extern int irq14; + extern int irq15; + idt_set_gate(0, (uint32_t)isr0, 0x08, 0x8E); - idt_set_gate(0, (uint32_t)isr1, 0x08, 0x8E); - idt_set_gate(0, (uint32_t)isr2, 0x08, 0x8E); - idt_set_gate(0, (uint32_t)isr3, 0x08, 0x8E); - idt_set_gate(0, (uint32_t)isr4, 0x08, 0x8E); - idt_set_gate(0, (uint32_t)isr5, 0x08, 0x8E); - idt_set_gate(0, (uint32_t)isr6, 0x08, 0x8E); - idt_set_gate(0, (uint32_t)isr7, 0x08, 0x8E); - idt_set_gate(0, (uint32_t)isr8, 0x08, 0x8E); - idt_set_gate(0, (uint32_t)isr9, 0x08, 0x8E); - idt_set_gate(0, (uint32_t)isr10, 0x08, 0x8E); - idt_set_gate(0, (uint32_t)isr11, 0x08, 0x8E); - idt_set_gate(0, (uint32_t)isr12, 0x08, 0x8E); - idt_set_gate(0, (uint32_t)isr13, 0x08, 0x8E); - idt_set_gate(0, (uint32_t)isr14, 0x08, 0x8E); - idt_set_gate(0, (uint32_t)isr15, 0x08, 0x8E); - idt_set_gate(0, (uint32_t)isr16, 0x08, 0x8E); - idt_set_gate(0, (uint32_t)isr17, 0x08, 0x8E); - idt_set_gate(0, (uint32_t)isr18, 0x08, 0x8E); - idt_set_gate(0, (uint32_t)isr19, 0x08, 0x8E); - idt_set_gate(0, (uint32_t)isr20, 0x08, 0x8E); - idt_set_gate(0, (uint32_t)isr21, 0x08, 0x8E); - idt_set_gate(0, (uint32_t)isr22, 0x08, 0x8E); - idt_set_gate(0, (uint32_t)isr23, 0x08, 0x8E); - idt_set_gate(0, (uint32_t)isr24, 0x08, 0x8E); - idt_set_gate(0, (uint32_t)isr25, 0x08, 0x8E); - idt_set_gate(0, (uint32_t)isr26, 0x08, 0x8E); - idt_set_gate(0, (uint32_t)isr27, 0x08, 0x8E); - idt_set_gate(0, (uint32_t)isr28, 0x08, 0x8E); - idt_set_gate(0, (uint32_t)isr29, 0x08, 0x8E); - idt_set_gate(0, (uint32_t)isr30, 0x08, 0x8E); - idt_set_gate(0, (uint32_t)isr31, 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); - - //__asm__ volatile ("lidt %0" : : "memory"(idt_ptr)); // load the new IDT - //__asm__ volatile ("sti"); // set the interrupt flag - - extern void idt_flush(uint32); + extern void idt_flush(uint32_t); idt_flush((uint32_t)&idt_ptr); - } -void isr_handler() +typedef struct registers { - serial_printf("Received interrupt\n"); + 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; + +typedef void (*isr_t)(registers_t); +isr_t interrupt_handlers[256]; + +void isr_handler(registers_t regs) +{ + printf("Received Interrupr\n"); + serial_printf("Received interrupt: %s\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) +{ + // 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); + } +} + +void register_interrupt_handler(uint8_t n, isr_t handler) +{ + interrupt_handlers[n] = handler; +} diff --git a/kernel/kernel/kernel.c b/kernel/kernel/kernel.c index 5846724..bfb7eb8 100644 --- a/kernel/kernel/kernel.c +++ b/kernel/kernel/kernel.c @@ -1,4 +1,5 @@ #include +#include void kernel_main(void) { printf("Hello from OrionOS!\n"); diff --git a/kernel/kernel/paging.h b/kernel/kernel/paging.h new file mode 100644 index 0000000..b5f0a44 --- /dev/null +++ b/kernel/kernel/paging.h @@ -0,0 +1,285 @@ +#ifndef PAGING_H +#define PAGING_H + +// A bitset of frames - used or free. +uint32_t *frames; +uint32_t nframes; + +/*extern */uint32_t placement_address; + +// Macros used in the bitset algorithms. +#define INDEX_FROM_BIT(a) (a/(8*4)) +#define OFFSET_FROM_BIT(a) (a%(8*4)) + +typedef struct page +{ + uint32_t present : 1; // Page present in memory + uint32_t rw : 1; // Read-only if clear, readwrite if set + uint32_t user : 1; // Supervisor level only if clear + uint32_t accessed : 1; // Has the page been accessed since last refresh? + uint32_t dirty : 1; // Has the page been written to since last refresh? + uint32_t unused : 7; // Amalgamation of unused and reserved bits + uint32_t 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. + */ + uint32_t tablesPhysical[1024]; + /* + The physical address of tablesPhysical. + */ + uint32_t physicalAddr; +} page_directory_t; + +uint32_t kmalloc(uint32_t sz) +{ + uint32_t tmp = placement_address; + placement_address += sz; + return tmp; +} + +uint32_t kmalloc_a(uint32_t sz) +{ + uint32_t tmp = placement_address; + placement_address += sz; + return tmp; +} + +/* +uint32_t kmalloc_a(uint32_t sz, int align) +{ + if (align == 1 && (placement_address & 0xFFFFF000)) // If the address is not already page-aligned + { + // Align it. + placement_address &= 0xFFFFF000; + placement_address += 0x1000; + } + uint32_t tmp = placement_address; + placement_address += sz; + return tmp; +} +*/ +uint32_t kmalloc_ap(uint32_t sz, int align, uint32_t *phys) +{ + if (align == 1 && (placement_address & 0xFFFFF000)) // If the address is not already page-aligned + { + // Align it. + 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(uint32_t sz); // page aligned. +uint32_t kmalloc_p(uint32_t sz, uint32_t *phys); // returns a physical address. +//uint32_t kmalloc_ap(uint32_t sz, uint32_t *phys); // page aligned and returns a physical address. +//uint32_t kmalloc(uint32_t sz); // vanilla (normal). + +/* + Handler for page faults. +*/ +void page_fault(registers_t regs){ + // A page fault has occurred. + // The faulting address is stored in the CR2 register. + uint32_t 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. + printf("Page fault: ( "); + if (present) {printf("present ");} + if (rw) {printf("read-only ");} + if (us) {printf("user-mode ");} + if (reserved) {printf("reserved ");} + printf(") at 0x%s\n", faulting_address); + panic("PAGE FAULT"); +} + +/* + 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(uint32_t address, int make, page_directory_t *dir){ + // Turn the address into an index. + address /= 0x1000; + // Find the page table containing this address. + uint32_t 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) + { + uint32_t tmp; + dir->tables[table_idx] = (page_table_t*)kmalloc_ap(sizeof(page_table_t), 1, &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 init_paging() { + + // The size of physical memory. For the moment we + // assume it is 16MB big. + uint32_t mem_end_page = 0x1000000; + + nframes = mem_end_page / 0x1000; + frames = (uint32_t*)kmalloc(INDEX_FROM_BIT(nframes)); + memset(frames, 0, INDEX_FROM_BIT(nframes)); + + // Let's make a page directory. + void* kernel_directory = (page_directory_t*)kmalloc_a(sizeof(page_directory_t)); + memset(kernel_directory, 0, sizeof(page_directory_t)); + void* current_directory = 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. + int i = 0; + while (i < placement_address) + { + // Kernel code is readable but not writeable from userspace. + alloc_frame( get_page(i, 1, kernel_directory), 0, 0); + i += 0x1000; + } + + // 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); +} + +/* + Causes the specified page directory to be loaded into the + CR3 register. +*/ +void switch_page_directory(page_directory_t *dir) { + void* current_directory = dir; + asm volatile("mov %0, %%eax":: "r"(&dir->tablesPhysical)); + asm("mov %eax, %cr3;"); + asm("mov %cr0, %eax;"); + asm("or 0x80000000, %eax;"); + asm("mov %eax, %cr0;"); +} + +// Static function to 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); +} + +// Static function to clear a bit in the frames 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); +} + +// Static function to test if a bit is set. +static uint32_t 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)); +} + +// Static function to 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. + { + // at least one bit is free here. + for (j = 0; j < 32; j++) + { + uint32_t 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; // Frame was already allocated, return straight away. + } + else + { + uint32_t idx = first_frame(); // idx is now the index of the first free frame. + if (idx == (uint32_t)-1) + { + panic("No free frames!"); + } + set_frame(idx*0x1000); // this frame is now ours! + page->present = 1; // Mark it as present. + page->rw = (is_writeable)?1:0; // Should the page be writeable? + page->user = (is_kernel)?0:1; // Should the page be user-mode? + page->frame = idx; + } +} + +// Function to deallocate a frame. +void free_frame(page_t *page) +{ + uint32_t frame; + if (!(frame=page->frame)) + { + return; // The given page didn't actually have an allocated frame! + } + else + { + clear_frame(frame); // Frame is now free again. + page->frame = 0x0; // Page now doesn't have a frame. + } +} +#endif diff --git a/kernel/kernel/timer.c b/kernel/kernel/timer.c new file mode 100644 index 0000000..703db7e --- /dev/null +++ b/kernel/kernel/timer.c @@ -0,0 +1,27 @@ +#include +//#include "asm_helper.h" +//#include "interrupts.c" + +uint32_t tick = 0; + +static void timer_callback(registers_t regs) +{ + tick++; + printf("Tick: %s\n", tick); + serial_printf("Tick: %s\n", tick); +} + +void init_timer(uint32_t frequency) +{ + register_interrupt_handler(IRQ0, &timer_callback); + + uint32_t divisor = 1193180 / frequency; + + outb(0x43, 0x36); + + uint8_t l = (uint8_t)(divisor & 0xFF); + uint8_t h = (uint8_t)( (divisor>>8) & 0xFF ); + + outb(0x40, l); + outb(0x40, h); +}