]> Chaos Git - misc/eidos.git/commitdiff
GDT / IDT seem functional-ish
authorJon Feldman <chaos.kagami@gmail.com>
Mon, 20 Feb 2017 09:12:58 +0000 (04:12 -0500)
committerJon Feldman <chaos.kagami@gmail.com>
Mon, 20 Feb 2017 09:12:58 +0000 (04:12 -0500)
17 files changed:
Makefile
common/kernel_main.c
common/stdc/memcmp.c [new file with mode: 0644]
common/stdc/memcpy.c [new file with mode: 0644]
common/stdc/memmove.c [new file with mode: 0644]
common/stdc/memset.c [new file with mode: 0644]
common/stdc/strlen.c [new file with mode: 0644]
i686/entry.S
i686/link.ld
i686/utils.c
include/memory.h [new file with mode: 0644]
include/module_console.h
include/stdc/cdefs.h [new file with mode: 0644]
include/stdc/string.h [new file with mode: 0644]
module/biosvga/module.c
module/console.c
module/serial/module.c [new file with mode: 0644]

index ee60ab55540111b64cb962ec2fa4e50601c0ef9c..c78b257bf9882e83aa615ba627b73c889cb3fd48 100644 (file)
--- a/Makefile
+++ b/Makefile
@@ -3,9 +3,17 @@
 # The only one that needs changing is the assembler
 # rule, as we use nasm instead of GNU as.
 
+CONF=-DCONF_EARLY_KPRINTF=1
+
 TGT=i686
 
-SOURCES=$(TGT)/entry.o $(TGT)/utils.o module/biosvga/module.o module/console.o common/kernel_main.o
+SOURCES= \
+    $(TGT)/entry.o \
+    $(TGT)/utils.o \
+    module/biosvga/module.o module/serial/module.o module/console.o \
+    common/stdc/memmove.o common/stdc/strlen.o common/stdc/memcmp.o common/stdc/memset.o common/stdc/memcpy.o \
+    common/kernel_main.o
+
 LDSC=$(TGT)/link.ld
 
 TGTNAM=$(TGT)-elf
@@ -35,10 +43,10 @@ link: $(SOURCES)
        $(LD) $(LDFLAGS) -o kernel $(LIBS) $(SOURCES)
 
 %.o: %.c
-       $(CC) $(CFLAGS) $(CPPFLAGS) $(ASFLAGS) -c -o $@ $<
+       $(CC) $(CFLAGS) $(CONF) $(CPPFLAGS) $(ASFLAGS) -c -o $@ $<
 
 %.o: %.cxx
-       $(CXX) $(CXXFLAGS) $(CPPFLAGS) $(ASFLAGS) -c -o $@ $<
+       $(CXX) $(CXXFLAGS) $(CONF) $(CPPFLAGS) $(ASFLAGS) -c -o $@ $<
 
 %.o: %.S
        $(AS) $(ASFLAGS) -c -o $@ $<
index 308f747d0a953a98c444846943a9f43d09a0960d..53c994ab4d561871e576ab8c63d26d0e7ef7b510 100644 (file)
@@ -6,34 +6,14 @@ int kernel_main(struct multiboot *mboot_ptr)
 {
     console_init();
 
-    kwrite("1\n");
-    kwrite("2\n");
-    kwrite("3\n");
-    kwrite("4\n");
-    kwrite("5\n");
-    kwrite("6\n");
-    kwrite("7\n");
-    kwrite("8\n");
-    kwrite("9\n");
-    kwrite("10\n");
-    kwrite("11\n");
-    kwrite("12\n");
-    kwrite("13\n");
-    kwrite("14\n");
-    kwrite("15\n");
-    kwrite("16\n");
-    kwrite("17\n");
-    kwrite("18\n");
-    kwrite("19\n");
-    kwrite("20\n");
-    kwrite("21\n");
-    kwrite("22\n");
-    kwrite("23\n");
-    kwrite("24\n");
-    kwrite("25\n");
-    kwrite("26\n");
+    kprintf("1%c%% %s %X\n", 'c', "whoop", 0xDEADBEEF);
 
     console_deinit();
 
+    asm volatile("int $4");
+    asm volatile("int $3");
+
+    while(1);
+
     return 0xDEADBEEF;
 }
diff --git a/common/stdc/memcmp.c b/common/stdc/memcmp.c
new file mode 100644 (file)
index 0000000..576a242
--- /dev/null
@@ -0,0 +1,13 @@
+#include <string.h>
+
+int memcmp(const void* aptr, const void* bptr, size_t size) {
+       const unsigned char* a = (const unsigned char*) aptr;
+       const unsigned char* b = (const unsigned char*) bptr;
+       for (size_t i = 0; i < size; i++) {
+               if (a[i] < b[i])
+                       return -1;
+               else if (b[i] < a[i])
+                       return 1;
+       }
+       return 0;
+}
diff --git a/common/stdc/memcpy.c b/common/stdc/memcpy.c
new file mode 100644 (file)
index 0000000..1834816
--- /dev/null
@@ -0,0 +1,9 @@
+#include <string.h>
+
+void* memcpy(void* restrict dstptr, const void* restrict srcptr, size_t size) {
+       unsigned char* dst = (unsigned char*) dstptr;
+       const unsigned char* src = (const unsigned char*) srcptr;
+       for (size_t i = 0; i < size; i++)
+               dst[i] = src[i];
+       return dstptr;
+}
diff --git a/common/stdc/memmove.c b/common/stdc/memmove.c
new file mode 100644 (file)
index 0000000..ceac1e1
--- /dev/null
@@ -0,0 +1,14 @@
+#include <string.h>
+
+void* memmove(void* dstptr, const void* srcptr, size_t size) {
+       unsigned char* dst = (unsigned char*) dstptr;
+       const unsigned char* src = (const unsigned char*) srcptr;
+       if (dst < src) {
+               for (size_t i = 0; i < size; i++)
+                       dst[i] = src[i];
+       } else {
+               for (size_t i = size; i != 0; i--)
+                       dst[i-1] = src[i-1];
+       }
+       return dstptr;
+}
diff --git a/common/stdc/memset.c b/common/stdc/memset.c
new file mode 100644 (file)
index 0000000..449d1e7
--- /dev/null
@@ -0,0 +1,8 @@
+#include <string.h>
+
+void* memset(void* bufptr, int value, size_t size) {
+       unsigned char* buf = (unsigned char*) bufptr;
+       for (size_t i = 0; i < size; i++)
+               buf[i] = (unsigned char) value;
+       return bufptr;
+}
diff --git a/common/stdc/strlen.c b/common/stdc/strlen.c
new file mode 100644 (file)
index 0000000..8ac0dbe
--- /dev/null
@@ -0,0 +1,8 @@
+#include <string.h>
+
+size_t strlen(const char* str) {
+       size_t len = 0;
+       while (str[len])
+               len++;
+       return len;
+}
index abb6a381c6d4532b6bf6e703cce4dff5e9aa821e..649c07d4fa8b8839252bc148a8c3e5669dfd1b68 100644 (file)
@@ -1,3 +1,5 @@
+/* Multiboot garbage. */
+
 .set ALIGN,    1<<0             # align loaded modules on page boundaries
 .set MEMINFO,  1<<1             # provide memory map
 .set FLAGS,    ALIGN | MEMINFO  # this is the Multiboot 'flag' field
 .long FLAGS
 .long CHECKSUM
 
-.section .bss
-.align 16
+/* Stack. */
+.section .bootstrap_stack, "aw", @nobits
 stack_bottom:
-.skip 16384 # 16 KiB
+.skip 16384 // 16 KiB
 stack_top:
 
+.set KERNEL_VBASE, 0xC0000000
+.set KERNEL_PNUM,  (KERNEL_VBASE >> 22)
+
+.section .data
+.align 0x1000
+boot_pgdir:
+    .int 0x83
+    .fill (KERNEL_PNUM - 1), 4, 0
+    .int 0x83
+    .fill (1024 - KERNEL_PNUM - 1), 4, 0
+
+/* Our actual entrypoint. */
 .section .text
 .global _start
 .type _start, @function
 _start:
-       mov $stack_top, %esp
+    // Map our kernel at KERNEL_VBASE and enable paging
+    mov    $(boot_pgdir - KERNEL_VBASE), %ecx
+    mov    %ecx,%cr3
+
+    mov    %cr4,%ecx
+    or     $0x10,%ecx
+    mov    %ecx,%cr4
+
+    mov    %cr0,%ecx
+    or     $0x80000000,%ecx
+    mov    %ecx,%cr0
+
+    lea    .init, %ecx
+    jmp    *%ecx
 
-        push %ebx
+.init:
+    // Unmap page 0 (it's not needed now)
+    movl   $0, boot_pgdir
+    invlpg 0
 
-       call kernel_main
+    mov $stack_top, %esp
 
-       cli
-.halt: hlt
-       jmp .halt
+    push %ebx
+
+    call i686_init
+
+    call kernel_main
+
+    cli
+.halt:
+    hlt
+    jmp .halt
 
 .size _start, . - _start
+
+.global gdt_flush
+.type gdt_flush, @function
+gdt_flush:
+    mov    0x4(%esp),%eax
+    lgdt  (%eax)
+    mov    $0x10,%ax
+    mov    %eax,%ds
+    mov    %eax,%es
+    mov    %eax,%fs
+    mov    %eax,%gs
+    mov    %eax,%ss
+    lea    .flush, %eax
+    jmp *%eax
+    .flush:
+        ret
+
+.size gdt_flush, . - gdt_flush
+
+.macro ISR index
+    .global isr_\index
+    .type isr_\index , @function
+    isr_\index :
+        push $0
+        push $\index
+        jmp isr_stub
+.endm
+
+.macro ISR_E index
+    .global isr_\index
+    .type isr_\index , @function
+     isr_\index :
+         push $\index
+         jmp isr_stub
+.endm
+
+ISR 0
+ISR 1
+ISR 2
+ISR 3
+ISR 4
+ISR 5
+ISR 6
+ISR 7
+ISR_E 8
+ISR 9
+ISR_E 10
+ISR_E 11
+ISR_E 12
+ISR_E 13
+ISR_E 14
+ISR 15
+ISR 16
+ISR_E 17
+ISR 18
+ISR 19
+ISR 20
+ISR 21
+ISR 22
+ISR 23
+ISR 24
+ISR 25
+ISR 26
+ISR 27
+ISR 28
+ISR 29
+ISR_E 30
+ISR 31
+
+isr_stub:
+        pusha
+
+        mov %ds, %ax
+        push %eax
+
+        mov    $0x10,%ax
+        mov    %ax,%ds
+        mov    %ax,%es
+        mov    %ax,%fs
+        mov    %ax,%gs
+
+        push %esp
+
+        cld
+        call isr_handler
+
+        pop    %eax
+
+        pop    %eax
+        mov    %ax,%ds
+        mov    %ax,%es
+        mov    %ax,%fs
+        mov    %ax,%gs
+
+        popa
+        add $8, %esp
+        iret
+
+.global idt_flush
+.type idt_flush, @function
+idt_flush:
+    mov    0x4(%esp),%eax
+    lidt  (%eax)
+    ret
index 88b0e0132d9fe02e046e36039d61f35ab0b109ce..07074a9448f5ddf3bfc2d78c22b4fa81b59b7d30 100644 (file)
@@ -6,38 +6,39 @@ ENTRY(_start)
    kernel image. */
 SECTIONS
 {
-       /* Begin putting sections at 1 MiB, a conventional place for kernels to be
-          loaded at by the bootloader. */
-       . = 1M;
-
-       /* First put the multiboot header, as it is required to be put very early
-          early in the image or the bootloader won't recognize the file format.
-          Next we'll put the .text section. */
-       .text BLOCK(4K) : ALIGN(4K)
+       /* Begin putting sections at 1 MiB (+3GB virt) */
+       . = 0xC0100000;
+
+        _kernel_start = .;
+
+       .text ALIGN (4K) : AT (ADDR (.text) - 0xC0000000)
        {
                *(.multiboot)
                *(.text)
        }
 
-       /* Read-only data. */
-       .rodata BLOCK(4K) : ALIGN(4K)
+       .rodata ALIGN (4K) : AT (ADDR (.rodata) - 0xC0000000)
        {
                *(.rodata)
        }
 
-       /* Read-write data (initialized) */
-       .data BLOCK(4K) : ALIGN(4K)
+       .data ALIGN (4K) : AT (ADDR (.data) - 0xC0000000)
        {
                *(.data)
        }
 
-       /* Read-write data (uninitialized) and stack */
-       .bss BLOCK(4K) : ALIGN(4K)
+       .bss ALIGN (4K) : AT (ADDR (.bss) - 0xC0000000)
        {
+                _sbss = .;
                *(COMMON)
                *(.bss)
+               *(.bootstrap_stack)
+                _ebss = .;
        }
 
        /* The compiler may produce other sections, by default it will put them in
           a segment with the same name. Simply add stuff here as needed. */
+
+       /* Add a symbol that indicates the end address of the kernel. */
+       _kernel_end = .;
 }
index dbef8784533df429115ee6323ee9015e2875bf82..9c5382e1e85b876148e1d149a1401e33939c9276 100644 (file)
@@ -1,4 +1,7 @@
 #include <stdint.h>
+#include <stddef.h>
+
+#include <stdc/string.h>
 
 void outb(uint16_t port, uint8_t val)
 {
@@ -41,3 +44,218 @@ uint32_t inl(uint16_t port)
                    : "Nd"(port) );
     return ret;
 }
+
+
+// Lets us access our ASM functions from our C code.
+extern void gdt_flush(uint32_t);
+
+// This structure contains the value of one GDT entry.
+// We use the attribute 'packed' to tell GCC not to change
+// any of the alignment in the structure.
+struct gdt_entry_struct
+{
+   uint16_t limit_low;           // The lower 16 bits of the limit.
+   uint16_t base_low;            // The lower 16 bits of the base.
+   uint8_t  base_middle;         // The next 8 bits of the base.
+   uint8_t  access;              // Access flags, determine what ring this segment can be used in.
+   uint8_t  granularity;
+   uint8_t  base_high;           // The last 8 bits of the base.
+} __attribute__((packed));
+typedef struct gdt_entry_struct gdt_entry_t;
+
+struct gdt_ptr_struct
+{
+   uint16_t limit;               // The upper 16 bits of all selector limits.
+   uint32_t base;                // The address of the first gdt_entry_t struct.
+}
+ __attribute__((packed));
+typedef struct gdt_ptr_struct gdt_ptr_t;
+
+gdt_entry_t gdt_entries[5];
+gdt_ptr_t   gdt_ptr;
+
+// Set the value of one GDT entry.
+static void gdt_set_gate(int32_t num, uint32_t base, uint32_t limit, uint8_t access, uint8_t gran)
+{
+   gdt_entries[num].base_low    = (base & 0xFFFF);
+   gdt_entries[num].base_middle = (base >> 16) & 0xFF;
+   gdt_entries[num].base_high   = (base >> 24) & 0xFF;
+
+   gdt_entries[num].limit_low   = (limit & 0xFFFF);
+   gdt_entries[num].granularity = (limit >> 16) & 0x0F;
+
+   gdt_entries[num].granularity |= gran & 0xF0;
+   gdt_entries[num].access      = access;
+}
+
+static void init_gdt()
+{
+    gdt_ptr.limit = (sizeof(gdt_entry_t) * 5) - 1;
+    gdt_ptr.base  = (uint32_t)&gdt_entries;
+
+    gdt_set_gate(0, 0, 0, 0, 0);                // Null segment
+    gdt_set_gate(1, 0, 0xFFFFFFFF, 0x9A, 0xCF); // Code segment
+    gdt_set_gate(2, 0, 0xFFFFFFFF, 0x92, 0xCF); // Data segment
+    gdt_set_gate(3, 0, 0xFFFFFFFF, 0xFA, 0xCF); // User mode code segment
+    gdt_set_gate(4, 0, 0xFFFFFFFF, 0xF2, 0xCF); // User mode data segment
+
+    gdt_flush((uint32_t)&gdt_ptr);
+}
+
+// A struct describing an interrupt gate.
+struct idt_entry_struct
+{
+   uint16_t base_lo;             // The lower 16 bits of the address to jump to when this interrupt fires.
+   uint16_t sel;                 // Kernel segment selector.
+   uint8_t  always0;             // This must always be zero.
+   uint8_t  flags;               // More flags. See documentation.
+   uint16_t base_hi;             // The upper 16 bits of the address to jump to.
+} __attribute__((packed));
+typedef struct idt_entry_struct idt_entry_t;
+
+// A struct describing a pointer to an array of interrupt handlers.
+// This is in a format suitable for giving to 'lidt'.
+struct idt_ptr_struct
+{
+   uint16_t limit;
+   uint32_t base;                // The address of the first element in our idt_entry_t array.
+} __attribute__((packed));
+typedef struct idt_ptr_struct idt_ptr_t;
+
+idt_entry_t idt_entries[256];
+idt_ptr_t   idt_ptr;
+
+static void idt_set_gate(uint8_t num, uint32_t base, uint16_t sel, uint8_t flags)
+{
+   idt_entries[num].base_lo = base & 0xFFFF;
+   idt_entries[num].base_hi = (base >> 16) & 0xFFFF;
+
+   idt_entries[num].sel     = sel;
+   idt_entries[num].always0 = 0;
+   // We must uncomment the OR below when we get to using user-mode.
+   // It sets the interrupt gate's privilege level to 3.
+   idt_entries[num].flags   = flags /* | 0x60 */;
+}
+
+struct interrupt_frame
+{
+   uint32_t ds;                  // Data segment selector
+   uint32_t edi, esi, ebp, reserve, ebx, edx, ecx, eax; // Pushed by pusha.
+   uint32_t int_no, err_code;    // Interrupt number and error code (if applicable)
+   uint32_t eip, cs, eflags, esp, ss; // Pushed by the processor automatically.
+};
+
+char *strs[] = {
+    "divide by zero",
+    "debug exception",
+    "non maskable interrupt",
+    "breakpoint",
+    "into detected overflow",
+    "out of bounds exception",
+    "invalid opcode exception",
+    "no coprocessor exception",
+    "double fault",
+    "coprocessor segment overrun",
+    "bad tss",
+    "segment not present",
+    "stack fault",
+    "general protection fault",
+    "page fault",
+    "unknown interrupt",
+    "coprocessor fault",
+    "alignment check exception",
+    "machine check exception"
+};
+
+void isr_handler(struct interrupt_frame* esp) {
+    kprintf("int %x: ", esp->int_no);
+    if (esp->int_no <= 18) {
+        kprintf("%s\n", strs[esp->int_no]);
+    } else {
+        kprintf("reserved\n");
+    }
+}
+
+extern void isr_0();
+extern void isr_1();
+extern void isr_2();
+extern void isr_3();
+extern void isr_4();
+extern void isr_5();
+extern void isr_6();
+extern void isr_7();
+extern void isr_8();
+extern void isr_9();
+extern void isr_10();
+extern void isr_11();
+extern void isr_12();
+extern void isr_13();
+extern void isr_14();
+extern void isr_15();
+extern void isr_16();
+extern void isr_17();
+extern void isr_18();
+extern void isr_19();
+extern void isr_20();
+extern void isr_21();
+extern void isr_22();
+extern void isr_23();
+extern void isr_24();
+extern void isr_25();
+extern void isr_26();
+extern void isr_27();
+extern void isr_28();
+extern void isr_29();
+extern void isr_30();
+extern void isr_31();
+
+#define ISR_CALL(N) \
+    idt_set_gate( N , (uint32_t) isr_##N , 0x08, 0x8E )
+
+static void init_idt()
+{
+    idt_ptr.limit = sizeof(idt_entry_t) * 256 - 1;
+    idt_ptr.base  = (uint32_t)&idt_entries;
+
+    memset(&idt_entries, 0, sizeof(idt_entry_t)*256);
+
+    ISR_CALL(0);
+    ISR_CALL(1);
+    ISR_CALL(2);
+    ISR_CALL(3);
+    ISR_CALL(4);
+    ISR_CALL(5);
+    ISR_CALL(6);
+    ISR_CALL(7);
+    ISR_CALL(8);
+    ISR_CALL(9);
+    ISR_CALL(10);
+    ISR_CALL(11);
+    ISR_CALL(12);
+    ISR_CALL(13);
+    ISR_CALL(14);
+    ISR_CALL(15);
+    ISR_CALL(16);
+    ISR_CALL(17);
+    ISR_CALL(18);
+    ISR_CALL(19);
+    ISR_CALL(20);
+    ISR_CALL(21);
+    ISR_CALL(22);
+    ISR_CALL(23);
+    ISR_CALL(24);
+    ISR_CALL(25);
+    ISR_CALL(26);
+    ISR_CALL(27);
+    ISR_CALL(28);
+    ISR_CALL(29);
+    ISR_CALL(30);
+    ISR_CALL(31);
+
+    idt_flush((uint32_t)&idt_ptr);
+}
+
+void i686_init() {
+    init_gdt();
+    init_idt();
+}
diff --git a/include/memory.h b/include/memory.h
new file mode 100644 (file)
index 0000000..2c73b82
--- /dev/null
@@ -0,0 +1,4 @@
+#define memcpy __builtin_memcpy
+#define memcmp __builtin_memcmp
+#define strcpy __builtin_strcpy
+#define strcmp __builtin_strcmp
index 895bcb61ef80947cbf9d08f2b37fda01bc9df8b9..02dbf9483a91a0deb51291ca23fc478e2d9a0442 100644 (file)
@@ -1,9 +1,12 @@
+#include <stdarg.h>
+
 typedef void     (*console_put_f)    (char,     char,  char,  uint16_t, uint16_t);
 typedef void     (*console_get_f)    (char*,    char*, char*, uint16_t, uint16_t);
 typedef void     (*console_cursor_f) (uint16_t, uint16_t);
 typedef uint16_t (*console_width_f)  ();
 typedef uint16_t (*console_height_f) ();
 typedef void     (*console_refresh_f)();
+typedef int      (*console_tty_f)    ();
 
 struct console_module {
     console_put_f     put;
@@ -12,6 +15,7 @@ struct console_module {
     console_width_f   width;
     console_height_f  height;
     console_refresh_f refresh;
+    console_tty_f     tty;
 };
 
 void console_register_module(struct console_module*);
@@ -21,4 +25,6 @@ void console_deinit();
 
 void kputc(char);
 void kwrite(char*);
+void kvsprintf(char *buffer, const char* fmt_con, va_list ap);
+void kprintf(const char* fmt, ...);
 
diff --git a/include/stdc/cdefs.h b/include/stdc/cdefs.h
new file mode 100644 (file)
index 0000000..7f51871
--- /dev/null
@@ -0,0 +1,6 @@
+#ifndef _SYS_CDEFS_H
+#define _SYS_CDEFS_H 1
+
+#define __eidos_kernel_stdc 1
+
+#endif
diff --git a/include/stdc/string.h b/include/stdc/string.h
new file mode 100644 (file)
index 0000000..31a6266
--- /dev/null
@@ -0,0 +1,21 @@
+#ifndef _STRING_H
+#define _STRING_H 1
+
+#include <sys/cdefs.h>
+#include <stddef.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+int memcmp(const void*, const void*, size_t);
+void* memcpy(void* __restrict, const void* __restrict, size_t);
+void* memmove(void*, const void*, size_t);
+void* memset(void*, int, size_t);
+size_t strlen(const char*);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
index bbbaab40a0c71ffd3099f8bd8d4674fe9ad5544d..afea9262f6c05b729ce3e9a5d083b4b59ad47a51 100644 (file)
@@ -2,7 +2,7 @@
 #include <module.h>
 #include <module_console.h>
 
-uint16_t *video_mem = (uint16_t*)0xB8000;
+uint16_t *video_mem = (uint16_t*)0xC00B8000;
 
 void biosvga_put(char c, char color_fg, char color_bg, uint16_t x, uint16_t y) {
     uint8_t attr = (color_bg << 4) | (color_fg & 0xF);
@@ -38,9 +38,14 @@ void biosvga_refresh() {
 
 }
 
+int biosvga_tty() {
+    return 1;
+}
+
 struct console_module biosvga_module = {
     .put    = biosvga_put,
     .get    = biosvga_get,
+    .tty    = biosvga_tty,
     .cursor = biosvga_cursor,
     .width  = biosvga_width,
     .height = biosvga_height,
index 6cc6ae95a1260182a437d43d4597c2b7ad47bba2..3f997981e00a76ff8fcadae64a4e5772f3ea10bd 100644 (file)
@@ -3,26 +3,38 @@ actually printing to; in the future, adding a new backend for framebuffer render
 
 #include <stdint.h>
 #include <stddef.h>
+#include <stdarg.h>
 #include <module.h>
 #include <module_console.h>
 
 struct console_module *active;
+struct console_module *reg[2];
 uint16_t x, y, tab_w;
 
 void biosvga_init();
 void biosvga_deinit();
 
+void console_mark_active(int idx) {
+    active = reg[idx];
+}
+
 void console_register_module(struct console_module* module) {
-    active = module;
+    reg[1] = reg[0];
+    reg[0] = module;
 }
 
 void console_unregister_module(struct console_module* module) {
-    if (active == module)
-        active = NULL;
+    if (reg[0] == module)
+        reg[0] = reg[1];
+    if (reg[1] == module)
+        reg[1] = NULL;
 }
 
 // TODO - Should this be part of the backend? Scrolling is slow.
 void scroll() {
+    if (active->tty() == 0)
+        return;
+
     if (y >= active->height()) {
         for(int cy = 1; cy <= active->height(); cy++) {
             for(int cx = 0; cx <= active->width(); cx++) {
@@ -41,6 +53,9 @@ void scroll() {
 
 // TODO - Should this be part of the backend? Scrolling is slow.
 void clear() {
+    if (active->tty() == 0)
+        return;
+
     x = y = 0;
     for(int cy = 0; cy < active->height(); cy++) {
         for(int cx = 0; cx < active->width(); cx++) {
@@ -75,7 +90,8 @@ void kputc(char c) {
     }
 
     scroll();
-    active->cursor(x, y);
+    if (active->tty() == 1)
+        active->cursor(x, y);
 }
 
 void kwrite(char* str) {
@@ -85,18 +101,117 @@ void kwrite(char* str) {
     }
 }
 
+int strcatlen(char* a, char* b) {
+    char* o = b;
+    while(*b != 0) {
+        *a = *b;
+        a++;
+        b++;
+    }
+    return b - o;
+}
+
+int dumphex(char* buf, const char* index, unsigned int num) {
+    uint8_t *num_8 = (uint8_t *)&num;
+    char *obuf = buf;
+    for (int i = 3; i >= 0; i--) {
+        uint8_t high = (num_8[i] >> 4) & 0xf;
+        uint8_t low = num_8[i] & 0xf;
+
+        *buf = (index)[high]; buf++;
+        *buf = (index)[low];  buf++;
+    }
+
+    return buf - obuf;
+}
+
+void kvsprintf(char *buffer, const char* fmt_con, va_list ap) {
+    char* fmt = (char*)fmt_con;
+    char* buf = buffer;
+    while(*fmt != 0) {
+        if (*fmt == '%') {
+            // Format string.
+            ++fmt;
+            switch (*fmt) {
+                case 'c':
+                    *buf = (char)va_arg(ap, int);
+                    buf++;
+                    fmt++;
+                    break;
+                case '%':
+                    *buf = '%';
+                    buf++;
+                    fmt++;
+                    break;
+                case 's':
+                    buf += strcatlen(buf, va_arg(ap, char*));
+                    fmt++;
+                    break;
+                case 'x':
+                    buf += dumphex(buf, "0123456789abcdef", va_arg(ap, unsigned int));
+                    ++fmt;
+                    break;
+                case 'X':
+                    buf += dumphex(buf, "0123456789ABCDEF", va_arg(ap, unsigned int));
+                    ++fmt;
+                    break;
+                case 'd':
+                case 'u':
+                default:
+                    ++fmt;
+                    break;
+            }
+        } else {
+            *buf = *fmt;
+            ++buf;
+            ++fmt;
+        }
+    }
+
+    *buf = 0;
+}
+
+char buf[4096];
+
+void kprintf(const char* fmt, ...) {
+    va_list ap;
+    va_start(ap, fmt);
+
+    kvsprintf(buf, fmt, ap);
+
+    va_end(ap);
+
+    kwrite(buf);
+}
+
+void defer_kprintf(const char* fmt, ...) {
+    va_list ap;
+    va_start(ap, fmt);
+
+    kvsprintf(buf, fmt, ap);
+
+    va_end(ap);
+}
+
+void flush_defer_kprintf() {
+    kwrite(buf);
+}
+
 void console_init() {
     x = y = 0;
     tab_w = 4;
 
     // Temporary
+    serial_init();
     biosvga_init();
 
+    console_mark_active(0);
+
     clear();
 }
 
 void console_deinit() {
     // Temporary
     biosvga_deinit();
+    serial_deinit();
 }
-
diff --git a/module/serial/module.c b/module/serial/module.c
new file mode 100644 (file)
index 0000000..2e7d2cf
--- /dev/null
@@ -0,0 +1,44 @@
+#include <stdint.h>
+#include <module.h>
+#include <module_console.h>
+
+#define PORT 0x3f8   /* COM1 */
+
+int is_transmit_empty() {
+    return inb(PORT + 5) & 0x20;
+}
+
+void serial_put(char c, char color_fg, char color_bg, uint16_t x, uint16_t y) {
+    while (is_transmit_empty() == 0);
+    outb(PORT, c);
+}
+
+void serial_get(char* c, char* color_fg, char* color_bg, uint16_t x, uint16_t y) {
+}
+
+int serial_tty() {
+    return 0;
+}
+
+struct console_module serial_module = {
+    .put     = serial_put,
+    .get     = serial_get,
+    .tty     = serial_tty
+};
+
+void serial_init() {
+    outb(PORT + 1, 0x00);    // Disable all interrupts
+    outb(PORT + 3, 0x80);    // Enable DLAB (set baud rate divisor)
+    outb(PORT + 0, 0x03);    // Set divisor to 3 (lo byte) 38400 baud
+    outb(PORT + 1, 0x00);    //                  (hi byte)
+    outb(PORT + 3, 0x03);    // 8 bits, no parity, one stop bit
+    outb(PORT + 2, 0xC7);    // Enable FIFO, clear them, with 14-byte threshold
+    outb(PORT + 4, 0x0B);    // IRQs enabled, RTS/DSR set
+
+    console_register_module(&serial_module);
+}
+
+void serial_deinit() {
+    console_unregister_module(&serial_module);
+    // Stub.
+}