aboutsummaryrefslogtreecommitdiff
path: root/src/kernel
diff options
context:
space:
mode:
authorAlex Auvolat <alex@adnab.me>2015-03-09 20:21:20 +0100
committerAlex Auvolat <alex@adnab.me>2015-03-09 20:21:20 +0100
commitf6acdfb863038a45709f0dc57884742c51fa6f07 (patch)
treed6a6e5a463ecf7a5f09dbc5a0cb73da32dc2b449 /src/kernel
parent45e1c020ae18c18ed0b31f7c12c53ccda2e4ac3a (diff)
downloadkogata-f6acdfb863038a45709f0dc57884742c51fa6f07.tar.gz
kogata-f6acdfb863038a45709f0dc57884742c51fa6f07.zip
Implement V86 monitor and VESA mode detection.
Diffstat (limited to 'src/kernel')
-rw-r--r--src/kernel/Makefile2
-rw-r--r--src/kernel/core/gdt.c3
-rw-r--r--src/kernel/core/kmain.c2
-rw-r--r--src/kernel/core/paging.c1
-rw-r--r--src/kernel/dev/v86.c208
-rw-r--r--src/kernel/dev/v86asm.s39
-rw-r--r--src/kernel/dev/vesa.c84
-rw-r--r--src/kernel/include/dev/v86.h43
-rw-r--r--src/kernel/include/dev/vesa.h7
-rw-r--r--src/kernel/user/pager.c2
10 files changed, 388 insertions, 3 deletions
diff --git a/src/kernel/Makefile b/src/kernel/Makefile
index 1b01f13..cee3e20 100644
--- a/src/kernel/Makefile
+++ b/src/kernel/Makefile
@@ -4,7 +4,7 @@ OBJ = core/loader.o core/dbglog.o \
core/frame.o core/paging.o core/freemem.o core/region.o core/kmalloc.o \
core/worker.o core/prng.o \
user/vfs.o user/nullfs.o user/process.o user/elf.o user/syscall.o user/ipc.o user/pager.o \
- dev/pci.o dev/pciide.o \
+ dev/pci.o dev/pciide.o dev/v86.o dev/v86asm.o dev/vesa.o \
fs/iso9660.o
LIB = ../common/libc/libc.lib ../common/libkogata/libkogata.lib ../common/libalgo/libalgo.lib
diff --git a/src/kernel/core/gdt.c b/src/kernel/core/gdt.c
index ed7abe0..d6b1e8d 100644
--- a/src/kernel/core/gdt.c
+++ b/src/kernel/core/gdt.c
@@ -50,6 +50,7 @@ typedef struct {
uint32_t ldt; // Unused...
uint16_t trap;
uint16_t iomap_base;
+ uint8_t iomap[8192];
} __attribute__((packed)) tss_entry_t;
// ========================= //
@@ -87,7 +88,7 @@ void gdt_init() {
tss_entry.ss0 = 0x10;
tss_entry.esp0 = 0;
- tss_entry.iomap_base = sizeof(tss_entry_t);
+ tss_entry.iomap_base = sizeof(tss_entry_t) - 8192;
uint32_t tss_base = (uint32_t)&tss_entry;
uint32_t tss_limit = tss_base + sizeof(tss_entry_t);
diff --git a/src/kernel/core/kmain.c b/src/kernel/core/kmain.c
index 9c81ca3..d23df1a 100644
--- a/src/kernel/core/kmain.c
+++ b/src/kernel/core/kmain.c
@@ -27,6 +27,7 @@
#include <dev/pci.h>
#include <dev/pciide.h>
+#include <dev/vesa.h>
#include <fs/iso9660.h>
#ifndef PRNG_INIT_ENTROPY
@@ -151,6 +152,7 @@ void kernel_init_stage2(void* data) {
// Scan for devices
pci_setup();
pciide_detect(iofs);
+ vesa_detect(iofs);
// Register FS drivers
register_iso9660_driver();
diff --git a/src/kernel/core/paging.c b/src/kernel/core/paging.c
index eec21f6..03a973f 100644
--- a/src/kernel/core/paging.c
+++ b/src/kernel/core/paging.c
@@ -200,7 +200,6 @@ bool pd_map_page(void* vaddr, uint32_t frame_id, bool rw) {
const uint32_t page = PAGE_OF_ADDR(vaddr);
ASSERT((size_t)vaddr < PD_MIRROR_ADDR);
- ASSERT(frame_id != 0);
bool on_kernel_pd = (size_t)vaddr >= K_HIGHHALF_ADDR || current_thread == 0;
diff --git a/src/kernel/dev/v86.c b/src/kernel/dev/v86.c
new file mode 100644
index 0000000..9498eaf
--- /dev/null
+++ b/src/kernel/dev/v86.c
@@ -0,0 +1,208 @@
+#include <string.h>
+#include <paging.h>
+
+#include <dev/v86.h>
+
+#define EFLAGS_VM 0x20000
+#define V86_VALID_FLAGS 0xDFF
+
+// ---- Ugly big static data
+
+STATIC_MUTEX(v86_mutex);
+
+v86_regs_t v86_regs;
+bool v86_if;
+
+pagedir_t *v86_pagedir = 0;
+void* v86_alloc_addr;
+
+bool v86_retval;
+thread_t *v86_caller_thread = 0;
+
+pagedir_t *v86_prev_pagedir = 0;
+
+// ---- Setup code
+
+void v86_run_bios_int(void*);
+void v86_ex_handler(registers_t *regs);
+void v86_pf_handler(void*, registers_t *regs, void* addr);
+void v86_asm_enter_v86(v86_regs_t*);
+
+bool v86_begin_session() {
+ mutex_lock(&v86_mutex);
+
+ if (v86_pagedir == 0) {
+ v86_pagedir = create_pagedir(v86_pf_handler, 0);
+ if (v86_pagedir == 0) return false;
+
+ v86_prev_pagedir = get_current_pagedir();
+ switch_pagedir(v86_pagedir);
+
+ for (void* addr = (void*)V86_ALLOC_ADDR; addr < (void*)V86_STACK_TOP; addr += PAGE_SIZE) {
+ pd_map_page(addr, (uint32_t)addr / PAGE_SIZE, true);
+ }
+ for (void* addr = (void*)V86_BIOS_BEGIN; addr < (void*)V86_BIOS_END; addr += PAGE_SIZE) {
+ pd_map_page(addr, (uint32_t)addr / PAGE_SIZE, true);
+ }
+ pd_map_page(0, 0, true);
+ }
+
+ v86_alloc_addr = (void*)V86_ALLOC_ADDR;
+
+ memset(&v86_regs, 0, sizeof(v86_regs));
+
+ return true;
+}
+
+void v86_end_session() {
+ switch_pagedir(v86_prev_pagedir);
+ v86_prev_pagedir = 0;
+ v86_caller_thread = 0;
+
+ mutex_unlock(&v86_mutex);
+}
+
+void* v86_alloc(size_t size) {
+ void* addr = v86_alloc_addr;
+
+ v86_alloc_addr += size;
+
+ return addr;
+}
+
+bool v86_bios_int(uint8_t int_no) {
+ v86_caller_thread = current_thread;
+
+ uint32_t int_no_32 = int_no;
+
+ thread_t *v86t = new_thread(v86_run_bios_int, (void*)int_no_32);
+ v86t->user_ex_handler = v86_ex_handler;
+
+ int st = enter_critical(CL_NOSWITCH);
+ start_thread(v86t);
+ wait_on(current_thread);
+ exit_critical(st);
+
+ return v86_retval;
+}
+
+void v86_run_bios_int(void* x) {
+ switch_pagedir(v86_pagedir);
+
+ uint32_t int_no = (uint32_t)x;
+
+ uint16_t *ivt = (uint16_t*)0;
+
+ v86_regs.cs = ivt[2 * int_no + 1];
+ v86_regs.ip = ivt[2 * int_no];
+ v86_regs.ss = ((V86_STACK_TOP - 0x10000) >> 4);
+ v86_regs.sp = 0;
+
+ v86_if = true;
+
+ v86_asm_enter_v86(&v86_regs);
+}
+
+void v86_exit_thread(bool status) {
+ v86_retval = status;
+
+ resume_on(v86_caller_thread);
+ exit();
+}
+
+bool v86_gpf_handler(registers_t *regs) {
+ uint8_t* ip = (uint8_t*)V86_LIN_OF_SEG_OFF(regs->cs, regs->eip);
+ uint16_t *stack = (uint16_t*)V86_LIN_OF_SEG_OFF(regs->ss, (regs->esp & 0xFFFF));
+ uint32_t *stack32 = (uint32_t*)stack;
+ bool is_operand32 = false; // bool is_address32 = false;
+
+ while (true) {
+ switch (ip[0]) {
+ case 0x66: // O32
+ is_operand32 = true;
+ ip++; regs->eip = (uint16_t)(regs->eip + 1);
+ break;
+ case 0x67: // A32
+ // is_address32 = true;
+ ip++; regs->eip = (uint16_t)(regs->eip + 1);
+ break;
+ case 0x9C: // PUSHF
+ if (is_operand32) {
+ regs->esp = ((regs->esp & 0xFFFF) - 4) & 0xFFFF;
+ stack32--;
+ *stack32 = regs->eflags & V86_VALID_FLAGS;
+ if (v86_if)
+ *stack32 |= EFLAGS_IF;
+ else
+ *stack32 &= ~EFLAGS_IF;
+ } else {
+ regs->esp = ((regs->esp & 0xFFFF) - 2) & 0xFFFF;
+ stack--;
+ *stack = regs->eflags;
+ if (v86_if)
+ *stack |= EFLAGS_IF;
+ else
+ *stack &= ~EFLAGS_IF;
+ }
+ regs->eip = (uint16_t)(regs->eip + 1);
+ return true;
+ case 0x9D: // POPF
+ if (is_operand32) {
+ regs->eflags = EFLAGS_IF | EFLAGS_VM | (stack32[0] & V86_VALID_FLAGS);
+ v86_if = (stack32[0] & EFLAGS_IF) != 0;
+ regs->esp = ((regs->esp & 0xFFFF) + 4) & 0xFFFF;
+ } else {
+ regs->eflags = EFLAGS_IF | EFLAGS_VM | stack[0];
+ v86_if = (stack[0] & EFLAGS_IF) != 0;
+ regs->esp = ((regs->esp & 0xFFFF) + 2) & 0xFFFF;
+ }
+ regs->eip = (uint16_t)(regs->eip + 1);
+ return true;
+ case 0xCF: // IRET
+ v86_regs.ax = (uint16_t)regs->eax;
+ v86_regs.bx = (uint16_t)regs->ebx;
+ v86_regs.cx = (uint16_t)regs->ecx;
+ v86_regs.dx = (uint16_t)regs->edx;
+ v86_regs.di = (uint16_t)regs->edi;
+ v86_regs.si = (uint16_t)regs->esi;
+
+ v86_exit_thread(true);
+ case 0xFA: // CLI
+ v86_if = false;
+ regs->eip = (uint16_t)(regs->eip + 1);
+ return true;
+ case 0xFB: // STI
+ v86_if = true;
+ regs->eip = (uint16_t)(regs->eip + 1);
+ return true;
+ default:
+ return false;
+ }
+ }
+}
+
+void v86_ex_handler(registers_t *regs) {
+ if (regs->int_no == EX_GENERAL_PROTECTION) {
+ if (!v86_gpf_handler(regs)) v86_exit_thread(false);
+ } else {
+ v86_exit_thread(false);
+ }
+}
+
+void v86_pf_handler(void* zero, registers_t *regs, void* addr) {
+ if (addr < (void*)V86_STACK_TOP) {
+ pd_map_page(addr, (uint32_t)addr / PAGE_SIZE, true);
+ } else {
+ dbg_printf("Unexpected V86 PF at 0x%p\n", addr);
+
+ if (current_thread->user_ex_handler == v86_ex_handler) {
+ // we are in V86 thread
+ v86_exit_thread(false);
+ } else {
+ PANIC("V86 memory access exception.");
+ }
+ }
+}
+
+
+/* vim: set ts=4 sw=4 tw=0 noet :*/
diff --git a/src/kernel/dev/v86asm.s b/src/kernel/dev/v86asm.s
new file mode 100644
index 0000000..da5bc38
--- /dev/null
+++ b/src/kernel/dev/v86asm.s
@@ -0,0 +1,39 @@
+
+[BITS 32]
+
+[GLOBAL v86_asm_enter_v86]
+v86_asm_enter_v86:
+ mov eax, [esp + 4]
+
+ xor ebx, ebx
+ mov bx, [eax + 20] ; get GS
+ push ebx
+ mov bx, [eax + 18] ; get FS
+ push ebx
+ mov bx, [eax + 14] ; get DS
+ push ebx
+ mov bx, [eax + 16] ; get ES
+ push ebx
+ mov bx, [eax + 22] ; get SS
+ push ebx
+ mov bx, [eax + 26] ; get SP
+ push ebx
+ pushf
+ pop ebx
+ or ebx, 20200h
+ push ebx
+ xor ebx, ebx
+ mov bx, [eax + 12] ; get CS
+ push ebx
+ mov bx, [eax + 24] ; get IP
+ push ebx
+
+ mov bx, [eax + 2]
+ mov cx, [eax + 4]
+ mov dx, [eax + 6]
+ mov di, [eax + 8]
+ mov si, [eax + 10]
+ mov ax, [eax]
+
+ iret
+
diff --git a/src/kernel/dev/vesa.c b/src/kernel/dev/vesa.c
new file mode 100644
index 0000000..6b23a34
--- /dev/null
+++ b/src/kernel/dev/vesa.c
@@ -0,0 +1,84 @@
+#include <string.h>
+
+#include <dev/vesa.h>
+#include <dev/v86.h>
+
+// ---- VESA data structures
+
+typedef struct {
+ char vbe_signature[4]; // == "VESA"
+ uint16_t vbe_version;
+ v86_farptr_t oem_string_ptr;
+ uint8_t capabilities[4];
+ v86_farptr_t video_mode_ptr;
+ uint16_t total_memory; // as # of 64KB blocks
+} __attribute__((packed)) vbe_info_block_t;
+
+typedef struct {
+ uint16_t attributes;
+ uint8_t winA, winB;
+ uint16_t granularity;
+ uint16_t winsize;
+ uint16_t segmentA, segmentB;
+ v86_farptr_t real_fct_ptr;
+ uint16_t pitch; // bytes per scanline
+
+ uint16_t Xres, Yres;
+ uint8_t Wchar, Ychar, planes, bpp, banks;
+ uint8_t memory_model, bank_size, image_pages;
+ uint8_t reserved0;
+
+ uint8_t red_mask, red_position;
+ uint8_t green_mask, green_position;
+ uint8_t blue_mask, blue_position;
+ uint8_t rsv_mask, rsv_position;
+ uint8_t directcolor_attributes;
+
+ uint32_t physbase; // the offset of the framebuffer in physical memory!
+ uint32_t reserved1;
+ uint16_t reserved2;
+} __attribute__((packed)) vbe_mode_info_block_t;
+
+// ---- VESA code
+
+void vesa_detect(fs_t *iofs) {
+ if (!v86_begin_session()) return;
+
+ vbe_info_block_t *i = (vbe_info_block_t*)v86_alloc(512);
+ memset(i, 0, 512);
+ strncpy(i->vbe_signature, "VBE2", 4);
+
+ v86_regs.ax = 0x4F00;
+ v86_regs.es = V86_SEG_OF_LIN(i);
+ v86_regs.di = V86_OFF_OF_LIN(i);
+
+ if (v86_bios_int(0x10)) {
+ if (v86_regs.ax == 0x004F) {
+ dbg_printf("Detected VESA (sig %s, ver 0x%x, oem %s) with %d kb ram.\n",
+ i->vbe_signature, i->vbe_version, v86_lin_of_fp(i->oem_string_ptr), 64 * i->total_memory);
+
+ vbe_mode_info_block_t *mi = (vbe_mode_info_block_t*)v86_alloc(256);
+
+ uint16_t *modes = (uint16_t*)v86_lin_of_fp(i->video_mode_ptr);
+ for (int n = 0; modes[n] != 0xFFFF; n++) {
+ v86_regs.ax = 0x4F01;
+ v86_regs.cx = modes[n];
+ v86_regs.es = V86_SEG_OF_LIN(mi);
+ v86_regs.di = V86_OFF_OF_LIN(mi);
+ if (v86_bios_int(0x10) && v86_regs.ax == 0x004F) {
+ dbg_printf("Mode 0x%x : %dx%dx%d at 0x%p\n", modes[n], mi->Xres, mi->Yres, mi->bpp, mi->physbase);
+ } else {
+ dbg_printf("Mode 0x%x : could not get info\n", modes[n]);
+ }
+ }
+ } else {
+ dbg_printf("Error in BIOS int 0x10/0x4F00.\n");
+ }
+ } else {
+ dbg_printf("Could not call BIOS int 0x10.\n");
+ }
+
+ v86_end_session();
+}
+
+/* vim: set ts=4 sw=4 tw=0 noet :*/
diff --git a/src/kernel/include/dev/v86.h b/src/kernel/include/dev/v86.h
new file mode 100644
index 0000000..b260fa7
--- /dev/null
+++ b/src/kernel/include/dev/v86.h
@@ -0,0 +1,43 @@
+#pragma once
+
+// This is in no way a complete 16-bit PC virtualizer, only a way
+// to call BIOS functions.
+// It is very ugly: it uses a global mutex and a lot of global variables.
+
+#include <thread.h>
+#include <mutex.h>
+
+#define V86_ALLOC_ADDR 0x20000
+#define V86_STACK_TOP 0x80000
+#define V86_BIOS_BEGIN 0xA0000
+#define V86_BIOS_END 0x100000
+
+typedef uint32_t v86_farptr_t;
+
+#define V86_SEG_OF_LIN(x) ((size_t)(x) >> 4)
+#define V86_OFF_OF_LIN(x) ((size_t)(x) & 0x0F)
+#define V86_LIN_OF_SEG_OFF(seg, off) ((((size_t)(seg)) << 4) + ((size_t)(off)))
+inline void* v86_lin_of_fp(v86_farptr_t x) {
+ return (void*)V86_LIN_OF_SEG_OFF(x>>16, x & 0xFFFF);
+}
+inline v86_farptr_t v86_fp_of_lin(void* p) {
+ return (V86_SEG_OF_LIN(p) << 16) | V86_OFF_OF_LIN(p);
+}
+
+typedef struct {
+ uint16_t ax, bx, cx, dx, di, si;
+ uint16_t cs, ds, es, fs, gs, ss;
+ uint16_t ip, sp;
+} v86_regs_t;
+extern v86_regs_t v86_regs;
+
+bool v86_begin_session();
+
+void* v86_alloc(size_t size);
+
+bool v86_bios_int(uint8_t int_no);
+
+void v86_end_session();
+
+
+/* vim: set ts=4 sw=4 tw=0 noet :*/
diff --git a/src/kernel/include/dev/vesa.h b/src/kernel/include/dev/vesa.h
new file mode 100644
index 0000000..326e54c
--- /dev/null
+++ b/src/kernel/include/dev/vesa.h
@@ -0,0 +1,7 @@
+#pragma once
+
+#include <vfs.h>
+
+void vesa_detect(fs_t *iofs);
+
+/* vim: set ts=4 sw=4 tw=0 noet :*/
diff --git a/src/kernel/user/pager.c b/src/kernel/user/pager.c
index 9987813..20c7014 100644
--- a/src/kernel/user/pager.c
+++ b/src/kernel/user/pager.c
@@ -7,6 +7,8 @@
#define ENT_FRAME_SHIFT 12
+// TODO TODO TODO TODO !!! when resizing/paging out, make sure to UNMAP ALL PAGES from processes!!
+
// ========== //
// SWAP PAGER //