From a2f94f569a4c99204de02814a20098a71527e913 Mon Sep 17 00:00:00 2001 From: John Tsiombikas Date: Thu, 26 Apr 2018 10:52:18 +0300 Subject: [PATCH] interrupts, timer, keyboard, segments, lots of kernel code --- Makefile | 6 +- src/asmops.h | 5 ++ src/config.h | 3 + src/desc.h | 10 +++ src/intr.c | 188 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ src/intr.h | 66 +++++++++++++++++++ src/intr_asm.S | 114 +++++++++++++++++++++++++++++++++ src/intrtab.h | 54 ++++++++++++++++ src/keyb.c | 132 ++++++++++++++++++++++++++++++++++++++ src/keyb.h | 42 +++++++++++++ src/kmain.c | 38 ++++++++++- src/panic.c | 48 ++++++++++++++ src/panic.h | 6 ++ src/regs.s | 54 ++++++++++++++++ src/segm.c | 124 ++++++++++++++++++++++++++++++++++++ src/segm.h | 19 ++++++ src/segm_asm.s | 47 ++++++++++++++ src/timer.c | 81 ++++++++++++++++++++++++ src/timer.h | 11 ++++ src/tss.h | 21 +++++++ src/vbioshack.s | 51 +++++++++++++++ 21 files changed, 1117 insertions(+), 3 deletions(-) create mode 100644 src/desc.h create mode 100644 src/intr.c create mode 100644 src/intr.h create mode 100644 src/intr_asm.S create mode 100644 src/intrtab.h create mode 100644 src/keyb.c create mode 100644 src/keyb.h create mode 100644 src/panic.c create mode 100644 src/panic.h create mode 100644 src/regs.s create mode 100644 src/segm.c create mode 100644 src/segm.h create mode 100644 src/segm_asm.s create mode 100644 src/timer.c create mode 100644 src/timer.h create mode 100644 src/tss.h create mode 100644 src/vbioshack.s diff --git a/Makefile b/Makefile index 1a2f734..0438d12 100644 --- a/Makefile +++ b/Makefile @@ -1,6 +1,7 @@ csrc = $(wildcard src/*.c) $(wildcard src/libc/*.c) ssrc = $(wildcard src/*.s) $(wildcard src/libc/*.s) $(wildcard src/boot/*.s) -obj = $(csrc:.c=.o) $(ssrc:.s=.o) +Ssrc = $(wildcard src/*.S) +obj = $(csrc:.c=.o) $(ssrc:.s=.o) $(Ssrc:.S=.o) dep = $(obj:.o=.d) elf = test bin = test.bin @@ -40,6 +41,9 @@ $(bin): $(elf) $(elf): $(obj) $(LD) -o $@ $(obj) -Map link.map $(LDFLAGS) +%.o: %.S + $(CC) -o $@ $(CFLAGS) -c $< + -include $(dep) %.d: %.c diff --git a/src/asmops.h b/src/asmops.h index 2578467..3ce2f17 100644 --- a/src/asmops.h +++ b/src/asmops.h @@ -7,6 +7,11 @@ #define disable_intr() asm volatile("cli") #define halt_cpu() asm volatile("hlt") +#define CALLER_EIP(eip) \ + asm volatile( \ + "mov (%%esp), %0\n\t" \ + : "=a" ((uint32_t)eip)) + static inline uint8_t inb(uint16_t port) { uint8_t res; diff --git a/src/config.h b/src/config.h index f01bf9d..fb361ca 100644 --- a/src/config.h +++ b/src/config.h @@ -1,6 +1,9 @@ #ifndef PCBOOT_CONFIG_H_ #define PCBOOT_CONFIG_H_ +/* frequency of generated timer ticks in hertz */ +#define TICK_FREQ_HZ 250 + #define CON_TEXTMODE #define CON_SERIAL diff --git a/src/desc.h b/src/desc.h new file mode 100644 index 0000000..adfee6e --- /dev/null +++ b/src/desc.h @@ -0,0 +1,10 @@ +#ifndef DESC_H_ +#define DESC_H_ + +#include + +typedef struct { + uint16_t d[4]; +} desc_t; + +#endif /* DESC_H_ */ diff --git a/src/intr.c b/src/intr.c new file mode 100644 index 0000000..1ace71e --- /dev/null +++ b/src/intr.c @@ -0,0 +1,188 @@ +#include +#include "intr.h" +#include "desc.h" +#include "segm.h" +#include "asmops.h" +#include "panic.h" + +#define SYSCALL_INT 0x80 + +/* IDT gate descriptor bits */ +#define GATE_TASK (5 << 8) +#define GATE_INTR (6 << 8) +#define GATE_TRAP (7 << 8) +#define GATE_DEFAULT (1 << 11) +#define GATE_PRESENT (1 << 15) + +/* PIC command and data ports */ +#define PIC1_CMD 0x20 +#define PIC1_DATA 0x21 +#define PIC2_CMD 0xa0 +#define PIC2_DATA 0xa1 + +/* PIC initialization command word 1 bits */ +#define ICW1_ICW4_NEEDED (1 << 0) +#define ICW1_SINGLE (1 << 1) +#define ICW1_INTERVAL4 (1 << 2) +#define ICW1_LEVEL (1 << 3) +#define ICW1_INIT (1 << 4) +/* PIC initialization command word 4 bits */ +#define ICW4_8086 (1 << 0) +#define ICW4_AUTO_EOI (1 << 1) +#define ICW4_BUF_SLAVE (1 << 3) /* 1000 */ +#define ICW4_BUF_MASTER (3 << 2) /* 1100 */ +#define ICW4_SPECIAL (1 << 4) + +/* PIC operation command word 2 bits */ +#define OCW2_EOI (1 << 5) + + +static void init_pic(int offset); +static void gate_desc(desc_t *desc, uint16_t sel, uint32_t addr, int dpl, int type); + +/* defined in intr_asm.S */ +void set_idt(uint32_t addr, uint16_t limit); +void intr_entry_default(void); + +/* the IDT (interrupt descriptor table) */ +static desc_t idt[256] __attribute__((aligned(8))); + +/* table of handler functions for all interrupts */ +static intr_func_t intr_func[256]; + +static struct intr_frame *cur_intr_frame; +static int eoi_pending; + + +void init_intr(void) +{ + int i; + + set_idt((uint32_t)idt, sizeof idt - 1); + + /* initialize all entry points and interrupt handlers */ + for(i=0; i<256; i++) { + set_intr_entry(i, intr_entry_default); + interrupt(i, 0); + } + + /* by including intrtab.h here (without ASM being defined) + * the series of INTR_ENTRY_* macros will be expanded to a series + * of function prototypes for all interrupt entry points and the + * corresponding calls to set_intr_entry to set up the IDT slots + */ +#include "intrtab.h" + + /* initialize the programmable interrupt controller + * setting up the maping of IRQs [0, 15] to interrupts [32, 47] + */ + init_pic(IRQ_OFFSET); + eoi_pending = 0; +} + +/* retrieve the current interrupt frame. + * returns 0 when called during init. + */ +struct intr_frame *get_intr_frame(void) +{ + return cur_intr_frame; +} + +/* set an interrupt handler function for a particular interrupt */ +void interrupt(int intr_num, intr_func_t func) +{ + intr_func[intr_num] = func; +} + +/* this function is called from all interrupt entry points + * it calls the appropriate interrupt handlers if available and handles + * sending an end-of-interrupt command to the PICs when finished. + */ +void dispatch_intr(struct intr_frame frm) +{ + cur_intr_frame = &frm; + + if(IS_IRQ(frm.inum)) { + eoi_pending = frm.inum; + } + + if(intr_func[frm.inum]) { + intr_func[frm.inum](frm.inum); + } else { + if(frm.inum < 32) { + panic("unhandled exception %d, error code: %d\n", frm.inum, frm.err); + } + printf("unhandled interrupt %d\n", frm.inum); + } + + disable_intr(); + if(eoi_pending) { + end_of_irq(INTR_TO_IRQ(eoi_pending)); + } +} + +static void init_pic(int offset) +{ + /* send ICW1 saying we'll follow with ICW4 later on */ + outb(ICW1_INIT | ICW1_ICW4_NEEDED, PIC1_CMD); + outb(ICW1_INIT | ICW1_ICW4_NEEDED, PIC2_CMD); + /* send ICW2 with IRQ remapping */ + outb(offset, PIC1_DATA); + outb(offset + 8, PIC2_DATA); + /* send ICW3 to setup the master/slave relationship */ + /* ... set bit3 = 3rd interrupt input has a slave */ + outb(4, PIC1_DATA); + /* ... set slave ID to 2 */ + outb(2, PIC2_DATA); + /* send ICW4 to set 8086 mode (no calls generated) */ + outb(ICW4_8086, PIC1_DATA); + outb(ICW4_8086, PIC2_DATA); + /* done, just reset the data port to 0 */ + outb(0, PIC1_DATA); + outb(0, PIC2_DATA); +} + +static void gate_desc(desc_t *desc, uint16_t sel, uint32_t addr, int dpl, int type) +{ + /* first 16bit part is the low 16bits of the entry address */ + desc->d[0] = addr & 0xffff; + /* second 16bit part is the segment selector for the entry code */ + desc->d[1] = sel; + /* third 16bit part has the privilege level, type, and present bit */ + desc->d[2] = ((dpl & 3) << 13) | type | GATE_DEFAULT | GATE_PRESENT; + /* last 16bit part is the high 16bits of the entry address */ + desc->d[3] = (addr & 0xffff0000) >> 16; +} + +#define IS_TRAP(n) ((n) >= 32 && !IS_IRQ(n)) +void set_intr_entry(int num, void (*handler)(void)) +{ + int type = IS_TRAP(num) ? GATE_TRAP : GATE_INTR; + + /* the syscall interrupt has to have a dpl of 3 otherwise calling it from + * user space will raise a general protection exception. All the rest should + * have a dpl of 0 to disallow user programs to execute critical interrupt + * handlers and possibly crashing the system. + */ + int dpl = (num == SYSCALL_INT) ? 3 : 0; + + gate_desc(idt + num, selector(SEGM_KCODE, 0), (uint32_t)handler, dpl, type); +} + +void end_of_irq(int irq) +{ + int intr_state = get_intr_flag(); + disable_intr(); + + if(!eoi_pending) { + return; + } + eoi_pending = 0; + + if(irq > 7) { + outb(OCW2_EOI, PIC2_CMD); + } + outb(OCW2_EOI, PIC1_CMD); + + set_intr_flag(intr_state); +} diff --git a/src/intr.h b/src/intr.h new file mode 100644 index 0000000..04c775c --- /dev/null +++ b/src/intr.h @@ -0,0 +1,66 @@ +#ifndef INTR_H_ +#define INTR_H_ + +#include +#include "asmops.h" + +/* offset used to remap IRQ numbers (+32) */ +#define IRQ_OFFSET 32 +/* conversion macros between IRQ and interrupt numbers */ +#define IRQ_TO_INTR(x) ((x) + IRQ_OFFSET) +#define INTR_TO_IRQ(x) ((x) - IRQ_OFFSET) +/* checks whether a particular interrupt is an remapped IRQ */ +#define IS_IRQ(n) ((n) >= IRQ_OFFSET && (n) < IRQ_OFFSET + 16) + +/* general purpose registers as they are pushed by pusha */ +struct registers { + uint32_t edi, esi, ebp, esp; + uint32_t ebx, edx, ecx, eax; +} __attribute__ ((packed)); + +/* structure used to pass the interrupt stack frame from the + * entry points to the C dispatch function. + */ +struct intr_frame { + /* registers pushed by pusha in intr_entry_* */ + struct registers regs; + /* data segment selectors */ + /* XXX removed: not needed unless we do dpl3 transitions + uint32_t ds, es, fs, gs; + */ + /* interrupt number and error code pushed in intr_entry_* */ + uint32_t inum, err; + /* pushed by CPU during interrupt entry */ + uint32_t eip, cs, eflags; + /* pushed by CPU during interrupt entry from user space */ + /* XXX removed: again, not doing user space currently + uint32_t esp, ss; + */ +} __attribute__ ((packed)); + + + +typedef void (*intr_func_t)(int); + + +void init_intr(void); + +struct intr_frame *get_intr_frame(void); + +/* install high level interrupt callback */ +void interrupt(int intr_num, intr_func_t func); + +/* install low-level interrupt vector in IDT + * must be able to handle EOI and return with iret + */ +void set_intr_entry(int num, void (*handler)(void)); + +/* defined in intr_asm.S */ +int get_intr_flag(void); +void set_intr_flag(int onoff); + +void intr_ret(struct intr_frame ifrm); + +void end_of_irq(int irq); + +#endif /* INTR_H_ */ diff --git a/src/intr_asm.S b/src/intr_asm.S new file mode 100644 index 0000000..ea0be36 --- /dev/null +++ b/src/intr_asm.S @@ -0,0 +1,114 @@ + .data + .align 4 + .short 0 +idtr_desc: +lim: .short 0 +addr: .long 0 + + .text +/* void set_idt(uint32_t addr, uint16_t limit) */ + .global set_idt +set_idt: + mov 4(%esp), %eax + mov %eax, addr + mov 8(%esp), %ax + mov %ax, lim + lidt (idtr_desc) + ret + +/* int get_intr_flag() */ + .global get_intr_flag +get_intr_flag: + pushf + popl %eax + # bit 9 of eflags is IF + shr $9, %eax + and $1, %eax + ret + +/* void set_intr_flag(int onoff) */ + .global set_intr_flag +set_intr_flag: + cmpl $0, 4(%esp) + jz 0f + sti + ret +0: cli + ret + +/* interrupt entry with error code macro + * this macro generates an interrupt entry point for the + * exceptions which include error codes in the stack frame + */ + .macro ientry_err n name + .globl intr_entry_\name +intr_entry_\name: + pushl $\n + jmp intr_entry_common + .endm + +/* interrupt entry without error code macro + * this macro generates an interrupt entry point for the interrupts + * and exceptions which do not include error codes in the stack frame + * it pushes a dummy error code (0), to make the stack frame identical + */ + .macro ientry_noerr n name + .globl intr_entry_\name +intr_entry_\name: + pushl $0 + pushl $\n + jmp intr_entry_common + .endm + +/* common code used by all entry points. calls dispatch_intr() + * defined in intr.c + */ + .extern dispatch_intr +intr_entry_common: + /* save general purpose registers */ + pusha + call dispatch_intr +intr_ret_local: + /* restore general purpose registers */ + popa + /* remove error code and intr num from stack */ + add $8, %esp + iret + +/* special case for the timer interrupt, to avoid all the overhead of + * going through the C interrupt dispatcher 250 times each second + */ + .extern nticks + .global intr_entry_fast_timer +intr_entry_fast_timer: + incl nticks + # signal end of interrupt + push %eax + mov $0x20, %al + out %al, $0x20 + pop %eax + iret + + +/* XXX not necessary for now, just leaving it in in case it's useful + * down the road. + * + * intr_ret is called by context_switch to return from the kernel + * to userspace. The argument is a properly formed intr_frame + * structure with the saved context of the new task. + * + * First thing to do is remove the return address pointing back + * to context_switch, which then leaves us with a proper interrupt + * stack frame, so we can jump right in the middle of the regular + * interrupt return code above. + */ + .global intr_ret +intr_ret: + add $4, %esp + jmp intr_ret_local + +/* by including interrupts.h with ASM defined, the macros above + * are expanded to generate all required interrupt entry points + */ +#define ASM +#include diff --git a/src/intrtab.h b/src/intrtab.h new file mode 100644 index 0000000..d89430e --- /dev/null +++ b/src/intrtab.h @@ -0,0 +1,54 @@ +#ifdef ASM +/* included from intr_asm.S */ +#define INTR_ENTRY_EC(n, name) ientry_err n, name +#define INTR_ENTRY_NOEC(n, name) ientry_noerr n, name +#else +/* included from intr.c inside init_intr() */ +#define INTR_ENTRY_EC(n, name) \ + void intr_entry_##name(void); \ + set_intr_entry(n, intr_entry_##name); +#define INTR_ENTRY_NOEC(n, name) INTR_ENTRY_EC(n, name) +#endif /* ASM */ + +/* faults/traps/aborts (plus NMI) */ +INTR_ENTRY_NOEC(0, div) +INTR_ENTRY_NOEC(1, debug) +INTR_ENTRY_NOEC(2, nmi) +INTR_ENTRY_NOEC(3, bp) +INTR_ENTRY_NOEC(4, overflow) +INTR_ENTRY_NOEC(5, bound) +INTR_ENTRY_NOEC(6, ill) +INTR_ENTRY_NOEC(7, nodev) +INTR_ENTRY_EC(8, dfault) +INTR_ENTRY_NOEC(9, copseg) +INTR_ENTRY_EC(10, tss) +INTR_ENTRY_EC(11, segpres) +INTR_ENTRY_EC(12, stack) +INTR_ENTRY_EC(13, prot) +INTR_ENTRY_EC(14, page) +INTR_ENTRY_NOEC(15, reserved) +INTR_ENTRY_NOEC(16, fpu) +INTR_ENTRY_EC(17, align) +INTR_ENTRY_NOEC(18, mce) +INTR_ENTRY_NOEC(19, sse) +/* redirected IRQs */ +INTR_ENTRY_NOEC(32, irq0) +INTR_ENTRY_NOEC(33, irq1) +INTR_ENTRY_NOEC(34, irq2) +INTR_ENTRY_NOEC(35, irq3) +INTR_ENTRY_NOEC(36, irq4) +INTR_ENTRY_NOEC(37, irq5) +INTR_ENTRY_NOEC(38, irq6) +INTR_ENTRY_NOEC(39, irq7) +INTR_ENTRY_NOEC(40, irq8) +INTR_ENTRY_NOEC(41, irq9) +INTR_ENTRY_NOEC(42, irq10) +INTR_ENTRY_NOEC(43, irq11) +INTR_ENTRY_NOEC(44, irq12) +INTR_ENTRY_NOEC(45, irq13) +INTR_ENTRY_NOEC(46, irq14) +INTR_ENTRY_NOEC(47, irq15) +/* system call interrupt */ +INTR_ENTRY_NOEC(128, syscall) +/* default interrupt */ +INTR_ENTRY_NOEC(255, default) diff --git a/src/keyb.c b/src/keyb.c new file mode 100644 index 0000000..c5a672c --- /dev/null +++ b/src/keyb.c @@ -0,0 +1,132 @@ +#include "keyb.h" +#include "intr.h" +#include "asmops.h" + +#define KB_IRQ 1 +#define KB_PORT 0x60 + +/* table with rough translations from set 1 scancodes to ASCII-ish */ +static int scantbl[] = { + 0, KB_ESC, '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '-', '=', '\b', /* 0 - e */ + '\t', 'q', 'w', 'e', 'r', 't', 'y', 'u', 'i', 'o', 'p', '[', ']', '\n', /* f - 1c */ + KB_LCTRL, 'a', 's', 'd', 'f', 'g', 'h', 'j', 'k', 'l', ';', '\'', '`', /* 1d - 29 */ + KB_LSHIFT, '\\', 'z', 'x', 'c', 'v', 'b', 'n', 'm', ',', '.', '/', KB_RSHIFT, /* 2a - 36 */ + KB_NUM_MUL, KB_LALT, ' ', KB_CAPSLK, KB_F1, KB_F2, KB_F3, KB_F4, KB_F5, KB_F6, KB_F7, KB_F8, KB_F9, KB_F10, /* 37 - 44 */ + KB_NUMLK, KB_SCRLK, KB_NUM_7, KB_NUM_8, KB_NUM_9, KB_NUM_MINUS, KB_NUM_4, KB_NUM_5, KB_NUM_6, KB_NUM_PLUS, /* 45 - 4e */ + KB_NUM_1, KB_NUM_2, KB_NUM_3, KB_NUM_0, KB_NUM_DOT, KB_SYSRQ, 0, 0, KB_F11, KB_F12, /* 4d - 58 */ + 0, 0, 0, 0, 0, 0, 0, /* 59 - 5f */ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 60 - 6f */ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 /* 70 - 7f */ +}; + +static void kbintr(); + +#define BUFSZ 64 +#define ADVANCE(x) ((x) = ((x) + 1) & (BUFSZ - 1)) + +static int buffer[BUFSZ]; +static int buf_ridx, buf_widx; + +static unsigned int num_pressed; +static unsigned char keystate[256]; + +void kb_init(void) +{ + interrupt(IRQ_TO_INTR(KB_IRQ), kbintr); +} + +int kb_isdown(int key) +{ + switch(key) { + case KB_ANY: + return num_pressed; + + case KB_ALT: + return keystate[KB_LALT] + keystate[KB_RALT]; + + case KB_CTRL: + return keystate[KB_LCTRL] + keystate[KB_RCTRL]; + } + return keystate[key]; +} + +void kb_wait(void) +{ + int key; + while((key = kb_getkey()) == -1) { + /* put the processor to sleep while waiting for keypresses, but first + * make sure interrupts are enabled, or we'll sleep forever + */ + enable_intr(); + halt_cpu(); + } + kb_putback(key); +} + +int kb_getkey(void) +{ + int res; + + if(buf_ridx == buf_widx) { + return -1; + } + res = buffer[buf_ridx]; + ADVANCE(buf_ridx); + return res; +} + +void kb_putback(int key) +{ + /* go back a place */ + if(--buf_ridx < 0) { + buf_ridx += BUFSZ; + } + + /* if the write end hasn't caught up with us, go back one place + * and put it there, otherwise just overwrite the oldest key which + * is right where we were. + */ + if(buf_ridx == buf_widx) { + ADVANCE(buf_ridx); + } + + buffer[buf_ridx] = key; +} + +static void kbintr() +{ + unsigned char code; + int key, press; + + code = inb(KB_PORT); + + if(code >= 128) { + press = 0; + code -= 128; + + if(num_pressed > 0) { + num_pressed--; + } + } else { + press = 1; + + num_pressed++; + } + + key = scantbl[code]; + + if(press) { + /* append to buffer */ + buffer[buf_widx] = key; + ADVANCE(buf_widx); + /* if the write end overtook the read end, advance the read end + * too, to discard the oldest keypress from the buffer + */ + if(buf_widx == buf_ridx) { + ADVANCE(buf_ridx); + } + } + + /* and update keystate table */ + keystate[key] = press; +} diff --git a/src/keyb.h b/src/keyb.h new file mode 100644 index 0000000..6b8f24c --- /dev/null +++ b/src/keyb.h @@ -0,0 +1,42 @@ +#ifndef KEYB_H_ +#define KEYB_H_ + +#define KB_ANY (-1) +#define KB_ALT (-2) +#define KB_CTRL (-3) +#define KB_SHIFT (-4) + +/* special keys */ +enum { + KB_ESC = 27, + KB_INSERT, KB_DEL, KB_HOME, KB_END, KB_PGUP, KB_PGDN, + KB_LEFT, KB_RIGHT, KB_UP, KB_DOWN, + KB_NUM_DOT, KB_NUM_ENTER, KB_NUM_PLUS, KB_NUM_MINUS, KB_NUM_MUL, KB_NUM_DIV, + KB_NUM_0, KB_NUM_1, KB_NUM_2, KB_NUM_3, KB_NUM_4, + KB_NUM_5, KB_NUM_6, KB_NUM_7, KB_NUM_8, KB_NUM_9, + KB_BACKSP = 127, + + KB_LALT, KB_RALT, + KB_LCTRL, KB_RCTRL, + KB_LSHIFT, KB_RSHIFT, + KB_F1, KB_F2, KB_F3, KB_F4, KB_F5, KB_F6, + KB_F7, KB_F8, KB_F9, KB_F10, KB_F11, KB_F12, + KB_CAPSLK, KB_NUMLK, KB_SCRLK, KB_SYSRQ +}; + +void kb_init(void); + +/* Boolean predicate for testing the current state of a particular key. + * You may also pass KB_ANY to test if any key is held down. + */ +int kb_isdown(int key); + +/* waits for any keypress */ +void kb_wait(void); + +/* removes and returns a single key from the input buffer. */ +int kb_getkey(void); + +void kb_putback(int key); + +#endif /* KEYB_H_ */ diff --git a/src/kmain.c b/src/kmain.c index f1533b2..6996908 100644 --- a/src/kmain.c +++ b/src/kmain.c @@ -1,11 +1,45 @@ #include +#include +#include "segm.h" +#include "intr.h" +#include "keyb.h" +#include "timer.h" #include "contty.h" -static int foo = 42; +void set_mode13h(void); +void logohack(void); void pcboot_main(void) { + init_segm(); + init_intr(); + kb_init(); con_init(); - printf("hello world: %d\n", foo); + /* initialize the timer */ + init_timer(); + + enable_intr(); + + printf("PCBoot kernel initialized\n"); + + for(;;) { + int c; + + halt_cpu(); + while((c = kb_getkey()) >= 0) { + if(c >= KB_F1 && c <= KB_F12) { + set_mode13h(); + logohack(); + } + if(isprint(c)) { + printf("key: %d '%c' \n", c, (char)c); + } else { + printf("key: %d \n", c); + } + } + if((nticks % 250) == 0) { + printf("ticks: %ld\r", nticks); + } + } } diff --git a/src/panic.c b/src/panic.c new file mode 100644 index 0000000..af79a9e --- /dev/null +++ b/src/panic.c @@ -0,0 +1,48 @@ +#include +#include +#include +#include "asmops.h" + +struct all_registers { + uint32_t eax, ebx, ecx, edx; + uint32_t esp, ebp, esi, edi; + uint32_t eflags; + uint32_t cs, ss, ds, es, fs, gs; + uint32_t cr0, cr1, cr2, cr3; +}; + +/* defined in regs.S */ +void get_regs(struct all_registers *regs); + +void panic(const char *fmt, ...) +{ + va_list ap; + struct all_registers regs; + uint32_t eip; + + disable_intr(); + + memset(®s, 0, sizeof regs); + get_regs(®s); + + CALLER_EIP(eip); + + printf("~~~~~ pcboot panic ~~~~~\n"); + va_start(ap, fmt); + vprintf(fmt, ap); + va_end(ap); + + printf("\nRegisters:\n"); + printf("eax: %x, ebx: %x, ecx: %x, edx: %x\n", regs.eax, regs.ebx, regs.ecx, regs.edx); + printf("esp: %x, ebp: %x, esi: %x, edi: %x\n", regs.esp, regs.ebp, regs.esi, regs.edi); + printf("eip: %x, eflags: %x\n", eip, regs.eflags); + printf("cr0: %x, cr1: %x, cr2: %x, cr3: %x\n", regs.cr0, regs.cr1, regs.cr2, regs.cr3); + printf("cs: %x (%d|%d)\n", regs.cs, regs.cs >> 3, regs.cs & 3); + printf("ss: %x (%d|%d)\n", regs.ss, regs.ss >> 3, regs.ss & 3); + printf("ds: %x (%d|%d)\n", regs.ds, regs.ds >> 3, regs.ds & 3); + printf("es: %x (%d|%d)\n", regs.es, regs.es >> 3, regs.es & 3); + printf("fs: %x (%d|%d)\n", regs.fs, regs.fs >> 3, regs.fs & 3); + printf("gs: %x (%d|%d)\n", regs.gs, regs.gs >> 3, regs.gs & 3); + + for(;;) halt_cpu(); +} diff --git a/src/panic.h b/src/panic.h new file mode 100644 index 0000000..fbf5fa1 --- /dev/null +++ b/src/panic.h @@ -0,0 +1,6 @@ +#ifndef PANIC_H_ +#define PANIC_H_ + +void panic(const char *fmt, ...); + +#endif /* PANIC_H_ */ diff --git a/src/regs.s b/src/regs.s new file mode 100644 index 0000000..713112e --- /dev/null +++ b/src/regs.s @@ -0,0 +1,54 @@ + .text + .align 4 + + .globl get_regs +get_regs: + pushl %ebp + movl %esp, %ebp + + pushl %edx + movl 8(%ebp), %edx + + movl %eax, (%edx) + movl %ebx, 4(%edx) + movl %ecx, 8(%edx) + + # juggle edx + movl %edx, %eax + popl %edx + movl %edx, 12(%eax) + pushl %edx + movl %eax, %edx + + # those two are pointless in a function + movl %esp, 16(%edx) + movl %ebp, 20(%edx) + + movl %esi, 24(%edx) + movl %edi, 28(%edx) + + pushf + popl %eax + movl %eax, 32(%edx) + + movw %cs, 36(%edx) + movw %ss, 40(%edx) + movw %ds, 44(%edx) + movw %es, 48(%edx) + movw %fs, 52(%edx) + movw %gs, 56(%edx) + + pushl %ebx + movl %cr0, %ebx + movl %ebx, 60(%edx) + #movl %cr1, %ebx + #movl %ebx, 64(%edx) + movl %cr2, %ebx + movl %ebx, 68(%edx) + movl %cr3, %ebx + movl %ebx, 72(%edx) + popl %ebx + + popl %edx + popl %ebp + ret diff --git a/src/segm.c b/src/segm.c new file mode 100644 index 0000000..d2f940a --- /dev/null +++ b/src/segm.c @@ -0,0 +1,124 @@ +#include +#include "segm.h" +#include "desc.h" +#include "tss.h" + +/* bits for the 3rd 16bt part of the descriptor */ +#define BIT_ACCESSED (1 << 8) +#define BIT_WR (1 << 9) +#define BIT_RD (1 << 9) +#define BIT_EXP_DOWN (1 << 10) +#define BIT_CONFORMING (1 << 10) +#define BIT_CODE (1 << 11) +#define BIT_NOSYS (1 << 12) +#define BIT_PRESENT (1 << 15) +/* TSS busy bit */ +#define BIT_BUSY (1 << 9) + +/* bits for the last 16bit part of the descriptor */ +#define BIT_BIG (1 << 6) +#define BIT_DEFAULT (1 << 6) +#define BIT_GRAN (1 << 7) + +enum {TYPE_DATA, TYPE_CODE}; + +/* we need the following bit pattern at the 8th bit excluding the busy bit: 1001 */ +#define TSS_TYPE_BITS (9 << 8) + +static void segm_desc(desc_t *desc, uint32_t base, uint32_t limit, int dpl, int type); +static void task_desc(desc_t *desc, uint32_t base, uint32_t limit, int dpl); + +/* these functions are implemented in segm-asm.S */ +void setup_selectors(uint16_t code, uint16_t data); +void set_gdt(uint32_t addr, uint16_t limit); +void set_task_reg(uint16_t tss_selector); + + +/* our global descriptor table */ +static desc_t gdt[6] __attribute__((aligned(8))); + + +void init_segm(void) +{ + memset(gdt, 0, sizeof gdt); + segm_desc(gdt + SEGM_KCODE, 0, 0xffffffff, 0, TYPE_CODE); + segm_desc(gdt + SEGM_KDATA, 0, 0xffffffff, 0, TYPE_DATA); + segm_desc(gdt + SEGM_UCODE, 0, 0xffffffff, 3, TYPE_CODE); + segm_desc(gdt + SEGM_UDATA, 0, 0xffffffff, 3, TYPE_DATA); + + set_gdt((uint32_t)gdt, sizeof gdt - 1); + + setup_selectors(selector(SEGM_KCODE, 0), selector(SEGM_KDATA, 0)); +} + +/* constructs a GDT selector based on index and priviledge level */ +uint16_t selector(int idx, int rpl) +{ + return (idx << 3) | (rpl & 3); +} + +void set_tss(uint32_t addr) +{ + task_desc(gdt + SEGM_TASK, addr, sizeof(struct task_state) - 1, 3); + set_task_reg(selector(SEGM_TASK, 0)); +} + +static void segm_desc(desc_t *desc, uint32_t base, uint32_t limit, int dpl, int type) +{ + desc->d[0] = limit & 0xffff; /* low order 16bits of limit */ + desc->d[1] = base & 0xffff; /* low order 16bits of base */ + + /* third 16bit part contains the last 8 bits of base, the 2 priviledge + * level bits starting on bit 13, present flag on bit 15, and type bits + * starting from bit 8 + */ + desc->d[2] = ((base >> 16) & 0xff) | ((dpl & 3) << 13) | BIT_PRESENT | + BIT_NOSYS | (type == TYPE_DATA ? BIT_WR : (BIT_RD | BIT_CODE)); + + /* last 16bit part contains the last nibble of limit, the last byte of + * base, and the granularity and deafult/big flags in bits 23 and 22 resp. + */ + desc->d[3] = ((limit >> 16) & 0xf) | ((base >> 16) & 0xff00) | BIT_GRAN | BIT_BIG; +} + +static void task_desc(desc_t *desc, uint32_t base, uint32_t limit, int dpl) +{ + desc->d[0] = limit & 0xffff; + desc->d[1] = base & 0xffff; + + desc->d[2] = ((base >> 16) & 0xff) | ((dpl & 3) << 13) | BIT_PRESENT | + TSS_TYPE_BITS; /* XXX busy ? */ + desc->d[3] = ((limit >> 16) & 0xf) | ((base >> 16) & 0xff00) | BIT_GRAN; +} +/* +static void dbg_print_gdt(void) +{ + int i; + + printf("Global Descriptor Table\n"); + printf("-----------------------\n"); + + for(i=0; i<6; i++) { + print_desc(gdt + i); + } +} + +static void print_desc(desc_t *desc) +{ + uint32_t base, limit; + int dpl, g, db, l, avl, p, s, type; + char *type_str; + + base = (uint32_t)desc->d[1] | ((uint32_t)(desc->d[2] & 0xff) << 16) | ((uint32_t)(desc->d[3] >> 8) << 24); + limit = (uint32_t)desc->d[0] | ((uint32_t)(desc->d[3] & 0xf) << 16); + dpl = (desc->d[2] >> 13) & 3; + type = (desc->d[2] >> 8) & 0xf; + g = (desc->d[3] >> 23) & 1; + db = (desc->d[3] >> 22) & 1; + l = (desc->d[3] >> 21) & 1; + avl = (desc->d[3] >> 20) & 1; + + p = (desc->d[2] >> 15) & 1; + s = (desc->d[2] >> 12) & 1; +} +*/ diff --git a/src/segm.h b/src/segm.h new file mode 100644 index 0000000..b687c19 --- /dev/null +++ b/src/segm.h @@ -0,0 +1,19 @@ +#ifndef SEGM_H_ +#define SEGM_H_ + +#define SEGM_KCODE 1 +#define SEGM_KDATA 2 +#define SEGM_UCODE 3 +#define SEGM_UDATA 4 +#define SEGM_TASK 5 + +#ifndef ASM +void init_segm(void); + +uint16_t selector(int idx, int rpl); + +void set_tss(uint32_t addr); +#endif /* ASM */ + + +#endif /* SEGM_H_ */ diff --git a/src/segm_asm.s b/src/segm_asm.s new file mode 100644 index 0000000..6ff0f0f --- /dev/null +++ b/src/segm_asm.s @@ -0,0 +1,47 @@ + .data + .align 4 +# memory reserved for setup_selectors +off: .long 0 +seg: .short 0 +# memory reserved for set_gdt +lim: .short 0 +addr: .long 0 + + .text +# setup_selectors(uint16_t code, uint16_t data) +# loads the requested selectors to all the selector registers + .globl setup_selectors +setup_selectors: + # set data selectors directly + movl 8(%esp), %eax + movw %ax, %ss + movw %ax, %es + movw %ax, %ds + movw %ax, %gs + movw %ax, %fs + # set cs using a long jump + movl 4(%esp), %eax + movw %ax, (seg) + movl $ldcs, (off) + ljmp *off +ldcs: + ret + +# set_gdt(uint32_t addr, uint16_t limit) +# loads the GDTR with the new address and limit for the GDT + .globl set_gdt +set_gdt: + movl 4(%esp), %eax + movl %eax, (addr) + movw 8(%esp), %ax + movw %ax, (lim) + lgdt (lim) + ret + +# set_task_reg(uint16_t tss_selector) +# loads the TSS selector in the task register + .globl set_task_reg +set_task_reg: + mov 4(%esp), %eax + ltr 4(%esp) + ret diff --git a/src/timer.c b/src/timer.c new file mode 100644 index 0000000..937b56d --- /dev/null +++ b/src/timer.c @@ -0,0 +1,81 @@ +#include +#include "intr.h" +#include "asmops.h" +#include "timer.h" +#include "config.h" + +/* frequency of the oscillator driving the 8254 timer */ +#define OSC_FREQ_HZ 1193182 + +/* macro to divide and round to the nearest integer */ +#define DIV_ROUND(a, b) ((a) / (b) + ((a) % (b)) / ((b) / 2)) + +/* I/O ports connected to the 8254 */ +#define PORT_DATA0 0x40 +#define PORT_DATA1 0x41 +#define PORT_DATA2 0x42 +#define PORT_CMD 0x43 + +/* command bits */ +#define CMD_CHAN0 0 +#define CMD_CHAN1 (1 << 6) +#define CMD_CHAN2 (2 << 6) +#define CMD_RDBACK (3 << 6) + +#define CMD_LATCH 0 +#define CMD_ACCESS_LOW (1 << 4) +#define CMD_ACCESS_HIGH (2 << 4) +#define CMD_ACCESS_BOTH (3 << 4) + +#define CMD_OP_INT_TERM 0 +#define CMD_OP_ONESHOT (1 << 1) +#define CMD_OP_RATE (2 << 1) +#define CMD_OP_SQWAVE (3 << 1) +#define CMD_OP_SOFT_STROBE (4 << 1) +#define CMD_OP_HW_STROBE (5 << 1) + +#define CMD_MODE_BIN 0 +#define CMD_MODE_BCD 1 + + +#define MSEC_TO_TICKS(ms) ((ms) * TICK_FREQ_HZ / 1000) + +struct timer_event { + int dt; /* remaining ticks delta from the previous event */ + struct timer_event *next; +}; + +/* defined in intr_asm.S */ +void intr_entry_fast_timer(void); + +static struct timer_event *evlist; + + +void init_timer(void) +{ + /* calculate the reload count: round(osc / freq) */ + int reload_count = DIV_ROUND(OSC_FREQ_HZ, TICK_FREQ_HZ); + + /* set the mode to square wave for channel 0, both low + * and high reload count bytes will follow... + */ + outb(CMD_CHAN0 | CMD_ACCESS_BOTH | CMD_OP_SQWAVE, PORT_CMD); + + /* write the low and high bytes of the reload count to the + * port for channel 0 + */ + outb(reload_count & 0xff, PORT_DATA0); + outb((reload_count >> 8) & 0xff, PORT_DATA0); + + /* set the timer interrupt handler */ + /*interrupt(IRQ_TO_INTR(0), timer_handler);*/ + /* set low level fast timer interrupt routine directly in the IDT */ + set_intr_entry(IRQ_TO_INTR(0), intr_entry_fast_timer); +} + +/* +static void timer_handler(int inum) +{ + nticks++; +} +*/ diff --git a/src/timer.h b/src/timer.h new file mode 100644 index 0000000..7f985d5 --- /dev/null +++ b/src/timer.h @@ -0,0 +1,11 @@ +#ifndef _TIMER_H_ +#define _TIMER_H_ + +unsigned long nticks; + +void init_timer(void); + +int sys_sleep(int sec); +void sleep(unsigned long msec); + +#endif /* _TIMER_H_ */ diff --git a/src/tss.h b/src/tss.h new file mode 100644 index 0000000..3a74642 --- /dev/null +++ b/src/tss.h @@ -0,0 +1,21 @@ +#ifndef TSS_H_ +#define TSS_H_ + +#include + +struct task_state { + uint32_t prev_task; + uint32_t esp0, ss0; /* we only ever set these two values */ + uint32_t esp1, ss1; + uint32_t esp2, ss2; + uint32_t cr3; + uint32_t eip; + uint32_t eflags; + uint32_t eax, ecx, edx, ebx; + uint32_t esp, ebp, esi, edi; + uint32_t es, cs, ss, ds, fs, gs; + uint32_t ldt_sel; + uint16_t trap, iomap_addr; +} __attribute__((packed)); + +#endif /* TSS_H_ */ diff --git a/src/vbioshack.s b/src/vbioshack.s new file mode 100644 index 0000000..7b3c20d --- /dev/null +++ b/src/vbioshack.s @@ -0,0 +1,51 @@ + .text + + .align 4 + .short 0 +saved_idtr: +idtlim: .short 0 +idtaddr:.long 0 + + .short 0 +saved_gdtr: +gdtlim: .short 0 +gdtaddr:.long 0 + + .short 0 +rmidt: .short 0x3ff + .long 0 + + # drop back to real mode to set video mode hack + .global set_mode13h +set_mode13h: + cli + #sgdt (saved_gdtr) + sidt (saved_idtr) + lidt (rmidt) + + mov %cr0, %eax + and $0xfffe, %ax + mov %eax, %cr0 + jmp 0f + +0: xor %ax, %ax + mov %ax, %ds + mov %ax, %es + mov %ax, %ss + + #mov $0x13, %ax + #int $0x10 + + mov %cr0, %eax + or $1, %ax + mov %eax, %cr0 + jmp 0f + +0: mov $0x10, %ax + mov %ax, %ds + mov %ax, %es + mov %ax, %ss + + sidt (saved_idtr) + sti + ret -- 1.7.10.4