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
$(elf): $(obj)
$(LD) -o $@ $(obj) -Map link.map $(LDFLAGS)
+%.o: %.S
+ $(CC) -o $@ $(CFLAGS) -c $<
+
-include $(dep)
%.d: %.c
#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;
#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
--- /dev/null
+#ifndef DESC_H_
+#define DESC_H_
+
+#include <inttypes.h>
+
+typedef struct {
+ uint16_t d[4];
+} desc_t;
+
+#endif /* DESC_H_ */
--- /dev/null
+#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);
+}
--- /dev/null
+#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_ */
--- /dev/null
+ .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>
--- /dev/null
+#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)
--- /dev/null
+#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;
+}
--- /dev/null
+#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_ */
#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);
+ }
+ }
}
--- /dev/null
+#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(®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();
+}
--- /dev/null
+#ifndef PANIC_H_
+#define PANIC_H_
+
+void panic(const char *fmt, ...);
+
+#endif /* PANIC_H_ */
--- /dev/null
+ .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
--- /dev/null
+#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;
+}
+*/
--- /dev/null
+#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_ */
--- /dev/null
+ .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
--- /dev/null
+#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++;
+}
+*/
--- /dev/null
+#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_ */
--- /dev/null
+#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_ */
--- /dev/null
+ .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