From c2f8ccdf2e1ccb5976a43d5dc14fb2d53b0170ff Mon Sep 17 00:00:00 2001 From: c0repwn3r Date: Sat, 14 May 2022 12:42:37 -0400 Subject: [PATCH] Interrupts mostly working, frame seems to be offset by one tho --- bin/shade.bin | Bin 15776 -> 22504 bytes bin/shade.iso | Bin 26597376 -> 26603520 bytes include/shade/platform/gdt.h | 9 ++ include/shade/platform/interrupts/idt.h | 38 +++++++ include/shade/platform/interrupts/isr.h | 45 ++++++++ include/shade/platform/interrupts/pic.h | 40 +++++++ include/shade/platform/ports.h | 15 ++- include/shade/util.h | 1 - include/strings.h | 7 ++ obj/kernel/kernel.o | Bin 3160 -> 3496 bytes obj/kernel/print.o | Bin 3952 -> 3936 bytes obj/kernel/util.o | Bin 1616 -> 1336 bytes src/kernel/kernel.c | 8 ++ src/kernel/platform/interrupts/idt.c | 33 ++++++ src/kernel/platform/interrupts/int.asm | 145 ++++++++++++++++++++++++ src/kernel/platform/interrupts/isr.c | 24 ++++ src/kernel/platform/interrupts/pic.c | 79 +++++++++++++ src/kernel/platform/ports.c | 12 +- src/kernel/print.c | 8 +- src/kernel/util.c | 12 -- src/libc/strings.c | 1 + 21 files changed, 452 insertions(+), 25 deletions(-) create mode 100644 include/shade/platform/gdt.h create mode 100644 include/shade/platform/interrupts/idt.h create mode 100644 include/shade/platform/interrupts/isr.h create mode 100644 include/shade/platform/interrupts/pic.h create mode 100644 src/kernel/platform/interrupts/idt.c create mode 100644 src/kernel/platform/interrupts/int.asm create mode 100644 src/kernel/platform/interrupts/isr.c create mode 100644 src/kernel/platform/interrupts/pic.c diff --git a/bin/shade.bin b/bin/shade.bin index 567cd2c278a553e4d3ab3b470fbfa6e4cdd2e223..3d2c531911cb5ef9092a5c961e4e199a5273ab53 100755 GIT binary patch literal 22504 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 delta 2156 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 z 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 Y-J+F3&sRaaQBcMges8T*$YSIF12~*w!T +#include +#include + +#define IDT_DESCRIPTORS 256 + +extern void* isr_stub_table[]; + +typedef struct idt_entry_struct { + uint16_t isr_low; // Low 16 bits of handler address + uint16_t kernel_cs; // The selector in the GDT that the CPU will load into CS before calling the handler + uint8_t ist; // The IST in the TSS that the CPU will load into RSP; set to zero for now + uint8_t attributes; // Type and attributes + uint16_t isr_mid; // Middle 16 bits of handler address + uint32_t isr_high; // Upper 32 bits of handler address + uint32_t reserved; // Reserved, set to 0 +} __attribute__((packed)) idt_descriptor_t; + +typedef struct idt_ptr_struct { + uint16_t limit; + uint64_t base; +} __attribute__((packed)) idt_ptr_t; + +__attribute__((aligned(0x10))) +static idt_descriptor_t idt[IDT_DESCRIPTORS]; +static idt_ptr_t idt_ptr; +static bool vectors[IDT_DESCRIPTORS]; + +void idt_set_gate(uint8_t index, void* isr, uint8_t flags); +void idt_assemble(); + +void idt_enable_interrupts(); +void idt_disable_interrupts(); + +#endif \ No newline at end of file diff --git a/include/shade/platform/interrupts/isr.h b/include/shade/platform/interrupts/isr.h new file mode 100644 index 0000000..1f341d8 --- /dev/null +++ b/include/shade/platform/interrupts/isr.h @@ -0,0 +1,45 @@ +#ifndef ISR_H +#define ISR_H + +#include + +#define ERR_MAX 5 + +static int err_count = 0; + +typedef struct { + struct { + uint64_t cr4; + uint64_t cr3; + uint64_t cr2; + uint64_t cr0; + } control_registers; + + struct { + uint64_t rdi; + uint64_t rsi; + uint64_t rdx; + uint64_t rcx; + uint64_t rbx; + uint64_t rax; + } general_registers; + + struct { + uint64_t rbp; + uint64_t vector; + uint64_t error_code; + uint64_t rip; + uint64_t cs; + uint64_t rflags; + uint64_t rsp; + uint64_t dss; + } base_frame; +} isr_xframe_t; + +__attribute__((noreturn)) +void exception_handler(isr_xframe_t frame); + +__attribute__((noreturn)) +void interrupt_handler(isr_xframe_t frame); + +#endif \ No newline at end of file diff --git a/include/shade/platform/interrupts/pic.h b/include/shade/platform/interrupts/pic.h new file mode 100644 index 0000000..568e15a --- /dev/null +++ b/include/shade/platform/interrupts/pic.h @@ -0,0 +1,40 @@ +#ifndef PIC_H +#define PIC_H + +#include + +#define PIC1 0x20 /* IO base address for master PIC */ +#define PIC2 0xA0 /* IO base address for slave PIC */ +#define PIC1_COMMAND PIC1 +#define PIC1_DATA (PIC1+1) +#define PIC2_COMMAND PIC2 +#define PIC2_DATA (PIC2+1) + +#define PIC_EOI 0x20 /* End-of-interrupt command code */ + +#define ICW1_ICW4 0x01 /* ICW4 (not) needed */ +#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 - required! */ + +#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_READ_IRR 0x0a /* OCW3 irq ready next CMD read */ +#define PIC_READ_ISR 0x0b /* OCW3 irq service next CMD read */ + + +void pic_send_eoi(uint8_t irq); +void pic_remap(int offset1, int offset2); +void pic_mask_irq(uint8_t irq); +void pic_unmask_irq(uint8_t irq); +static uint16_t __pic_get_irq_reg(int ocw3); +uint16_t pic_get_irr(); +uint16_t pic_get_isr(); + + +#endif \ No newline at end of file diff --git a/include/shade/platform/ports.h b/include/shade/platform/ports.h index d616f0e..b5b0524 100644 --- a/include/shade/platform/ports.h +++ b/include/shade/platform/ports.h @@ -1,4 +1,11 @@ -unsigned char port_byte_in(unsigned short port); -void port_byte_out(unsigned short port, unsigned char data); -unsigned short port_word_in(unsigned short port); -void port_word_out(unsigned short port, unsigned short data); \ No newline at end of file +#ifndef PORTS_H +#define PORTS_H + +unsigned char inb(unsigned short port); +void outb(unsigned short port, unsigned char data); +unsigned short inw(unsigned short port); +void outw(unsigned short port, unsigned short data); + +void io_wait(); + +#endif \ No newline at end of file diff --git a/include/shade/util.h b/include/shade/util.h index 889ccb0..7ffca01 100644 --- a/include/shade/util.h +++ b/include/shade/util.h @@ -3,6 +3,5 @@ void memcpy(char *source, char *dest, int nbytes); void memset(char *dest, char val, int len); -void int_to_ascii(int n, char str[]); #endif \ No newline at end of file diff --git a/include/strings.h b/include/strings.h index ee4402a..8fca198 100644 --- a/include/strings.h +++ b/include/strings.h @@ -1,10 +1,17 @@ #ifndef STRINGS_H #define STRINGS_H +#include + void itoa(int n, char str[]); void strrev(char s[]); int strlen(char s[]); char* strcpy(char* strDest, const char* strSrc); char* strcnc(char* str1, char* str2); +char* u8_hex(const uint8_t x); +char* u16_hex(const uint16_t x); +char* u32_hex(const uint32_t x); +char* u64_hex(const uint64_t x); + #endif \ No newline at end of file diff --git a/obj/kernel/kernel.o b/obj/kernel/kernel.o index 66bfda775dcffa94bce1fa61453a1d4d4684619e..aec1acb5e90e4b9b11fded44fd38cba856b1b10f 100644 GIT binary patch delta 748 zcmY*VJxClu6rS0?x!Jun=c%lR5?8pOXmO@Gh;XNvg9w3CfpoHZiynAp*;_pgNns-) zq&R&RR$?iNg|P}DNfodWOqV76aoOw7j zipi%zgwSVqeXmzLQO<8*RqZ^Q!%dv6EO^uNe$}llM1B}922lfkC=UEqB72>cpz*fM znlfP<&`gx6YkZkB0T zX891CkeJWTb>V9YTobq{aGN8XYP}t`YJ}LD3Zf7#`7_b;umR_q-xJhkrb55&1w!>2 z4ZqGFE84>o{hrP5dHpwz9C*ZmEj^D-_^3NDPtx#9WV%tf<$Nlf&kXK-WeBIeoZOlJ zO&Si20@J?7+6KwrD&G_3k4QR~md1aT8ni*3IQAV$y%06KB#$$2LL5xtjPMa%DUOv? z!IPgdl6*72OuN+Ofy8GezMSy9hsstn$sbDo%+X>>#rQF-&>|f(n2SXV-qRv}3zxK< z+eyqWpD2Y3ljZ>ZufBm5v#1QI@XjnLV=7#kB^3Wef9D+Uy-}K6Eg=UXOY$cWp&!j8+(HPqwjA!_P>T))wzLJpw-z)s zNDvf`5%hzDLy$wz5L82RQ_v99IrlbP-{C#qIp=xb;p(!ZPWniN<2r2xeu&1Rkfpg8m z6nf75rKN`W>JYz71Yajsn0h*!9)`iO!#oht`9X%;&O9WAIck~A(=&i=%>tVma6}CA zbBH~pDQv%P~1*t{h&d3K_vP)I|vpTWWb7`qUA L;i@?=FFxZB8W@G# diff --git a/obj/kernel/print.o b/obj/kernel/print.o index 99394ea7f4e2c60ca10842cf60a5ffebeae36ebf..34e575331cdaac34c7e52d7dca0b638aad95b181 100644 GIT binary patch delta 76 zcmew$_dsrf2BX48&2~1%s>vJKdW7;zOOhC}Q;YIabK-N0)8q5AH^;MQvN8s2KFPD6 dk?9B5@ ovp;(#D`Uduojm&)nK-y7SMjwmPMplhZ_asw8=@j*G9#}%0H-w?`Tzg` diff --git a/obj/kernel/util.o b/obj/kernel/util.o index 7032cdd476eae9121859a242ab2c19aedca5c371..242f41f02f5e253bcced1dff3697e295afd903e5 100644 GIT binary patch delta 170 zcmcb>vx94b22%yoM6Kx}4DQa(Rtg&KexaHQhDLgZdIk&(lY1GvCr@FPm>j^AF!=t+Vg{n2(0TFTPo|lj7AG_>bh^H2u6@H$KMicP z7#0(bG3WwC5hfmEV3@d1V&aVsIT@e}V?fRXf?XiOkx!tF$(fgpX)6yq$K*yPsmT?L z34&rk86gHnhDMMQAlNneBBLySW?o5rNq&4{adKuRP;l~FMq|MQkTei5L2=_`Nv8cw z){K*HF_|${Gfqxq6q~HUtN~+YK$w%5?U*JoPQJu!$9aGete>G`vLuTI2gn#8@R%IQ fV$Nv+ #include +#include +#include void kernel_welcome() { print_str("Welcome to "); @@ -43,4 +45,10 @@ void kmain() { print_str("This program is provided \"as-is\" and no express or implied warranty is provided.\n"); print_str("The full license can be found at /sys/LICENCE on this system or ./LICENCE in the source tree.\n"); + pic_remap(0x20, 0x28); + idt_assemble(); + kernel_msg_ok("Enabled interrupts"); + __asm__ __volatile__("int $2"); + + for (;;) {} } \ No newline at end of file diff --git a/src/kernel/platform/interrupts/idt.c b/src/kernel/platform/interrupts/idt.c new file mode 100644 index 0000000..4f79e3e --- /dev/null +++ b/src/kernel/platform/interrupts/idt.c @@ -0,0 +1,33 @@ +#include + +void idt_set_gate(uint8_t index, void* isr, uint8_t flags) { + idt_descriptor_t* descriptor = &idt[index]; + + descriptor->isr_low = (uint64_t)isr & 0xFFFF; + descriptor->kernel_cs = KERNEL_CODE; + descriptor->ist = 0; + descriptor->attributes = flags; + descriptor->isr_mid = ((uint64_t)isr >> 16) & 0xFFFF; + descriptor->isr_high = ((uint64_t)isr >> 32) & 0xFFFFFFFF; + descriptor->reserved = 0; +} + +void idt_assemble() { + idt_ptr.base = (uintptr_t)&idt[0]; + idt_ptr.limit = (uint16_t)sizeof(idt_descriptor_t) * IDT_DESCRIPTORS - 1; + + for (uint8_t vector = 0; vector < 47; vector++) { + idt_set_gate(vector, isr_stub_table[vector], 0x8E); + vectors[vector] = true; + } + + __asm__ __volatile__ ("lidt %0" : : "m"(idt_ptr)); + idt_enable_interrupts(); +} + +void idt_enable_interrupts() { + __asm__ __volatile__("sti"); +} +void idt_disable_interrupts() { + __asm__ __volatile__("cli"); +} \ No newline at end of file diff --git a/src/kernel/platform/interrupts/int.asm b/src/kernel/platform/interrupts/int.asm new file mode 100644 index 0000000..e7485c8 --- /dev/null +++ b/src/kernel/platform/interrupts/int.asm @@ -0,0 +1,145 @@ +extern exception_handler + +%macro isr_err_stub 1 +isr_stub_%+%1: + push %1 + jmp isr_xframe_assembler +%endmacro + +%macro isr_no_err_stub 1 +isr_stub_%+%1: + push 0 + push %1 + jmp isr_xframe_assembler +%endmacro + +%macro interrupt_stub 1 +isr_stub_%+%1: + push 0 + push %1 + jmp isr_xframe_assembler +%endmacro + +%macro pushagrd 0 +push rax +push rbx +push rcx +push rdx +push rsi +push rdi +%endmacro + +%macro popagrd 0 +pop rdi +pop rsi +pop rdx +pop rcx +pop rbx +pop rax +%endmacro + +%macro pushacrd 0 +mov rax, cr0 +push rax +mov rax, cr2 +push rax +mov rax, cr3 +push rax +mov rax, cr4 +push rax +%endmacro + +%macro popacrd 0 +pop rax +mov cr4, rax +pop rax +mov cr3, rax +pop rax +mov cr2, rax +pop rax +mov cr0, rax +%endmacro + +isr_xframe_assembler: + push rbp + mov rbp, rsp + pushagrd + pushacrd + mov ax, ds + push rax + push qword 0 + mov ax, 0x10 + mov ds, ax + mov es, ax + mov ss, ax + + lea rdi, [rsp + 0x10] + call exception_handler + + pop rax + pop rax + mov ds, ax + mov es, ax + popacrd + popagrd + pop rbp + add rsp, 0x10 + iretq + +isr_no_err_stub 0 +isr_no_err_stub 1 +isr_no_err_stub 2 +isr_no_err_stub 3 +isr_no_err_stub 4 +isr_no_err_stub 5 +isr_no_err_stub 6 +isr_no_err_stub 7 +isr_err_stub 8 +isr_no_err_stub 9 +isr_err_stub 10 +isr_err_stub 11 +isr_err_stub 12 +isr_err_stub 13 +isr_err_stub 14 +isr_no_err_stub 15 +isr_no_err_stub 16 +isr_err_stub 17 +isr_no_err_stub 18 +isr_no_err_stub 19 +isr_no_err_stub 20 +isr_no_err_stub 21 +isr_no_err_stub 22 +isr_no_err_stub 23 +isr_no_err_stub 24 +isr_no_err_stub 25 +isr_no_err_stub 26 +isr_no_err_stub 27 +isr_no_err_stub 28 +isr_no_err_stub 29 +isr_err_stub 30 +isr_no_err_stub 31 + +interrupt_stub 32 +interrupt_stub 33 +interrupt_stub 34 +interrupt_stub 35 +interrupt_stub 36 +interrupt_stub 37 +interrupt_stub 38 +interrupt_stub 39 +interrupt_stub 40 +interrupt_stub 41 +interrupt_stub 42 +interrupt_stub 43 +interrupt_stub 44 +interrupt_stub 45 +interrupt_stub 46 +interrupt_stub 47 + +global isr_stub_table +isr_stub_table: +%assign i 0 +%rep 47 + dq isr_stub_%+i +%assign i i+1 +%endrep \ No newline at end of file diff --git a/src/kernel/platform/interrupts/isr.c b/src/kernel/platform/interrupts/isr.c new file mode 100644 index 0000000..0b26405 --- /dev/null +++ b/src/kernel/platform/interrupts/isr.c @@ -0,0 +1,24 @@ +#include +#include + +void exception_handler(isr_xframe_t frame) { + err_count++; + char s[256]; + itoa(err_count, s); + print_str(" "); + print_str(s); + print_str(": cpu: check_exception "); + itoa(frame.base_frame.vector, s); + print_str(s); + print_str(" @ "); + itoa(frame.base_frame.rip, s); + print_str(s); + print_str(" err_code => "); + itoa(frame.base_frame.error_code, s); + print_str(s); + print_str("\n"); + if (err_count > ERR_MAX) { + print_str("cpu: ierr hit err_max, halt"); + __asm__ __volatile__("cli; hlt"); + } +} \ No newline at end of file diff --git a/src/kernel/platform/interrupts/pic.c b/src/kernel/platform/interrupts/pic.c new file mode 100644 index 0000000..e312c8a --- /dev/null +++ b/src/kernel/platform/interrupts/pic.c @@ -0,0 +1,79 @@ +#include +#include + +void pic_send_eoi(uint8_t irq) { + if (irq >= 8) { + outb(PIC2_COMMAND, PIC_EOI); + } + outb(PIC1_COMMAND, PIC_EOI); +} + +void pic_remap(int offset1, int offset2) { + uint8_t a1, a2; + + a1 = inb(PIC1_DATA); // Save the current masks for later use + a2 = inb(PIC2_DATA); // Save the current masks for later use + + outb(PIC1_COMMAND, ICW1_INIT | ICW1_ICW4); // starts the initialization sequence (in cascade mode) + io_wait(); + outb(PIC2_COMMAND, ICW1_INIT | ICW1_ICW4); + io_wait(); + outb(PIC1_DATA, offset1); // ICW2: Master PIC vector offset + io_wait(); + outb(PIC2_DATA, offset2); // ICW2: Slave PIC vector offset + io_wait(); + outb(PIC1_DATA, 4); // ICW3: tell Master PIC that there is a slave PIC at IRQ2 (0000 0100) + io_wait(); + outb(PIC2_DATA, 2); // ICW3: tell Slave PIC its cascade identity (0000 0010) + io_wait(); + + outb(PIC1_DATA, ICW4_8086); + io_wait(); + outb(PIC2_DATA, ICW4_8086); + io_wait(); + + outb(PIC1_DATA, a1); // restore saved masks. + outb(PIC2_DATA, a2); +} + +void pic_mask_irq(uint8_t irq) { + uint16_t port; + uint8_t value; + + if (irq < 8) { + port = PIC1_DATA; + } else { + port = PIC2_DATA; + irq -= 8; + } + value = inb(port) | (1 << irq); + outb(port, value); +} + +void pic_unmask_irq(uint8_t irq) { + uint16_t port; + uint8_t value; + + if (irq < 8) { + port = PIC1_DATA; + } else { + port = PIC2_DATA; + irq -= 8; + } + value = inb(port) & ~(1 << irq); + outb(port, value); +} + +static uint16_t __pic_get_irq_reg(int ocw3) { + outb(PIC1_COMMAND, ocw3); + outb(PIC2_COMMAND, ocw3); + return (inb(PIC2_COMMAND) << 8) | inb(PIC1_COMMAND); +} + +uint16_t pic_get_irr() { + return __pic_get_irq_reg(PIC_READ_IRR); +} + +uint16_t pic_get_isr() { + return __pic_get_irq_reg(PIC_READ_ISR); +} \ No newline at end of file diff --git a/src/kernel/platform/ports.c b/src/kernel/platform/ports.c index 34a48db..9e061fe 100644 --- a/src/kernel/platform/ports.c +++ b/src/kernel/platform/ports.c @@ -1,7 +1,7 @@ /* * Read 1 byte from specified port */ -unsigned char port_byte_in(unsigned short port) { +unsigned char inb(unsigned short port) { unsigned char result; // source and dest are backwards. // "=a (result)" set the C variable to the value of register e'a'x @@ -9,18 +9,22 @@ unsigned char port_byte_in(unsigned short port) { __asm__("in %%dx, %%al" : "=a" (result) : "d" (port)); } -void port_byte_out(unsigned short port, unsigned char data) { +void outb(unsigned short port, unsigned char data) { // both regs are C vars, nothing is returned, so no '=' in asm syntax // comma because two vars in input area and none in return area __asm__("out %%al, %%dx" : : "a" (data), "d" (port)); } -unsigned short port_word_in(unsigned short port) { +unsigned short inw(unsigned short port) { unsigned short result; __asm__("in %%dx, %%ax" : "=a" (result) : "d" (port)); return result; } -void port_word_out(unsigned short port, unsigned short data) { +void outw(unsigned short port, unsigned short data) { __asm__("out %%ax, %%dx" : : "a" (data), "d" (port)); +} + +void io_wait() { + outb(0x80, 0); } \ No newline at end of file diff --git a/src/kernel/print.c b/src/kernel/print.c index 0ee30b7..fb4213a 100644 --- a/src/kernel/print.c +++ b/src/kernel/print.c @@ -87,10 +87,10 @@ void print_set_color(char foreground, char background) { void set_cursor_pos(int col, int row) { int offset = col + NUM_COLS * row; - port_byte_out(REG_SCREEN_CTRL, 14); - port_byte_out(REG_SCREEN_DATA, (unsigned char)(offset >> 8)); - port_byte_out(REG_SCREEN_CTRL, 15); - port_byte_out(REG_SCREEN_DATA, (unsigned char)(offset & 0xff)); + outb(REG_SCREEN_CTRL, 14); + outb(REG_SCREEN_DATA, (unsigned char)(offset >> 8)); + outb(REG_SCREEN_CTRL, 15); + outb(REG_SCREEN_DATA, (unsigned char)(offset & 0xff)); } void kernel_msg_ok(char* msg) { diff --git a/src/kernel/util.c b/src/kernel/util.c index 5063a05..f36d9ad 100644 --- a/src/kernel/util.c +++ b/src/kernel/util.c @@ -10,16 +10,4 @@ void memcpy(char *source, char *dest, int nbytes) { void memset(char *dest, char val, int len) { char *temp = (char *)dest; for(; len !=0; len--) *temp++ = val; -} - -void int_to_ascii(int n, char str[]) { - int i, sign; - if ((sign = n) < 0) n = -n; - i = 0; - do { - str[i++] = n % 10 + '0'; - } while ((n /= 10) > 0); - - if (sign < 0) str[i++] = '-'; - str[i] = '\0'; } \ No newline at end of file diff --git a/src/libc/strings.c b/src/libc/strings.c index d148672..899fe9f 100644 --- a/src/libc/strings.c +++ b/src/libc/strings.c @@ -1,5 +1,6 @@ #include #include +#include void itoa(int n, char str[]) { int i, sign;