interrupts, timer, keyboard, segments, lots of kernel code
authorJohn Tsiombikas <nuclear@member.fsf.org>
Thu, 26 Apr 2018 07:52:18 +0000 (10:52 +0300)
committerJohn Tsiombikas <nuclear@member.fsf.org>
Thu, 26 Apr 2018 07:52:18 +0000 (10:52 +0300)
21 files changed:
Makefile
src/asmops.h
src/config.h
src/desc.h [new file with mode: 0644]
src/intr.c [new file with mode: 0644]
src/intr.h [new file with mode: 0644]
src/intr_asm.S [new file with mode: 0644]
src/intrtab.h [new file with mode: 0644]
src/keyb.c [new file with mode: 0644]
src/keyb.h [new file with mode: 0644]
src/kmain.c
src/panic.c [new file with mode: 0644]
src/panic.h [new file with mode: 0644]
src/regs.s [new file with mode: 0644]
src/segm.c [new file with mode: 0644]
src/segm.h [new file with mode: 0644]
src/segm_asm.s [new file with mode: 0644]
src/timer.c [new file with mode: 0644]
src/timer.h [new file with mode: 0644]
src/tss.h [new file with mode: 0644]
src/vbioshack.s [new file with mode: 0644]

index 1a2f734..0438d12 100644 (file)
--- 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
index 2578467..3ce2f17 100644 (file)
@@ -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;
index f01bf9d..fb361ca 100644 (file)
@@ -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 (file)
index 0000000..adfee6e
--- /dev/null
@@ -0,0 +1,10 @@
+#ifndef DESC_H_
+#define DESC_H_
+
+#include <inttypes.h>
+
+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 (file)
index 0000000..1ace71e
--- /dev/null
@@ -0,0 +1,188 @@
+#include <stdio.h>
+#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 (file)
index 0000000..04c775c
--- /dev/null
@@ -0,0 +1,66 @@
+#ifndef INTR_H_
+#define INTR_H_
+
+#include <inttypes.h>
+#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 (file)
index 0000000..ea0be36
--- /dev/null
@@ -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 <intrtab.h>
diff --git a/src/intrtab.h b/src/intrtab.h
new file mode 100644 (file)
index 0000000..d89430e
--- /dev/null
@@ -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 (file)
index 0000000..c5a672c
--- /dev/null
@@ -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 (file)
index 0000000..6b8f24c
--- /dev/null
@@ -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_ */
index f1533b2..6996908 100644 (file)
@@ -1,11 +1,45 @@
 #include <stdio.h>
+#include <ctype.h>
+#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 (file)
index 0000000..af79a9e
--- /dev/null
@@ -0,0 +1,48 @@
+#include <stdio.h>
+#include <string.h>
+#include <stdarg.h>
+#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(&regs, 0, sizeof regs);
+       get_regs(&regs);
+
+       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 (file)
index 0000000..fbf5fa1
--- /dev/null
@@ -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 (file)
index 0000000..713112e
--- /dev/null
@@ -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 (file)
index 0000000..d2f940a
--- /dev/null
@@ -0,0 +1,124 @@
+#include <string.h>
+#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 (file)
index 0000000..b687c19
--- /dev/null
@@ -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 (file)
index 0000000..6ff0f0f
--- /dev/null
@@ -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 (file)
index 0000000..937b56d
--- /dev/null
@@ -0,0 +1,81 @@
+#include <stdio.h>
+#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 (file)
index 0000000..7f985d5
--- /dev/null
@@ -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 (file)
index 0000000..3a74642
--- /dev/null
+++ b/src/tss.h
@@ -0,0 +1,21 @@
+#ifndef TSS_H_
+#define TSS_H_
+
+#include <inttypes.h>
+
+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 (file)
index 0000000..7b3c20d
--- /dev/null
@@ -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