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:
D | include/dev/kbd.h | | | 12 | ------------ |
M | include/stdarg.h | | | 6 | +++--- |
D | include/sys/io.h | | | 79 | ------------------------------------------------------------------------------- |
D | include/sys/libk.h | | | 27 | --------------------------- |
D | include/sys/tty.h | | | 34 | ---------------------------------- |
M | kern/Makefile | | | 10 | +++++----- |
D | kern/boot.asm | | | 302 | ------------------------------------------------------------------------------ |
A | kern/boot.s | | | 302 | ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
M | kern/idt.c | | | 263 | ++++++++++++++++++++++++++++++++++++++++++------------------------------------- |
M | kern/idt.h | | | 113 | +++++++++++++++++++++++++++---------------------------------------------------- |
D | kern/int.asm | | | 146 | ------------------------------------------------------------------------------- |
A | kern/intr.s | | | 121 | +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
A | kern/io.h | | | 78 | ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
M | kern/kbd.c | | | 21 | +++++++++++++-------- |
A | kern/kbd.h | | | 6 | ++++++ |
M | kern/kern_main.c | | | 14 | +++++--------- |
M | kern/libk.c | | | 313 | +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++-------------- |
A | kern/libk.h | | | 29 | +++++++++++++++++++++++++++++ |
M | kern/timer.c | | | 16 | +++++++++------- |
D | kern/tty.c | | | 136 | ------------------------------------------------------------------------------- |
A | kern/vga.c | | | 139 | +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
A | kern/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_ */