diff --git a/kernel/arch/idt/idt.c b/kernel/arch/idt/idt.c index 5be9f1a..1125d0e 100644 --- a/kernel/arch/idt/idt.c +++ b/kernel/arch/idt/idt.c @@ -37,6 +37,7 @@ void idt_init(void) vectors[vector] = true; } + // The "m" indicates actual data, not a pointer __asm__ volatile("lidt %0" : : "m"(idtr)); // load the new IDT __asm__ volatile("sti"); // set the interrupt flag } diff --git a/kernel/arch/io/io.s b/kernel/arch/io/io.s index 497bd93..567288e 100644 --- a/kernel/arch/io/io.s +++ b/kernel/arch/io/io.s @@ -18,3 +18,9 @@ inb: in al, dx ; read a byte from the I/O port and store it in the al register ret ; return the read byte + +global io_wait +io_wait: + mov al, 0x0 + out 0x80, al + ret diff --git a/kernel/arch/io/serial.c b/kernel/arch/io/serial.c index fcf0bd7..2b9028f 100644 --- a/kernel/arch/io/serial.c +++ b/kernel/arch/io/serial.c @@ -3,9 +3,7 @@ #include #include - - -#include "io.h" +#include /* I/O ports */ diff --git a/kernel/arch/io/tty.c b/kernel/arch/io/tty.c index 2536a64..49826e9 100644 --- a/kernel/arch/io/tty.c +++ b/kernel/arch/io/tty.c @@ -4,9 +4,9 @@ #include #include +#include #include "vga.h" -#include "io.h" /* I/O ports */ #define VGA_COMMAND_PORT 0x3D4 diff --git a/kernel/arch/pic/pic.c b/kernel/arch/pic/pic.c new file mode 100644 index 0000000..dfe1f55 --- /dev/null +++ b/kernel/arch/pic/pic.c @@ -0,0 +1,101 @@ +#include +#include + +void PIC_sendEOI(uint8_t irq) +{ + if (irq >= 8) // if we're over the PIC1 limit + outb(PIC2_COMMAND, PIC_EOI); + outb(PIC1_COMMAND, PIC_EOI); // if the IRQ came from the slave, it must go to both PICs +} + +void PIC_remap(int offset1, int offset2) +{ + // The io_wait calls are necessary for older machines, to give the PIC time to react + // + // After the init, the PIC requires 3 init words + // ICW2 // its vector offset + // ICW3 // how its wired to the master/slave + // ICW4 // additional info about the environment + + outb(PIC1_COMMAND, ICW1_INIT | ICW1_ICW4); // starts the init sequence, in cascade mode + io_wait(); + outb(PIC2_COMMAND, ICW1_INIT | ICW1_ICW4); + io_wait(); + outb(PIC1_DATA, offset1); // ICW2 - the offset for master + io_wait(); + outb(PIC2_DATA, offset2); // same as above for slave + io_wait(); + outb(PIC1_DATA, 4); // ICW3 - Tells master theres a slave at IRQ2 + io_wait(); + outb(PIC2_DATA, 2); // ICW3 - Tells slave the cascade identity + io_wait(); + + outb(PIC1_DATA, ICW4_8086); // ICW4 - Use 8086 mode (not 8080 mode) + io_wait(); + outb(PIC2_DATA, ICW4_8086); + io_wait(); + + // Unmask the PICs + outb(PIC1_DATA, 0); + outb(PIC2_DATA, 0); +} + +void pic_disable(void) +{ // Mask the PIC interrupts to disable them + outb(PIC1_DATA, 0xFF); + outb(PIC2_DATA, 0xFF); +} + +void IRQ_set_mask(uint8_t IRQline) // Masked IRQlines are ignored by the PIC, masked IRQ2 will fully ignore the slave +{ + uint16_t port; + uint8_t value; + + if (IRQline < 8) { + port = PIC1_DATA; + } else { + port = PIC2_DATA; + IRQline -= 8; + } + + value = inb(port) | (1 << IRQline); + outb(port, value); +} + +void IRQ_clear_mask(uint8_t IRQline) +{ + uint16_t port; + uint8_t value; + + if (IRQline < 8) { + port = PIC1_DATA; + } else { + port = PIC2_DATA; + IRQline -= 8; + } + + value = inb(port) & ~(1 << IRQline); + outb(port, value); +} + +static uint16_t __pic_get_irq_reg(int ocw3) +{ + /** OCW3 to PIC CMD to get the register values + * PIC2 is chained, and represents IRQs 8-1. + * PIC1 is IRQs 0-7, with 2 being the chain **/ + outb(PIC1_COMMAND, ocw3); + outb(PIC2_COMMAND, ocw3); + + return (inb(PIC2_COMMAND) << 8) | inb(PIC1_COMMAND); +} + +uint16_t pic_get_irr(void) +{ + return __pic_get_irq_reg(PIC_READ_IRR); +} + +uint16_t pic_get_isr(void) +{ + return __pic_get_irq_reg(PIC_READ_ISR); +} + diff --git a/kernel/arch/io/io.h b/kernel/include/kernel/x86/io.h similarity index 60% rename from kernel/arch/io/io.h rename to kernel/include/kernel/x86/io.h index 46bd467..9579303 100644 --- a/kernel/arch/io/io.h +++ b/kernel/include/kernel/x86/io.h @@ -6,7 +6,7 @@ * Sends the given data to the I/O port. Defined in io.s * * @param port The I/O port to send the data to - * @param data TThe data to send to the I/O port + * @param data The data to send to the I/O port */ void outb(unsigned short port, unsigned char data); @@ -19,6 +19,15 @@ void outb(unsigned short port, unsigned char data); */ unsigned char inb(unsigned short port); +/** + * io_wait: + * Wait for a very small amount of time (1 to 4 microseconds, generally) + * A simple imprecise wait. + * + * This performs an operation (sends 0) to port 0x80 + */ +void io_wait(void); + #endif diff --git a/kernel/include/kernel/x86/pic.h b/kernel/include/kernel/x86/pic.h new file mode 100644 index 0000000..19e063b --- /dev/null +++ b/kernel/include/kernel/x86/pic.h @@ -0,0 +1,59 @@ +#include + +#ifndef ARCH_PIC_H +#define ARCH_PIC_H + +/** PIC I/O ports **/ +#define PIC1 0x20 /** Master PIC **/ +#define PIC2 0xA0 /** Slave PIC **/ + +/** PIC helper defines **/ +#define PIC1_COMMAND (PIC1) +#define PIC1_DATA (PIC1 + 1) +#define PIC2_COMMAND (PIC2) +#define PIC2_DATA (PIC2 + 1) + +/** PIC Commands **/ +#define ICW1_ICW4 0x01 /** Indicates ICW4 will be present **/ +#define ICW1_SINGLE 0x02 /** Single (cascade mode) **/ +#define ICW1_INTERVAL4 0x04 /** Call address interval 4 (8) **/ +#define ICW1_LEVEL 0x08 /** Level triggered (edge) mode **/ +#define ICW1_INIT 0x10 /** Initialization **/ + +#define ICW4_8086 0x01 /** 8086/88 (MCS-80/85) mode **/ +#define ICW4_AUTO 0x02 /** Auto (normal) EOI **/ +#define ICW4_BUF_SLAVE 0x08 /** Buffered mode/slave **/ +#define ICW4_BUF_MASTER 0x0C /** Buffered mode/master **/ +#define ICW4_SFNM 0x10 /** Special fully nested (not) **/ + +#define PIC_EOI 0x20 /** End-of-interrupt command code **/ +#define PIC_READ_IRR 0x0a /** OCW3 irq ready next CMD read **/ +#define PIC_READ_ISR 0x0b /** OCW3 irq service next CMD read **/ + + +void PIC_sendEOI(uint8_t irq); + +/** + * PIC_remap: + * + * @param offset1 - Vector offset for master PIC + * @param offset2 - Vector offset for slave PIC + */ +void PIC_remap(int offset1, int offset2); + +void pic_disable(void); + +void IRQ_set_mask(uint8_t IRQline); +void IRQ_clear_mask(uint8_t IRQline); + +/** Returns the combined value of the cascaded PICs irq request register **/ +uint16_t pic_get_irr(void); +/** Returns the combined value of the cascaded PICs in-service register **/ +uint16_t pic_get_isr(void); + +/** + * TODO: implement handling for Spurious IRQs + * https://wiki.osdev.org/8259_PIC#Spurious_IRQs + */ + +#endif diff --git a/kernel/kmain.c b/kernel/kmain.c index fc0fe92..d207c6c 100644 --- a/kernel/kmain.c +++ b/kernel/kmain.c @@ -5,6 +5,7 @@ #include #include #include +#include #define GDT_SIZE 5 @@ -30,6 +31,9 @@ void kmain(void) gdt_init(); idt_init(); + PIC_remap(0x20, 0x28); + + terminal_initialize(); serial_initialize(); diff --git a/util/bochsrc.txt b/util/bochsrc.txt index e357d67..0039aed 100644 --- a/util/bochsrc.txt +++ b/util/bochsrc.txt @@ -8,3 +8,4 @@ log: bochslog.txt clock: sync=realtime, time0=local cpu: count=1, ips=1000000 com1: enabled=1, mode=file, dev=com1.out +keyboard: type=mf, serial_delay=150