os

Toy OS
git clone git://git.margiolis.net/os.git
Log | Files | Refs | README | LICENSE

commit 90c35e3098bfe4101314687b8f8590d4cf11d184
parent a2a687d2ddc7982d8c9740d23934d87cc3578ef4
Author: Christos Margiolis <christos@margiolis.net>
Date:   Mon, 29 Nov 2021 02:55:42 +0200

fixed idt stuff, improved libk

Diffstat:
Dinclude/dev/kbd.h | 12------------
Minclude/stdarg.h | 6+++---
Dinclude/sys/io.h | 79-------------------------------------------------------------------------------
Dinclude/sys/libk.h | 27---------------------------
Dinclude/sys/tty.h | 34----------------------------------
Mkern/Makefile | 10+++++-----
Dkern/boot.asm | 302------------------------------------------------------------------------------
Akern/boot.s | 302++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Mkern/idt.c | 263++++++++++++++++++++++++++++++++++++++++++-------------------------------------
Mkern/idt.h | 113+++++++++++++++++++++++++++----------------------------------------------------
Dkern/int.asm | 146-------------------------------------------------------------------------------
Akern/intr.s | 121+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Akern/io.h | 78++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Mkern/kbd.c | 21+++++++++++++--------
Akern/kbd.h | 6++++++
Mkern/kern_main.c | 14+++++---------
Mkern/libk.c | 313+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++--------------
Akern/libk.h | 29+++++++++++++++++++++++++++++
Mkern/timer.c | 16+++++++++-------
Dkern/tty.c | 136-------------------------------------------------------------------------------
Akern/vga.c | 139+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Akern/vga.h | 34++++++++++++++++++++++++++++++++++
22 files changed, 1184 insertions(+), 1017 deletions(-)

diff --git a/include/dev/kbd.h b/include/dev/kbd.h @@ -1,12 +0,0 @@ -#ifndef _KERNEL_KBD_H_ -#define _KERNEL_KBD_H_ - -#define KBD_PRESSED 0x80 -#define KBD_LSHIFT 0x2a -#define KBD_RSHIFT 0x36 -#define KBD_LSHIFT_REL 0xaa -#define KBD_RSHIFT_REL 0xb6 - -void kbd_init(void); - -#endif /* _KERNEL_KBD_H_ */ diff --git a/include/stdarg.h b/include/stdarg.h @@ -1,5 +1,5 @@ -#ifndef _KERNEL_STDARG_H -#define _KERNEL_STDARG_H +#ifndef _KERNEL_STDARG_H_ +#define _KERNEL_STDARG_H_ #ifndef _VA_LIST_DECLARED #define _VA_LIST_DECLARED @@ -11,4 +11,4 @@ typedef __va_list va_list; #define va_copy(dest, src) __builtin_va_copy((dest), (src)) #define va_end(ap) __builtin_va_end(ap) -#endif /* _KERNEL_STDARG_H */ +#endif /* _KERNEL_STDARG_H_ */ diff --git a/include/sys/io.h b/include/sys/io.h @@ -1,79 +0,0 @@ -#ifndef _KERNEL_IO_H_ -#define _KERNEL_IO_H_ - -#include <stdint.h> - -#define IO_PIC1_CMD 0x20 -#define IO_PIC2_CMD 0xa0 -#define IO_PIC1_DATA (IO_PIC1_CMD + 1) -#define IO_PIC2_DATA (IO_PIC2_CMD + 1) -#define IO_TIMER_CMD 0x43 -#define IO_TIMER_DATA 0x40 -#define IO_CURS_CMD 0x3d4 -#define IO_CURS_DATA 0x3d5 -#define IO_KBD 0x60 - -static inline uint8_t -inb(uint16_t port) -{ - uint8_t res; - - __asm__ __volatile__ ("in %%dx, %%al" : "=a" (res) : "d" (port)); - return (res); -} - -static inline void -outb(uint16_t port, uint8_t v) -{ - __asm__ __volatile__ ("out %%al, %%dx" : : "a" (v), "d" (port)); -} - -static inline uint16_t -inw(uint16_t port) -{ - uint16_t res; - - __asm__ __volatile__ ("in %%dx, %%ax" : "=a" (res) : "d" (port)); - return (res); -} - -static inline void -outw(uint16_t port, uint16_t v) -{ - __asm__ __volatile__ ("out %%ax, %%dx" : : "a" (v), "d" (port)); -} - -static inline uint32_t -inl(uint16_t port) -{ - uint32_t res; - - __asm__ __volatile__ ("in %%dx, %%eax" : "=a" (res) : "d" (port)); - return (res); -} - -static inline void -outl(uint16_t port, uint32_t v) -{ - __asm__ __volatile__ ("out %%eax, %%dx" : : "a" (v), "d" (port)); -} - -static inline void -hlt(void) -{ - __asm__ __volatile__ ("hlt"); -} - -static inline void -cli(void) -{ - __asm__ __volatile__ ("cli"); -} - -static inline void -sti(void) -{ - __asm__ __volatile__ ("sti"); -} - -#endif /* _KERNEL_IO_H_ */ diff --git a/include/sys/libk.h b/include/sys/libk.h @@ -1,27 +0,0 @@ -#ifndef _KERNEL_LIBK_H_ -#define _KERNEL_LIBK_H_ - -#include <stddef.h> -#include <stdint.h> -#include <stdarg.h> - -#include <sys/io.h> -#include <sys/tty.h> - -#define BUFSIZ 1024 - -#define ARRLEN(x) (sizeof(x) / sizeof((x)[0])) -#define UNUSED(x) ((void)(x)) -#define sizeof_field(s, f) (sizeof(((t *)0)->f)) - -void *memset(void *, int, size_t); -void *memcpy(void *, const void *, size_t); -size_t strlen(const char *); -int strcmp(const char *, const char *); -int vsprintf(char *, const char *, va_list); -int sprintf(char *, const char *, ...); -/* TODO: kprintf */ -int printf(const char *, ...); -void panic(const char *, ...); - -#endif /* _KERNEL_LIBK_H_ */ diff --git a/include/sys/tty.h b/include/sys/tty.h @@ -1,34 +0,0 @@ -#ifndef _KERNEL_TTY_H_ -#define _KERNEL_TTY_H_ - -#include <stddef.h> - -enum vga_color: uint8_t { - VGA_BLACK = 0, - VGA_BLUE, - VGA_GREEN, - VGA_CYAN, - VGA_RED, - VGA_MAGENTA, - VGA_BROWN, - VGA_LIGHT_GREY, - VGA_DARK_GREY, - VGA_LIGHT_BLUE, - VGA_LIGHT_GREEN, - VGA_LIGHT_CYAN, - VGA_LIGHT_RED, - VGA_LIGHT_MAGENTA, - VGA_LIGHT_BROWN, - VGA_WHITE, -}; - -void tty_clear(uint8_t, uint8_t); -void tty_set_color(uint8_t, uint8_t); -void tty_putc(char); -void tty_write(const char *); -void tty_curs_enable(uint8_t, uint8_t); -void tty_curs_disable(void); -void tty_curs_setpos(int, int); -uint16_t tty_curs_getpos(void); - -#endif /* _KERNEL_TTY_H_ */ diff --git a/kern/Makefile b/kern/Makefile @@ -8,15 +8,15 @@ INCDIR = ../include CFLAGS = -g -m32 -nostdlib -ffreestanding -Wall -Wextra -std=c99 -O2 -I${INCDIR} LDFLAGS = -Ttext 0x1000 --oformat binary -BOOTFILE = boot.asm +BOOTFILE = boot.s BOOT_BIN = boot.bin KERNEL_BIN = kernel.bin -SRC = *.c *.asm +SRC = *.c *.s OBJ = kern_main.o \ libk.o \ - tty.o \ idt.o \ - int.o \ + intr.o \ + vga.o \ kbd.o \ timer.o @@ -40,7 +40,7 @@ ${BIN}: ${OBJ} .c.o: ${CC} -c ${CFLAGS} $< -.asm.o: +.s.o: ${ASM} -felf $< install: all diff --git a/kern/boot.asm b/kern/boot.asm @@ -1,302 +0,0 @@ -; Load code to 0x7c00 (Boot Sector address). -[org 0x7c00] -[bits 16] - -MAGICOFF equ (0x7c00 + 510) -KERNOFF equ 0x1000 - -section .text - global _start - -; Entry point. -_start: - cli ; Disable interrupts. - xor ax, ax ; Clear segment registers. - mov ds, ax - mov es, ax - mov fs, ax - mov gs, ax - mov ss, ax - mov bp, 0x9000 ; Set the base and stack pointers. - mov sp, bp - cld ; Read strings from low to high. - sti ; Enable interrupts back. - - call a20_test - call a20_enable - call kernel_load ; Read kernel from disk to memory. - jmp pm_enter ; Enter 32-bit Protected Mode. - jmp $ ; Safety hang. - -; Test to see if the A20 line is enabled. -; As a reference we will use the magic number 0xaa55 since we know it's always -; going to have the same value and be located at address 0x7dfe (0x7c00 + 510). -; If the return code in `ax` is 1, the A20 is enabled, and if it's 0 it's disabled. -a20_test: - pusha - - mov ax, [MAGICOFF] ; The magic number is there. - mov dx, ax - -; We'll try to advance 1MB in memory. If the end result hasn't wrapped up -; around zero, it means that our processor is able to access more than 1MB -; of addresses, so the A20 is enabled. Using 0x7dfe again, the expected -; address after the 1MB advance should be: -; -; 0x100000 + 0x7dfe = 0x107dfe -; -; The formula for memory segmentation is: -; -; address = segment * 16 + offset -; -; To find the offset, the segment will be 0xffff, so `0xffff * 16 = 0xffff0`. -; Applying the formula to calculate our offset, we have: -; -; 0xffff0 + offset = 0x107dfe => -; offset = 0x107dfe - 0xffff0 => -; offset = 0x7e0e - - push bx - mov bx, 0xffff - mov es, bx ; Assign value to the segment register. - pop bx - - mov bx, 0x7e0e - mov dx, [es:bx] ; If the A20 line is disabled we get 0xaa55. - - cmp ax, dx - je cont ; If they're equal, the A20 line might be disabled. - popa - mov ax, 0x01 ; Success code 1. - ret -cont: - mov ax, [0x7dff] - mov dx, ax - - push bx - mov bx, 0xffff ; Make another segment. - mov es, bx - pop bx - - mov bx, 0x7e0f - mov dx, [es:bx] - - cmp ax, dx ; Now it really is disabled if ax == [es:bx]. - je exit - popa - mov ax, 0x01 ; Success code 1. - ret -exit: - popa - xor ax, ax ; Error code 0. - ret - -; Enable the A20 line. We'll try enabling it using the following ways: -; - BIOS interrupt. -; - Keyboard controller. -; - FastA20. -a20_enable: - pusha - - ; BIOS interrupt. - mov ax, 0x2401 ; A20-Gate Active. - int 0x15 - - call a20_test - cmp ax, 0x01 - je a20_done - jmp a20_fail - - ; Keyboard controller. - sti ; Enable interrupts. - - call a20_waitc - mov al, 0xad ; Disable controller. - out 0x64, al ; Send data to port 0x64. - call a20_waitc - mov al, 0xd0 ; We want to read from the controller. - out 0x64, al - - call a20_waitd - in al, 0x60 ; Read data from port 0x60. - push ax ; Save data. - - call a20_waitc - mov al, 0xd1 ; We want to send data. - out 0x64, al - - call a20_waitc - pop ax - or al, 0x02 ; Write the second bit back. - out 0x60, al ; Send data to the data port. - - call a20_waitc - mov al, 0xae ; Enable controller. - out 0x64, al - - call a20_waitc - sti ; Enable interrupts again. - - call a20_test - cmp ax, 0x01 - je a20_done - jmp a20_fail - -; Wait for the keyboard controller to be available for receiving commands. -a20_waitc: - in al, 0x64 ; Read from port 0x64. - test al, 0x02 ; Test the second bit. - jnz a20_waitc ; If it's 1, it's busy. - ret -; Wait for the keyboard controller to send data back. -a20_waitd: - in al, 0x64 ; Read from port 0x64 again. - test al, 0x01 ; Test the first bit. - jz a20_waitd ; If it's 1, the data is not ready to be sent. - ret - -; FastA20 - in al, 0x92 ; FastA20 uses port 0x92. - or al, 0x02 ; Mask second bit. - out 0x92, al ; Send data back to port 0x92. - - call a20_test - cmp ax, 0x01 - je a20_done - jmp a20_fail - -; The A20 is enabled. Go on. -a20_done: - popa - ret - -; The A20 is disabled. -a20_fail: - mov si, str_a20_fail - call puts - jmp $ - -; Load kernel from disk to memory. -kernel_load: - pusha - mov ah, 0x42 ; BIOS read disk extended function. - mov dl, [BOOTDRV] ; Set boot drive. - mov si, disk_packet - int 0x13 ; BIOS read disk interrupt. - - jc diskerr ; There's an error. Too bad. - popa - ret -diskerr: - mov si, str_diskerr - call puts - jmp $ ; There's nothing we can do at this point. - -disk_packet: - .size db 0x10 - .zero db 0x00 - .count dw 0x0f - .off16 dw KERNOFF - .seg16 dw 0x0000 - .lba dq 1 - -; Set up the GDT (Global Descriptor Table). -gdt: -gdt_null: - dq 0x00000000 - -gdt_kernel_code: - dw 0xffff - dw 0x0000 - db 0x00 - db 10011010b - db 11001111b - db 0x00 - -gdt_kernel_data: - dw 0xffff - dw 0x0000 - db 0x00 - db 10010010b - db 11001111b - db 0x00 - -gdt_userland_code: - dw 0xffff - dw 0x0000 - db 0x00 - db 11111010b - db 11001111b - db 0x00 - -gdt_userland_data: - dw 0xffff - dw 0x0000 - db 0x00 - db 11110010b - db 11001111b - db 0x00 - -gdt_ptr: - dw $ - gdt - 1 - dd gdt - -GDT_CODESEG equ gdt_kernel_code - gdt -GDT_DATASEG equ gdt_kernel_data - gdt - -; Set up the GDT and go into Protected Mode. -pm_enter: - cli ; Disable BIOS interrupts. - lgdt [gdt_ptr] ; Load the GDT. - mov eax, cr0 - or eax, 1 ; Protected Mode Enable. - mov cr0, eax ; There's no going back now. - jmp GDT_CODESEG:pm_init - -; We're in Protected Mode. -[bits 32] -pm_init: - mov ax, GDT_DATASEG - mov ds, ax - mov es, ax - mov fs, ax - mov gs, ax - mov ss, ax - - mov ebp, 0x90000 - mov esp, ebp - - call kernel_exec - jmp $ - -; Hand control over to the C kernel. Godspeed! You Black Emperor. -kernel_exec: - call KERNOFF - jmp $ - -; Print a null-terminated string. -[bits 16] -puts: - pusha - mov ah, 0x0e ; Tell BIOS to display a character. -loop: - mov al, [si] ; Load character. - cmp al, 0 ; Check for \0. - jne putchar ; If it's not \0, print the character. - popa ; We're done, pop everything. - ret ; Return back to where we were. -putchar: - int 0x10 ; BIOS print interrupt. - inc si ; `str++` - jmp loop ; Go to the next character. - -; String declarations. -str_diskerr: db "Error loading disk.", 0x0a, 0x0d, 0x00 -str_a20_fail: db "The A20 Line is disabled", 0x0a, 0x0d, 0x00 - -BOOTDRV: db 0x80 - -; Padding to 512 bytes. The last 2 bytes will come from the magic number. -times 510 - ($ - $$) db 0 -; Magic number. -dw 0xaa55 diff --git a/kern/boot.s b/kern/boot.s @@ -0,0 +1,302 @@ +; Load code to 0x7c00 (Boot Sector address). +[org 0x7c00] +[bits 16] + +MAGICOFF equ (0x7c00 + 510) +KERNOFF equ 0x1000 + +section .text + global _start + +; Entry point. +_start: + cli ; Disable interrupts. + xor ax, ax ; Clear segment registers. + mov ds, ax + mov es, ax + mov fs, ax + mov gs, ax + mov ss, ax + mov bp, 0x9000 ; Set the base and stack pointers. + mov sp, bp + cld ; Read strings from low to high. + sti ; Enable interrupts back. + + call a20_test + call a20_enable + call kernel_load ; Read kernel from disk to memory. + jmp pm_enter ; Enter 32-bit Protected Mode. + jmp $ ; Safety hang. + +; Test to see if the A20 line is enabled. +; As a reference we will use the magic number 0xaa55 since we know it's always +; going to have the same value and be located at address 0x7dfe (0x7c00 + 510). +; If the return code in `ax` is 1, the A20 is enabled, and if it's 0 it's disabled. +a20_test: + pusha + + mov ax, [MAGICOFF] ; The magic number is there. + mov dx, ax + +; We'll try to advance 1MB in memory. If the end result hasn't wrapped up +; around zero, it means that our processor is able to access more than 1MB +; of addresses, so the A20 is enabled. Using 0x7dfe again, the expected +; address after the 1MB advance should be: +; +; 0x100000 + 0x7dfe = 0x107dfe +; +; The formula for memory segmentation is: +; +; address = segment * 16 + offset +; +; To find the offset, the segment will be 0xffff, so `0xffff * 16 = 0xffff0`. +; Applying the formula to calculate our offset, we have: +; +; 0xffff0 + offset = 0x107dfe => +; offset = 0x107dfe - 0xffff0 => +; offset = 0x7e0e + + push bx + mov bx, 0xffff + mov es, bx ; Assign value to the segment register. + pop bx + + mov bx, 0x7e0e + mov dx, [es:bx] ; If the A20 line is disabled we get 0xaa55. + + cmp ax, dx + je cont ; If they're equal, the A20 line might be disabled. + popa + mov ax, 0x01 ; Success code 1. + ret +cont: + mov ax, [0x7dff] + mov dx, ax + + push bx + mov bx, 0xffff ; Make another segment. + mov es, bx + pop bx + + mov bx, 0x7e0f + mov dx, [es:bx] + + cmp ax, dx ; Now it really is disabled if ax == [es:bx]. + je exit + popa + mov ax, 0x01 ; Success code 1. + ret +exit: + popa + xor ax, ax ; Error code 0. + ret + +; Enable the A20 line. We'll try enabling it using the following ways: +; - BIOS interrupt. +; - Keyboard controller. +; - FastA20. +a20_enable: + pusha + + ; BIOS interrupt. + mov ax, 0x2401 ; A20-Gate Active. + int 0x15 + + call a20_test + cmp ax, 0x01 + je a20_done + jmp a20_fail + + ; Keyboard controller. + sti ; Enable interrupts. + + call a20_waitc + mov al, 0xad ; Disable controller. + out 0x64, al ; Send data to port 0x64. + call a20_waitc + mov al, 0xd0 ; We want to read from the controller. + out 0x64, al + + call a20_waitd + in al, 0x60 ; Read data from port 0x60. + push ax ; Save data. + + call a20_waitc + mov al, 0xd1 ; We want to send data. + out 0x64, al + + call a20_waitc + pop ax + or al, 0x02 ; Write the second bit back. + out 0x60, al ; Send data to the data port. + + call a20_waitc + mov al, 0xae ; Enable controller. + out 0x64, al + + call a20_waitc + sti ; Enable interrupts again. + + call a20_test + cmp ax, 0x01 + je a20_done + jmp a20_fail + +; Wait for the keyboard controller to be available for receiving commands. +a20_waitc: + in al, 0x64 ; Read from port 0x64. + test al, 0x02 ; Test the second bit. + jnz a20_waitc ; If it's 1, it's busy. + ret +; Wait for the keyboard controller to send data back. +a20_waitd: + in al, 0x64 ; Read from port 0x64 again. + test al, 0x01 ; Test the first bit. + jz a20_waitd ; If it's 1, the data is not ready to be sent. + ret + +; FastA20 + in al, 0x92 ; FastA20 uses port 0x92. + or al, 0x02 ; Mask second bit. + out 0x92, al ; Send data back to port 0x92. + + call a20_test + cmp ax, 0x01 + je a20_done + jmp a20_fail + +; The A20 is enabled. Go on. +a20_done: + popa + ret + +; The A20 is disabled. +a20_fail: + mov si, str_a20_fail + call puts + jmp $ + +; Load kernel from disk to memory. +kernel_load: + pusha + mov ah, 0x42 ; BIOS read disk extended function. + mov dl, [BOOTDRV] ; Set boot drive. + mov si, disk_packet + int 0x13 ; BIOS read disk interrupt. + + jc diskerr ; There's an error. Too bad. + popa + ret +diskerr: + mov si, str_diskerr + call puts + jmp $ ; There's nothing we can do at this point. + +disk_packet: + .size db 0x10 + .zero db 0x00 + .count dw 0x0f + .off16 dw KERNOFF + .seg16 dw 0x0000 + .lba dq 1 + +; Set up the GDT (Global Descriptor Table). +gdt: +gdt_null: ; offset = 0x0 + dq 0x00000000 + +gdt_kernel_code: ; offset = 0x08 + dw 0xffff ; segment limit 0..15 + dw 0x0000 ; segment base 0..15 + db 0x00 ; segment base 16..23 set for 0K + db 0x9f ; flags: type + db 0xcf ; flags: limit + db 0x00 ; segment base 24..32 + +gdt_kernel_data: ; offset = 0x10 + dw 0xffff + dw 0x0000 + db 0x00 + db 0x93 + db 0xcf + db 0x00 + +gdt_userland_code: ; offset = 0x18 + dw 0xffff + dw 0x0000 + db 0x00 + db 0x9e + db 0xcf + db 0x00 + +gdt_userland_data: ; offset = 0x20 + dw 0xffff + dw 0x0000 + db 0x00 + db 0x92 + db 0xcf + db 0x00 + +gdt_ptr: + dw $ - gdt - 1 + dd gdt + +GDT_CODESEG equ gdt_kernel_code - gdt +GDT_DATASEG equ gdt_kernel_data - gdt + +; Set up the GDT and go into Protected Mode. +pm_enter: + cli ; Disable BIOS interrupts. + lgdt [gdt_ptr] ; Load the GDT. + mov eax, cr0 + or eax, 1 ; Protected Mode Enable. + mov cr0, eax ; There's no going back now. + jmp GDT_CODESEG:pm_init + +; We're in Protected Mode. +[bits 32] +pm_init: + mov ax, GDT_DATASEG + mov ds, ax + mov es, ax + mov fs, ax + mov gs, ax + mov ss, ax + + mov ebp, 0x90000 + mov esp, ebp + + call kernel_exec + jmp $ + +; Hand control over to the C kernel. Godspeed! You Black Emperor. +kernel_exec: + call KERNOFF + jmp $ + +; Print a null-terminated string. +[bits 16] +puts: + pusha + mov ah, 0x0e ; Tell BIOS to display a character. +loop: + mov al, [si] ; Load character. + cmp al, 0 ; Check for \0. + jne putchar ; If it's not \0, print the character. + popa ; We're done, pop everything. + ret ; Return back to where we were. +putchar: + int 0x10 ; BIOS print interrupt. + inc si ; `str++` + jmp loop ; Go to the next character. + +; String declarations. +str_diskerr: db "Error loading disk.", 0x0a, 0x0d, 0x00 +str_a20_fail: db "The A20 Line is disabled", 0x0a, 0x0d, 0x00 + +BOOTDRV: db 0x80 + +; Padding to 512 bytes. The last 2 bytes will come from the magic number. +times 510 - ($ - $$) db 0 +; Magic number. +dw 0xaa55 diff --git a/kern/idt.c b/kern/idt.c @@ -1,24 +1,137 @@ -#include <sys/libk.h> - +#include "libk.h" #include "idt.h" -#define NINT 256 -#define NISR 16 -#define KERN_CODESEG 0x08 +#define NINT 256 +#define NRSVINT 32 +#define NISR 16 +#define PL_KERN 0 /* Kernel privilege level */ +#define PL_DRV1 1 /* Device driver privilege level 1 */ +#define PL_DRV2 2 /* Device driver privilege level 2 */ +#define PL_USER 3 /* User privilege level */ +#define SEL_KCODE 0x08 /* Kernel code descriptor */ +#define TP_386IGT 0xe /* System 386 interrupt gate */ +/* Type 0..3, DPL 4..6, P 7..8 */ +#define GT_FLAGS(r, t) ((1<<7) | (r<<5) | t) + +#define PIC_MASTER_CMD 0x20 +#define PIC_MASTER_DATA (PIC_MASTER_CMD + 1) +#define PIC_SLAVE_CMD 0xa0 +#define PIC_SLAVE_DATA (PIC_SLAVE_CMD + 1) + +static void print_regs(struct reg *); +static void idt_set_gate(struct gate_desc *, void *, uint16_t, uint8_t); +static void pic_remap(void); + +static struct gate_desc idt[NINT]; +static intrhand_t isr[NISR] = { NULL }; + +static void +print_regs(struct reg *r) +{ + printf("eax=%#08x\tebx=%#08x\tecx=%#08x\tedx=%#08x\n", + r->r_eax, r->r_ebx, r->r_ecx, r->r_edx); + printf("esp=%#08x\tebp=%#08x\tesi=%#08x\tedi=%#08x\n", + r->r_esp, r->r_ebp, r->r_esi, r->r_edi); + printf("ds=%#08x \tes=%#08x \tfs=%#08x \tgs=%#08x\n", + r->r_ds, r->r_es, r->r_fs, r->r_gs); + printf("eip=%#08x\tcs=%#08x \tss=%#08x \teflags=%08x\n", + r->r_eip, r->r_cs, r->r_ss, r->r_eflags); + printf("int=%#08x\terr=%#08x\tuesp=%#08x\n", + r->r_intrno, r->r_err, r->r_uesp); +} + +static void +idt_set_gate(struct gate_desc *gd, void *func, uint16_t sel, uint8_t flags) +{ + gd->gd_off_lo = (uint32_t)func & 0xffff; + gd->gd_sel = sel; + gd->gd_rsvd = 0; + gd->gd_flags = flags; + gd->gd_off_hi = ((uint32_t)func >> 16) & 0xffff; +} + +/* TODO: explain. */ +static void +pic_remap(void) +{ + outb(PIC_MASTER_CMD, 0x11); + outb(PIC_SLAVE_CMD, 0x11); + outb(PIC_MASTER_DATA, 0x20); + outb(PIC_SLAVE_DATA, 0x28); + outb(PIC_MASTER_DATA, 0x04); + outb(PIC_SLAVE_DATA, 0x02); + outb(PIC_MASTER_DATA, 0x01); + outb(PIC_SLAVE_DATA, 0x01); + outb(PIC_MASTER_DATA, 0x00); + outb(PIC_MASTER_DATA, 0x00); +} -static void idt_set_entry(uint32_t); +extern void ex_div, ex_dbg, ex_nmsk, ex_bpt, ex_ofl, ex_bnd, ex_ill, ex_dna, + ex_dbl, ex_fpusegm, ex_tss, ex_missing, ex_stk, ex_prot, ex_page, ex_rsvd, + ex_fpu, ex_align, ex_mchk, ex_simd; -/* IA-32 */ -struct idt_ent { - uint16_t off_lo; - uint16_t sel; - uint8_t zero; - uint8_t flags; - uint16_t off_hi; -} __attribute__((packed)) idt[NINT]; +extern void irq0, irq1, irq2, irq3, irq4, irq5, irq6, irq7, irq8, irq9, irq10, + irq11, irq12, irq13, irq14, irq15; -static void *isr[NISR] = {NULL}; -static const char *except[] = { +void +idt_init(void) +{ + struct region_desc r_idt; + int i; + + for (i = 0; i < NINT; i++) + idt_set_gate(&idt[i], &ex_rsvd, SEL_KCODE, + GT_FLAGS(PL_KERN, TP_386IGT)); + idt_set_gate(&idt[ 0], &ex_div, SEL_KCODE, GT_FLAGS(PL_KERN, TP_386IGT)); + idt_set_gate(&idt[ 1], &ex_dbg, SEL_KCODE, GT_FLAGS(PL_KERN, TP_386IGT)); + idt_set_gate(&idt[ 2], &ex_nmsk, SEL_KCODE, GT_FLAGS(PL_KERN, TP_386IGT)); + idt_set_gate(&idt[ 3], &ex_bpt, SEL_KCODE, GT_FLAGS(PL_KERN, TP_386IGT)); + idt_set_gate(&idt[ 4], &ex_ofl, SEL_KCODE, GT_FLAGS(PL_KERN, TP_386IGT)); + idt_set_gate(&idt[ 5], &ex_bnd, SEL_KCODE, GT_FLAGS(PL_KERN, TP_386IGT)); + idt_set_gate(&idt[ 6], &ex_ill, SEL_KCODE, GT_FLAGS(PL_KERN, TP_386IGT)); + idt_set_gate(&idt[ 7], &ex_dna, SEL_KCODE, GT_FLAGS(PL_KERN, TP_386IGT)); + idt_set_gate(&idt[ 8], &ex_dbl, SEL_KCODE, GT_FLAGS(PL_KERN, TP_386IGT)); + idt_set_gate(&idt[ 9], &ex_fpusegm, SEL_KCODE, GT_FLAGS(PL_KERN, TP_386IGT)); + idt_set_gate(&idt[10], &ex_tss, SEL_KCODE, GT_FLAGS(PL_KERN, TP_386IGT)); + idt_set_gate(&idt[11], &ex_missing, SEL_KCODE, GT_FLAGS(PL_KERN, TP_386IGT)); + idt_set_gate(&idt[12], &ex_stk, SEL_KCODE, GT_FLAGS(PL_KERN, TP_386IGT)); + idt_set_gate(&idt[13], &ex_prot, SEL_KCODE, GT_FLAGS(PL_KERN, TP_386IGT)); + idt_set_gate(&idt[14], &ex_page, SEL_KCODE, GT_FLAGS(PL_KERN, TP_386IGT)); + idt_set_gate(&idt[15], &ex_rsvd, SEL_KCODE, GT_FLAGS(PL_KERN, TP_386IGT)); + idt_set_gate(&idt[16], &ex_fpu, SEL_KCODE, GT_FLAGS(PL_KERN, TP_386IGT)); + idt_set_gate(&idt[17], &ex_align, SEL_KCODE, GT_FLAGS(PL_KERN, TP_386IGT)); + idt_set_gate(&idt[18], &ex_mchk, SEL_KCODE, GT_FLAGS(PL_KERN, TP_386IGT)); + idt_set_gate(&idt[19], &ex_simd, SEL_KCODE, GT_FLAGS(PL_KERN, TP_386IGT)); + /* 20 - 31 are reserved. */ + + pic_remap(); + + /* FIXME: how is this related to irq 0 -> timer etc? */ + idt_set_gate(&idt[32], &irq0, SEL_KCODE, GT_FLAGS(PL_KERN, TP_386IGT)); + idt_set_gate(&idt[33], &irq1, SEL_KCODE, GT_FLAGS(PL_KERN, TP_386IGT)); + idt_set_gate(&idt[34], &irq2, SEL_KCODE, GT_FLAGS(PL_KERN, TP_386IGT)); + idt_set_gate(&idt[35], &irq3, SEL_KCODE, GT_FLAGS(PL_KERN, TP_386IGT)); + idt_set_gate(&idt[36], &irq4, SEL_KCODE, GT_FLAGS(PL_KERN, TP_386IGT)); + idt_set_gate(&idt[37], &irq5, SEL_KCODE, GT_FLAGS(PL_KERN, TP_386IGT)); + idt_set_gate(&idt[38], &irq6, SEL_KCODE, GT_FLAGS(PL_KERN, TP_386IGT)); + idt_set_gate(&idt[39], &irq7, SEL_KCODE, GT_FLAGS(PL_KERN, TP_386IGT)); + idt_set_gate(&idt[40], &irq8, SEL_KCODE, GT_FLAGS(PL_KERN, TP_386IGT)); + idt_set_gate(&idt[41], &irq9, SEL_KCODE, GT_FLAGS(PL_KERN, TP_386IGT)); + idt_set_gate(&idt[42], &irq10, SEL_KCODE, GT_FLAGS(PL_KERN, TP_386IGT)); + idt_set_gate(&idt[43], &irq11, SEL_KCODE, GT_FLAGS(PL_KERN, TP_386IGT)); + idt_set_gate(&idt[44], &irq12, SEL_KCODE, GT_FLAGS(PL_KERN, TP_386IGT)); + idt_set_gate(&idt[45], &irq13, SEL_KCODE, GT_FLAGS(PL_KERN, TP_386IGT)); + idt_set_gate(&idt[46], &irq14, SEL_KCODE, GT_FLAGS(PL_KERN, TP_386IGT)); + idt_set_gate(&idt[47], &irq15, SEL_KCODE, GT_FLAGS(PL_KERN, TP_386IGT)); + + /*idt_set_gate(&idt[127], &syscall, SEL_KCODE, GT_FLAGS(PL_KERN, TP_386IGT));*/ + + r_idt.rd_base = (uint32_t)&idt; + r_idt.rd_limit = NINT * sizeof(struct gate_desc) - 1; + lidt(&r_idt); +} + +static const char *exceptmsg[] = { "Division By Zero Exception", "Debug Exception", "Non Maskable Interrupt Exception", @@ -53,122 +166,28 @@ static const char *except[] = { "Reserved Exception", }; -static void -idt_set_entry(uint32_t ptr) -{ - static int i = 0; - - idt[i].off_lo = ptr & 0xffff; - idt[i].sel = KERN_CODESEG; - idt[i].zero = 0; - /* The gate is present and is running in kernel mode. */ - idt[i].flags = 0x8e; - idt[i].off_hi = (ptr >> 16) & 0xffff; - i++; -} - -void -idt_init(void) -{ - struct idt_reg { - uint16_t limit; /* IDT size in bytes. */ - uint32_t base; /* Points at `idt[0]`. */ - } __attribute__((packed)) idtr; - - (void)memset(&idt, 0, sizeof(idt)); - - /* Set up exception interrupts. */ - idt_set_entry((uint32_t)ex0); - idt_set_entry((uint32_t)ex1); - idt_set_entry((uint32_t)ex2); - idt_set_entry((uint32_t)ex3); - idt_set_entry((uint32_t)ex4); - idt_set_entry((uint32_t)ex5); - idt_set_entry((uint32_t)ex6); - idt_set_entry((uint32_t)ex7); - idt_set_entry((uint32_t)ex8); - idt_set_entry((uint32_t)ex9); - idt_set_entry((uint32_t)ex10); - idt_set_entry((uint32_t)ex11); - idt_set_entry((uint32_t)ex12); - idt_set_entry((uint32_t)ex13); - idt_set_entry((uint32_t)ex14); - idt_set_entry((uint32_t)ex15); - idt_set_entry((uint32_t)ex16); - idt_set_entry((uint32_t)ex17); - idt_set_entry((uint32_t)ex18); - idt_set_entry((uint32_t)ex19); - idt_set_entry((uint32_t)ex20); - idt_set_entry((uint32_t)ex21); - idt_set_entry((uint32_t)ex22); - idt_set_entry((uint32_t)ex23); - idt_set_entry((uint32_t)ex24); - idt_set_entry((uint32_t)ex25); - idt_set_entry((uint32_t)ex26); - idt_set_entry((uint32_t)ex27); - idt_set_entry((uint32_t)ex28); - idt_set_entry((uint32_t)ex29); - idt_set_entry((uint32_t)ex30); - idt_set_entry((uint32_t)ex31); - - /* Remap the PIC */ - /* TODO: explain.*/ - outb(IO_PIC1_CMD, 0x11); - outb(IO_PIC2_CMD, 0x11); - outb(IO_PIC1_DATA, 0x20); - outb(IO_PIC2_DATA, 0x28); - outb(IO_PIC1_DATA, 0x04); - outb(IO_PIC2_DATA, 0x02); - outb(IO_PIC1_DATA, 0x01); - outb(IO_PIC2_DATA, 0x01); - outb(IO_PIC1_DATA, 0x00); - outb(IO_PIC1_DATA, 0x00); - - /* Set up IRQs */ - idt_set_entry((uint32_t)irq0); - idt_set_entry((uint32_t)irq1); - idt_set_entry((uint32_t)irq2); - idt_set_entry((uint32_t)irq3); - idt_set_entry((uint32_t)irq4); - idt_set_entry((uint32_t)irq5); - idt_set_entry((uint32_t)irq6); - idt_set_entry((uint32_t)irq7); - idt_set_entry((uint32_t)irq8); - idt_set_entry((uint32_t)irq9); - idt_set_entry((uint32_t)irq10); - idt_set_entry((uint32_t)irq11); - idt_set_entry((uint32_t)irq12); - idt_set_entry((uint32_t)irq13); - idt_set_entry((uint32_t)irq14); - idt_set_entry((uint32_t)irq15); - - /* https://wiki.osdev.org/Interrupt_Descriptor_Table#Location_and_Size */ - idtr.base = (uint32_t)&idt; - idtr.limit = NINT * sizeof(struct idt_ent) - 1; - __asm__ __volatile__ ("lidtl (%0)" : : "r" (&idtr)); -} - void -int_handler(struct reg *r) +intr_handler(struct reg *r) { - void (*handler)(struct reg *); + intrhand_t handler; /* * We'll call the handler only if the interrupt number is > 32, * which means that we're dealing with an IRQ and not an exception. */ - if (r->intno >= 32 && (handler = isr[r->intno - 32]) != NULL) + if (r->r_intrno >= 32 && (handler = isr[r->r_intrno - 32]) != NULL) handler(r); - /* Entries below index 32 in the IDT are exceptions, we need to hang. */ - else if (r->intno < 32) - panic("%s: System halted...\n", except[r->intno]); - if (r->intno >= 40) - outb(IO_PIC2_CMD, 0x20); - outb(IO_PIC1_CMD, 0x20); + else if (r->r_intrno < 32) { + print_regs(r); + panic("%s: system halted...\n", exceptmsg[r->r_intrno]); + } + if (r->r_intrno >= 40) + outb(PIC_SLAVE_CMD, 0x20); + outb(PIC_MASTER_CMD, 0x20); } void -int_add_handler(uint8_t intno, void (*handler)(struct reg *r)) +intr_register_handler(uint8_t intrno, intrhand_t handler) { - isr[intno] = handler; + isr[intrno] = handler; } diff --git a/kern/idt.h b/kern/idt.h @@ -1,92 +1,57 @@ #ifndef _KERNEL_IDT_H_ #define _KERNEL_IDT_H_ +/* IA-32 */ +struct gate_desc { + uint16_t gd_off_lo; + uint16_t gd_sel; + uint8_t gd_rsvd; + uint8_t gd_flags; /* type, dpl, p */ + uint16_t gd_off_hi; +} __packed; + +struct region_desc { + uint16_t rd_limit; + uint32_t rd_base; +} __packed; + /* TODO: keep counter? */ -/* This will be populated by `int_common_stub` in `int.asm`. */ +/* This will be populated by `intr_common_stub` in `intr.asm`. */ +/* FIXME: move to i386 dir? */ struct reg { /* Will be popped last. */ - uint32_t gs; - uint32_t fs; - uint32_t es; - uint32_t ds; + uint32_t r_gs; + uint32_t r_fs; + uint32_t r_es; + uint32_t r_ds; /* Pushed by `pusha`. */ - uint32_t edi; - uint32_t esi; - uint32_t ebp; - uint32_t esp; - uint32_t ebx; - uint32_t edx; - uint32_t ecx; - uint32_t eax; + uint32_t r_edi; + uint32_t r_esi; + uint32_t r_ebp; + uint32_t r_esp; + uint32_t r_ebx; + uint32_t r_edx; + uint32_t r_ecx; + uint32_t r_eax; /* Interrupt info. Pushed by `push byte`. */ - uint32_t intno; - uint32_t err; + uint32_t r_intrno; + uint32_t r_err; /* Pushed by the CPU. */ - uint32_t eip; - uint32_t cs; - uint32_t eflags; - uint32_t uesp; - uint32_t ss; + uint32_t r_eip; + uint32_t r_cs; + uint32_t r_eflags; + uint32_t r_uesp; + uint32_t r_ss; }; +typedef void (*intrhand_t)(struct reg *); + /* Called by `kern_main`. */ void idt_init(void); /* Called by drivers. */ -void int_handler(struct reg *); -void int_add_handler(uint8_t, void (*)(struct reg *)); - -/* The first 32 interrupts are reserved for exceptions. */ -void ex0(void); -void ex1(void); -void ex2(void); -void ex3(void); -void ex4(void); -void ex5(void); -void ex6(void); -void ex7(void); -void ex8(void); -void ex9(void); -void ex10(void); -void ex11(void); -void ex12(void); -void ex13(void); -void ex14(void); -void ex15(void); -void ex16(void); -void ex17(void); -void ex18(void); -void ex19(void); -void ex20(void); -void ex21(void); -void ex22(void); -void ex23(void); -void ex24(void); -void ex25(void); -void ex26(void); -void ex27(void); -void ex28(void); -void ex29(void); -void ex30(void); -void ex31(void); - -/* IRQs */ -void irq0(void); -void irq1(void); -void irq2(void); -void irq3(void); -void irq4(void); -void irq5(void); -void irq6(void); -void irq7(void); -void irq8(void); -void irq9(void); -void irq10(void); -void irq11(void); -void irq12(void); -void irq13(void); -void irq14(void); -void irq15(void); +void intr_handler(struct reg *); +void intr_register_handler(uint8_t, intrhand_t); #endif /* _KERNEL_IDT_H_ */ diff --git a/kern/int.asm b/kern/int.asm @@ -1,146 +0,0 @@ -; Defined in `idt.h`. -[extern int_handler] - -; Exceptions -global ex0 -global ex1 -global ex2 -global ex3 -global ex4 -global ex5 -global ex6 -global ex7 -global ex8 -global ex9 -global ex10 -global ex11 -global ex12 -global ex13 -global ex14 -global ex15 -global ex16 -global ex17 -global ex18 -global ex19 -global ex20 -global ex21 -global ex22 -global ex23 -global ex24 -global ex25 -global ex26 -global ex27 -global ex28 -global ex29 -global ex30 -global ex31 - -; IRQs -global irq0 -global irq1 -global irq2 -global irq3 -global irq4 -global irq5 -global irq6 -global irq7 -global irq8 -global irq9 -global irq10 -global irq11 -global irq12 -global irq13 -global irq14 -global irq15 - -%macro int_noerr 1 - cli - push byte 0 - push byte %1 - jmp int_common_stub -%endmacro - -%macro int_err 1 - cli - push byte 0 - push byte %1 - jmp int_common_stub -%endmacro - -; Exceptions -ex0: int_noerr 0 ; Division By Zero -ex1: int_noerr 1 ; Debug -ex2: int_noerr 2 ; Non Maskable Interrupt -ex3: int_noerr 3 ; Breakpoint -ex4: int_noerr 4 ; Into Detected Overflow -ex5: int_noerr 5 ; Out of Bounds -ex6: int_noerr 6 ; Invalid Opcode -ex7: int_noerr 7 ; No Coprocessor -ex8: int_err 8 ; Double Fault (with error code) -ex9: int_noerr 9 ; Coprocessor Segment Overrrun -ex10: int_err 10 ; Bad TSS (with error code) -ex11: int_err 11 ; Segment Not Present (with error code) -ex12: int_err 12 ; Stack Fault (with error code) -ex13: int_err 13 ; General Protection Fault (with error code) -ex14: int_err 14 ; Page Fault (with error code) -ex15: int_noerr 15 ; Unkown Interrupt -ex16: int_noerr 16 ; Coprocessor Fault -ex17: int_noerr 17 ; Alignment Check (486+) -ex18: int_noerr 18 ; Machine Check (Pentium/586+) -ex19: int_noerr 19 ; Reserved -ex20: int_noerr 20 ; Reserved -ex21: int_noerr 21 ; Reserved -ex22: int_noerr 22 ; Reserved -ex23: int_noerr 23 ; Reserved -ex24: int_noerr 24 ; Reserved -ex25: int_noerr 25 ; Reserved -ex26: int_noerr 26 ; Reserved -ex27: int_noerr 27 ; Reserved -ex28: int_noerr 28 ; Reserved -ex29: int_noerr 29 ; Reserved -ex30: int_noerr 30 ; Reserved -ex31: int_noerr 31 ; Reserved -; IRQs -irq0: int_noerr 32 ; Programmable Interrupt Timer -irq1: int_noerr 33 ; Keyboard -irq2: int_noerr 34 ; Cascade (used internally by the two PICs, never raised) -irq3: int_noerr 35 ; COM2 (if enabled) -irq4: int_noerr 36 ; COM1 (if enabled) -irq5: int_noerr 37 ; LPT2 (if enabled) -irq6: int_noerr 38 ; Floppy Disk -irq7: int_noerr 39 ; LPT1 -irq8: int_noerr 40 ; CMOS real-time clock (if enabled) -irq9: int_noerr 41 ; Peripherals / Legacy SCSI / NIC -irq10: int_noerr 42 ; Peripherals / SCSI / NIC -irq11: int_noerr 43 ; Peripherals / SCSI / NIC -irq12: int_noerr 44 ; PS2 Mouse -irq13: int_noerr 45 ; FPU / Coprocessor / Inter-processor -irq14: int_noerr 46 ; Primary ATA Hard Disk -irq15: int_noerr 47 ; Secondary ATA Hard Disk - -; Save the processor state, call the C interrupt handler and restore the -; stack frame. -int_common_stub: - pusha - push ds - push es - push fs - push gs - mov ax, 0x10 ; Kernel data segment descriptor. - mov ds, ax - mov es, ax - mov fs, ax - mov gs, ax - mov eax, esp ; Push the stack. - push eax - mov eax, int_handler - call eax - pop eax - pop gs - pop fs - pop es - pop ds - popa - add esp, 8 ; Clean up ISR info. - sti - iret diff --git a/kern/intr.s b/kern/intr.s @@ -0,0 +1,121 @@ +; Defined in `idt.h`. +[extern intr_handler] + +global ex_div +global ex_dbg +global ex_nmsk +global ex_bpt +global ex_ofl +global ex_bnd +global ex_ill +global ex_dna +global ex_dbl +global ex_fpusegm +global ex_tss +global ex_missing +global ex_stk +global ex_prot +global ex_page +global ex_rsvd +global ex_fpu +global ex_align +global ex_mchk +global ex_simd + +; IRQs +global irq0 +global irq1 +global irq2 +global irq3 +global irq4 +global irq5 +global irq6 +global irq7 +global irq8 +global irq9 +global irq10 +global irq11 +global irq12 +global irq13 +global irq14 +global irq15 + +%macro intr_noerr 1 + cli + push byte 0 + push byte %1 + jmp intr_common_stub +%endmacro + +%macro intr_err 1 + cli + push byte %1 + jmp intr_common_stub +%endmacro + +; Exceptions +ex_div: intr_noerr 0 ; Division By Zero +ex_dbg: intr_noerr 1 ; Debug +ex_nmsk: intr_noerr 2 ; Non Maskable Interrupt +ex_bpt: intr_noerr 3 ; Breakpoint +ex_ofl: intr_noerr 4 ; Into Detected Overflow +ex_bnd: intr_noerr 5 ; Out of Bounds +ex_ill: intr_noerr 6 ; Invalid Opcode +ex_dna: intr_noerr 7 ; No Coprocessor +ex_dbl: intr_err 8 ; Double Fault (with error code) +ex_fpusegm: intr_noerr 9 ; Coprocessor Segment Overrrun +ex_tss: intr_err 10 ; Bad TSS (with error code) +ex_missing: intr_err 11 ; Segment Not Present (with error code) +ex_stk: intr_err 12 ; Stack Fault (with error code) +ex_prot: intr_err 13 ; General Protection Fault (with error code) +ex_page: intr_err 14 ; Page Fault (with error code) +ex_rsvd: intr_noerr 15 ; Unkown Interrupt +ex_fpu: intr_noerr 16 ; Coprocessor Fault +ex_align: intr_noerr 17 ; Alignment Check (486+) +ex_mchk: intr_noerr 18 ; Machine Check (Pentium/586+) +ex_simd: intr_noerr 19 ; Reserved ; FIXME: ?? + +; IRQs +irq0: intr_noerr 32 ; Programmable Interrupt Timer +irq1: intr_noerr 33 ; Keyboard +irq2: intr_noerr 34 ; Cascade (used internally by the two PICs, never raised) +irq3: intr_noerr 35 ; COM2 (if enabled) +irq4: intr_noerr 36 ; COM1 (if enabled) +irq5: intr_noerr 37 ; LPT2 (if enabled) +irq6: intr_noerr 38 ; Floppy Disk +irq7: intr_noerr 39 ; LPT1 +irq8: intr_noerr 40 ; CMOS real-time clock (if enabled) +irq9: intr_noerr 41 ; Peripherals / Legacy SCSI / NIC +irq10: intr_noerr 42 ; Peripherals / SCSI / NIC +irq11: intr_noerr 43 ; Peripherals / SCSI / NIC +irq12: intr_noerr 44 ; PS2 Mouse +irq13: intr_noerr 45 ; FPU / Coprocessor / Inter-processor +irq14: intr_noerr 46 ; Primary ATA Hard Disk +irq15: intr_noerr 47 ; Secondary ATA Hard Disk + +; Save the processor state, call the C interrupt handler and restore the +; stack frame. +intr_common_stub: + pusha + push ds + push es + push fs + push gs + mov ax, 0x10 ; Kernel data segment descriptor. + mov ds, ax + mov es, ax + mov fs, ax + mov gs, ax + mov eax, esp ; Push the stack. + push eax + mov eax, intr_handler + call eax + pop eax + pop gs + pop fs + pop es + pop ds + popa + add esp, 8 ; Clean up ISR info. + sti + iret ; I spent many hours debugging this... diff --git a/kern/io.h b/kern/io.h @@ -0,0 +1,78 @@ +#ifndef _KERNEL_IO_H_ +#define _KERNEL_IO_H_ + +#include <stdint.h> +#include "idt.h" + +struct region_descriptor; + +static inline void +lidt(struct region_desc *rd) +{ + __asm__ __volatile("lidt (%0)" : : "r" (rd)); +} + +static inline uint8_t +inb(uint16_t port) +{ + uint8_t res; + + __asm__ __volatile("in %%dx, %%al" : "=a" (res) : "d" (port)); + return (res); +} + +static inline void +outb(uint16_t port, uint8_t v) +{ + __asm__ __volatile("out %%al, %%dx" : : "a" (v), "d" (port)); +} + +static inline uint16_t +inw(uint16_t port) +{ + uint16_t res; + + __asm__ __volatile("in %%dx, %%ax" : "=a" (res) : "d" (port)); + return (res); +} + +static inline void +outw(uint16_t port, uint16_t v) +{ + __asm__ __volatile("out %%ax, %%dx" : : "a" (v), "d" (port)); +} + +static inline uint32_t +inl(uint16_t port) +{ + uint32_t res; + + __asm__ __volatile("in %%dx, %%eax" : "=a" (res) : "d" (port)); + return (res); +} + +static inline void +outl(uint16_t port, uint32_t v) +{ + __asm__ __volatile("out %%eax, %%dx" : : "a" (v), "d" (port)); +} + +static inline void +hlt(void) +{ + __asm__ __volatile("hlt"); +} + +static inline void +cli(void) +{ + __asm__ __volatile("cli"); +} + +static inline void +sti(void) +{ + __asm__ __volatile("sti"); +} + +#endif /* _KERNEL_IO_H_ */ diff --git a/kern/kbd.c b/kern/kbd.c @@ -1,9 +1,14 @@ -#include <sys/libk.h> - -#include <dev/kbd.h> - +#include "libk.h" +#include "kbd.h" #include "idt.h" +#define KBD_CMD 0x60 +#define KBD_PRESSED 0x80 +#define KBD_LSHIFT 0x2a +#define KBD_RSHIFT 0x36 +#define KBD_LSHIFT_REL 0xaa +#define KBD_RSHIFT_REL 0xb6 + static void kbd_callback(struct reg *); static unsigned char kbdus_upper[128] = { @@ -88,10 +93,10 @@ kbd_callback(struct reg *r) uint8_t sc; int shift = 0; - if ((sc = inb(IO_KBD)) & KBD_PRESSED) { + if ((sc = inb(KBD_CMD)) & KBD_PRESSED) { } else { /* TODO: shift */ - tty_putc(shift ? kbdus_upper[sc] : kbdus_lower[sc]); + vga_putc(shift ? kbdus_upper[sc] : kbdus_lower[sc]); } UNUSED(r); } @@ -99,6 +104,6 @@ kbd_callback(struct reg *r) void kbd_init(void) { - int_add_handler(1, kbd_callback); - printf("kbd on int 1\n"); + intr_register_handler(1, kbd_callback); + printf("kbd on irq %d\n", 1); } diff --git a/kern/kbd.h b/kern/kbd.h @@ -0,0 +1,6 @@ +#ifndef _KERNEL_KBD_H_ +#define _KERNEL_KBD_H_ + +void kbd_init(void); + +#endif /* _KERNEL_KBD_H_ */ diff --git a/kern/kern_main.c b/kern/kern_main.c @@ -1,22 +1,18 @@ -#include <sys/libk.h> - -#include <dev/kbd.h> - +#include "libk.h" +#include "kbd.h" #include "idt.h" #include "timer.h" -int +void kern_main(void) { - tty_clear(VGA_BLUE, VGA_WHITE); + vga_clear(VGA_BLACK, VGA_WHITE); idt_init(); timer_init(); kbd_init(); + sti(); /* Off to userland! */ for (;;); - - /* NOTREACHED */ - return (0); } diff --git a/kern/libk.c b/kern/libk.c @@ -1,13 +1,104 @@ -#include <sys/libk.h> +#include "libk.h" + +#define ZEROPAD 0x01 +#define SIGN 0x02 +#define PLUS 0x04 +#define SPACE 0x08 +#define LEFT 0x10 +#define SMALL 0x20 +#define SPECIAL 0x40 + +static int calc_width(const char **); +static int div(long *, int); +static char *itoa(char *, long, int, int, int, int); + +static int +calc_width(const char **s) +{ + int i = 0; + + while (isdigit(**s)) + i = i * 10 + *((*s)++) - '0'; + return (i); +} + +static int +div(long *n, int base) +{ + int res; + + res = (unsigned long)*n % (unsigned int)base; + *n = (unsigned long)*n / (unsigned int)base; + return (res); +} -/* TODO: implement */ static char * -itoa(int n, char *buf, int base) +itoa(char *str, long num, int base, int size, int precision, int flags) { - (void)n; - (void)base; + static const char digits[16] = "0123456789ABCDEF"; + char tmp[66]; + int i; + char c, locase, sign; + + locase = (flags & SMALL); + if (flags & LEFT) + flags &= ~ZEROPAD; + if (base < 2 || base > 16) + return (NULL); + c = (flags & ZEROPAD) ? '0' : ' '; + sign = 0; + if (flags & SIGN) { + if (num < 0) { + sign = '-'; + num = -num; + size--; + } else if (flags & PLUS) { + sign = '+'; + size--; + } else if (flags & SPACE) { + sign = ' '; + size--; + } + } + if (flags & SPECIAL) { + if (base == 8) + size--; + else if (base == 16) + size -= 2; + } + i = 0; + if (!num) + tmp[i++] = '0'; + else + while (num) + tmp[i++] = (digits[div(&num, base)] | locase); + if (i > precision) + precision = i; + size -= precision; + if (!(flags & (ZEROPAD | LEFT))) /* XXX: + instead of | ? */ + while (size-- > 0) + *str++ = ' '; + if (sign) + *str++ = sign; + if (flags & SPECIAL) { + if (base == 8) + *str++ = '0'; + else if (base == 16) { + *str++ = '0'; + *str++ = ('X' | locase); + } + } + if (!(flags & LEFT)) + while (size-- > 0) + *str++ = c; + while (i < precision--) + *str++ = '0'; + while (i-- > 0) + *str++ = tmp[i]; + while (size-- > 0) + *str++ = ' '; - return (buf); + return (str); } void * @@ -48,6 +139,18 @@ strlen(const char *str) return (s - str); } +size_t +strnlen(const char *str, size_t maxlen) +{ + const char *s = str; + + while (*s && maxlen) { + s++; + maxlen--; + } + return (s - str); +} + int strcmp(const char *s1, const char *s2) { @@ -59,49 +162,147 @@ strcmp(const char *s1, const char *s2) } int -vsprintf(char *buf, const char *fmt, va_list args) +isdigit(char c) +{ + return (c >= '0' && c <= '9'); +} + +int +vsprintf(char *buf, const char *fmt, va_list ap) { char *str, *s; - int base, i, n; + int base, i, n, flags, qual, size, precision; for (str = buf; *fmt != '\0'; fmt++) { - base = 10; if (*fmt != '%') { *str++ = *fmt; continue; } - if (*fmt == '%') { - switch (*(++fmt)) { - case 'c': - *str++ = (unsigned char)va_arg(args, int); - continue; - case 's': - s = va_arg(args, char *); - n = strlen(s); - for (i = 0; i < n; i++) - *str++ = *s++; - continue; - case 'p': - continue; - case 'd': /* FALLTHROUGH */ - case 'i': - n = va_arg(args, int); - break; - case 'u': - n = va_arg(args, unsigned int); - break; - case 'o': - base = 8; - break; - case 'X': - base = 16; - break; - case '%': - *str++ = '%'; - continue; + flags = 0; +repeat: + fmt++; + switch (*fmt) { + case '-': + flags |= LEFT; + goto repeat; + case '+': + flags |= PLUS; + goto repeat; + case ' ': + flags |= SPACE; + goto repeat; + case '#': + flags |= SPECIAL; + goto repeat; + case '0': + flags |= ZEROPAD; + goto repeat; + } + + size = -1; + if (isdigit(*fmt)) + size = calc_width(&fmt); + else if (*fmt == '*') { + fmt++; + if ((size = va_arg(ap, int)) < 0) { + size -= size; + flags |= LEFT; + } + } + + precision = -1; + if (*fmt == '.') { + fmt++; + if (isdigit(*fmt)) + precision = calc_width(&fmt); + else if (*fmt == '*') { + fmt++; + precision = va_arg(ap, int); + } + if (precision < 0) + precision = 0; + } + + qual = -1; + if (*fmt == 'h' || *fmt == 'l' || *fmt == 'L') + qual = *fmt++; + + base = 10; + switch (*fmt) { + case 'c': + if (!(flags & LEFT)) + while (--size > 0) + *str++ = ' '; + *str++ = (unsigned char)va_arg(ap, int); + while (--size > 0) + *str++ = ' '; + continue; + case 's': + s = va_arg(ap, char *); + n = strnlen(s, precision); + if (!(flags & LEFT)) + while (n < size--) + *str++ = ' '; + for (i = 0; i < n; i++) + *str++ = *s++; + while (n < size--) + *str++ = ' '; + continue; + case 'p': + if (size == -1) { + size = sizeof(void *) << 1; + flags |= ZEROPAD; + } + str = itoa(str, + (unsigned long)va_arg(ap, void *), 16, + size, precision, flags); + continue; + case 'n': + if (qual == 'l') { + long *ip = va_arg(ap, long *); + *ip = (str - buf); + } else { + int *ip = va_arg(ap, int *); + *ip = (str - buf); } - str = itoa(n, str, base); + continue; + case '%': + *str++ = '%'; + continue; + case 'd': /* FALLTHROUGH */ + case 'i': + flags |= SIGN; + break; + case 'u': + break; + case 'o': + base = 8; + break; + case 'x': /* FALLTHROUGH */ + flags |= SMALL; + case 'X': + base = 16; + break; + default: + *str++ = '%'; + if (*fmt) + *str++ = *fmt; + else + fmt--; + continue; } + + if (qual == 'l') + n = va_arg(ap, unsigned long); + else if (qual == 'h') { + n = (unsigned short)va_arg(ap, int); + if (flags & SIGN) + n = (short)n; + } else if (flags & SIGN) + n = va_arg(ap, int); + else + n = va_arg(ap, unsigned int); + str = itoa(str, n, base, size, precision, flags); } *str = '\0'; @@ -111,12 +312,12 @@ vsprintf(char *buf, const char *fmt, va_list args) int sprintf(char *buf, const char *fmt, ...) { - va_list args; + va_list ap; int n; - va_start(args, fmt); - n = vsprintf(buf, fmt, args); - va_end(args); + va_start(ap, fmt); + n = vsprintf(buf, fmt, ap); + va_end(ap); return (n); } @@ -125,26 +326,32 @@ int printf(const char *fmt, ...) { char buf[BUFSIZ]; - va_list args; + va_list ap; int n; - va_start(args, fmt); - n = vsprintf(buf, fmt, args); - va_end(args); - tty_write(buf); + va_start(ap, fmt); + n = vsprintf(buf, fmt, ap); + va_end(ap); + vga_write(buf); return (n); } +/* TODO: print regs */ void panic(const char *fmt, ...) { - va_list args; + char buf[BUFSIZ]; + va_list ap; + int n; cli(); - printf("Kernel panic!\n"); - va_start(args, fmt); - printf(fmt, args); - va_end(args); + vga_set_color(VGA_RED, VGA_WHITE); + printf("panic: "); + va_start(ap, fmt); + n = vsprintf(buf, fmt, ap); + va_end(ap); + vga_write(buf); + vga_set_color(VGA_BLACK, VGA_WHITE); hlt(); } diff --git a/kern/libk.h b/kern/libk.h @@ -0,0 +1,29 @@ +#ifndef _KERNEL_LIBK_H_ +#define _KERNEL_LIBK_H_ + +#include <stddef.h> +#include <stdint.h> +#include <stdarg.h> + +#include "io.h" +#include "vga.h" + +#define BUFSIZ 1024 + +#define ARRLEN(x) (sizeof(x) / sizeof(*(x))) +#define UNUSED(x) ((void)(x)) +#define sizeof_field(s, f) (sizeof(((t *)0)->f)) + +void *memset(void *, int, size_t); +void *memcpy(void *, const void *, size_t); +size_t strlen(const char *); +size_t strnlen(const char *, size_t); +int strcmp(const char *, const char *); +int isdigit(char); +int vsprintf(char *, const char *, va_list); +int sprintf(char *, const char *, ...); +int printf(const char *, ...); +//int kprintf(const char *, ...); +void panic(const char *, ...); + +#endif /* _KERNEL_LIBK_H_ */ diff --git a/kern/timer.c b/kern/timer.c @@ -1,7 +1,9 @@ -#include <sys/libk.h> - +#include "libk.h" #include "idt.h" +#define TIMER_CMD 0x43 +#define TIMER_DATA 0x40 + static void timer_callback(struct reg *); static uint32_t timer_ticks = 0; @@ -19,9 +21,9 @@ timer_init(void) const uint32_t hz = 60; uint32_t div = 1193180 / hz; - int_add_handler(0, timer_callback); - outb(IO_TIMER_CMD, 0x36); - outb(IO_TIMER_DATA, (uint8_t)(div & 0xff)); - outb(IO_TIMER_DATA, (uint8_t)((div >> 8) & 0xff)); - printf("timer on int 0\n"); + intr_register_handler(0, timer_callback); + outb(TIMER_CMD, 0x36); + outb(TIMER_DATA, (uint8_t)(div & 0xff)); + outb(TIMER_DATA, (uint8_t)((div >> 8) & 0xff)); + printf("timer on irq %d\n", 0); } diff --git a/kern/tty.c b/kern/tty.c @@ -1,136 +0,0 @@ -#include <sys/libk.h> - -#define VGA_MEM 0xb8000; -#define VGA_COLS 80 -#define VGA_ROWS 25 -#define TTY_PUTC(c) (((uint16_t)tty.color << 8) | (c)) - -struct tty_info { - volatile uint16_t *buf; - size_t row; - size_t col; - uint8_t color; -}; - -static struct tty_info tty; - -void -tty_clear(uint8_t fg, uint8_t bg) -{ - size_t y, x; - - tty_curs_enable(0x0e, 0x0f); /* Low cursor shape. */ - tty_curs_setpos(0, 0); - tty.row = 0; - tty.col = 0; - tty.buf = (uint16_t *)VGA_MEM; - tty_set_color(fg, bg); - for (x = 0; x < VGA_COLS; x++) - for (y = 0; y < VGA_ROWS; y++) - tty.buf[y * VGA_COLS + x] = TTY_PUTC(' '); -} - -void -tty_set_color(uint8_t fg, uint8_t bg) -{ - tty.color = fg | (bg << 4); -} - -void -tty_putc(char c) -{ - switch (c) { - case '\n': - tty.row++; - tty.col = 0; - break; - case '\b': - tty.col--; - tty.buf[tty.row * VGA_COLS + tty.col] = TTY_PUTC(' '); - break; - case '\r': - tty.col = 0; - break; - case '\t': - tty.col += 8; - break; - default: - tty.buf[tty.row * VGA_COLS + tty.col] = TTY_PUTC(c); - tty.col++; - } - - if (tty.row >= VGA_ROWS) { - tty.row = 0; - tty.col = 0; - } - if (tty.col >= VGA_COLS) { - tty.row++; - tty.col = 0; - } - tty_curs_setpos(tty.col, tty.row); -} - -void -tty_write(const char *str) -{ - while (*str != '\0') - tty_putc(*str++); -} - -/* - * The arguments `start` and `end` define the shape of the cursor. - * The lowest scaline value is 0x00 and the highest 0x0f. A cursor shape - * with `start = 0x00` and `end = 0x0f` is effectively a block cursor. - */ -void -tty_curs_enable(uint8_t start, uint8_t end) -{ - /* 0x0a: Low cursor shape */ - outb(IO_CURS_CMD, 0x0a); - outb(IO_CURS_DATA, (inb(IO_CURS_DATA) & 0xc0) | start); - outb(IO_CURS_CMD, 0x0b); - outb(IO_CURS_DATA, (inb(IO_CURS_DATA) & 0xe0) | end); -} - -void -tty_curs_disable(void) -{ - outb(IO_CURS_CMD, 0x0a); - /* - * Bit representation: - * 7 6 | 5 | 4 0 - * Unused | Disable | Shape - */ - outb(IO_CURS_DATA, 0x20); -} - -/* - * Returns the position in `y * VGA_COLS + x` format. - * x = pos % VGA_COLS - * y = pos / VGA_COLS - */ -uint16_t -tty_curs_getpos(void) -{ - uint16_t pos = 0; - - outb(IO_CURS_CMD, 0x0e); - pos |= (uint16_t)inb(IO_CURS_DATA) << 8; - outb(IO_CURS_CMD, 0x0f); - pos |= inb(IO_CURS_DATA); - - return (pos); -} - -void -tty_curs_setpos(int x, int y) -{ - uint16_t pos = y * VGA_COLS + x; - - /* Expect 8 highest bits */ - outb(IO_CURS_CMD, 0x0e); - outb(IO_CURS_DATA, (uint8_t)((pos >> 8) & 0xff)); - /* Expect 8 lowest bits */ - outb(IO_CURS_CMD, 0x0f); - outb(IO_CURS_DATA, (uint8_t)(pos & 0xff)); -} diff --git a/kern/vga.c b/kern/vga.c @@ -0,0 +1,139 @@ +#include "libk.h" + +#define VGA_MEM 0xb8000; +#define VGA_COLS 80 +#define VGA_ROWS 25 +#define VGA_PUTC(c) (((uint16_t)vga.color << 8) | (c)) + +#define CURS_CMD 0x3d4 +#define CURS_DATA 0x3d5 + +struct vga_info { + volatile uint16_t *buf; + size_t row; + size_t col; + uint8_t color; +}; + +static struct vga_info vga; + +void +vga_clear(uint8_t fg, uint8_t bg) +{ + size_t y, x; + + vga_curs_enable(0x0e, 0x0f); /* Low cursor shape. */ + vga_curs_setpos(0, 0); + vga.row = 0; + vga.col = 0; + vga.buf = (uint16_t *)VGA_MEM; + vga_set_color(fg, bg); + for (x = 0; x < VGA_COLS; x++) + for (y = 0; y < VGA_ROWS; y++) + vga.buf[y * VGA_COLS + x] = VGA_PUTC(' '); +} + +void +vga_set_color(uint8_t fg, uint8_t bg) +{ + vga.color = fg | (bg << 4); +} + +void +vga_putc(char c) +{ + switch (c) { + case '\n': + vga.row++; + vga.col = 0; + break; + case '\b': + vga.col--; + vga.buf[vga.row * VGA_COLS + vga.col] = VGA_PUTC(' '); + break; + case '\r': + vga.col = 0; + break; + case '\t': + vga.col += 8; + break; + default: + vga.buf[vga.row * VGA_COLS + vga.col] = VGA_PUTC(c); + vga.col++; + } + + if (vga.row >= VGA_ROWS) { + /* FIXME: scroll */ + vga_clear(VGA_BLACK, VGA_WHITE); + vga.row = 0; + vga.col = 0; + } + if (vga.col >= VGA_COLS) { + vga.row++; + vga.col = 0; + } + vga_curs_setpos(vga.col, vga.row); +} + +void +vga_write(const char *str) +{ + while (*str != '\0') + vga_putc(*str++); +} + +/* + * The arguments `start` and `end` define the shape of the cursor. + * The lowest scaline value is 0x00 and the highest 0x0f. A cursor shape + * with `start = 0x00` and `end = 0x0f` is effectively a block cursor. + */ +void +vga_curs_enable(uint8_t start, uint8_t end) +{ + /* 0x0a: Low cursor shape */ + outb(CURS_CMD, 0x0a); + outb(CURS_DATA, (inb(CURS_DATA) & 0xc0) | start); + outb(CURS_CMD, 0x0b); + outb(CURS_DATA, (inb(CURS_DATA) & 0xe0) | end); +} + +void +vga_curs_disable(void) +{ + outb(CURS_CMD, 0x0a); + /* + * Bit representation: + * 7 6 | 5 | 4 0 + * Unused | Disable | Shape + */ + outb(CURS_DATA, 0x20); +} + +/* + * Returns the position in `y * VGA_COLS + x` format. + * x = pos % VGA_COLS + * y = pos / VGA_COLS + */ +uint16_t +vga_curs_getpos(void) +{ + uint16_t pos = 0; + + outb(CURS_CMD, 0x0e); + pos |= (uint16_t)inb(CURS_DATA) << 8; + outb(CURS_CMD, 0x0f); + pos |= inb(CURS_DATA); + + return (pos); +} + +void +vga_curs_setpos(int x, int y) +{ + uint16_t pos = y * VGA_COLS + x; + + outb(CURS_CMD, 0x0e); + outb(CURS_DATA, (uint8_t)((pos >> 8) & 0xff)); + outb(CURS_CMD, 0x0f); + outb(CURS_DATA, (uint8_t)(pos & 0xff)); +} diff --git a/kern/vga.h b/kern/vga.h @@ -0,0 +1,34 @@ +#ifndef _KERNEL_VGA_H_ +#define _KERNEL_VGA_H_ + +#include <stddef.h> + +enum vga_color: uint8_t { + VGA_BLACK = 0, + VGA_BLUE, + VGA_GREEN, + VGA_CYAN, + VGA_RED, + VGA_MAGENTA, + VGA_BROWN, + VGA_LIGHT_GREY, + VGA_DARK_GREY, + VGA_LIGHT_BLUE, + VGA_LIGHT_GREEN, + VGA_LIGHT_CYAN, + VGA_LIGHT_RED, + VGA_LIGHT_MAGENTA, + VGA_LIGHT_BROWN, + VGA_WHITE, +}; + +void vga_clear(uint8_t, uint8_t); +void vga_set_color(uint8_t, uint8_t); +void vga_putc(char); +void vga_write(const char *); +void vga_curs_enable(uint8_t, uint8_t); +void vga_curs_disable(void); +void vga_curs_setpos(int, int); +uint16_t vga_curs_getpos(void); + +#endif /* _KERNEL_VGA_H_ */