diff options
author | Alex Auvolat <alex@adnab.me> | 2015-03-09 20:21:20 +0100 |
---|---|---|
committer | Alex Auvolat <alex@adnab.me> | 2015-03-09 20:21:20 +0100 |
commit | f6acdfb863038a45709f0dc57884742c51fa6f07 (patch) | |
tree | d6a6e5a463ecf7a5f09dbc5a0cb73da32dc2b449 /src | |
parent | 45e1c020ae18c18ed0b31f7c12c53ccda2e4ac3a (diff) | |
download | kogata-f6acdfb863038a45709f0dc57884742c51fa6f07.tar.gz kogata-f6acdfb863038a45709f0dc57884742c51fa6f07.zip |
Implement V86 monitor and VESA mode detection.
Diffstat (limited to 'src')
-rw-r--r-- | src/kernel/Makefile | 2 | ||||
-rw-r--r-- | src/kernel/core/gdt.c | 3 | ||||
-rw-r--r-- | src/kernel/core/kmain.c | 2 | ||||
-rw-r--r-- | src/kernel/core/paging.c | 1 | ||||
-rw-r--r-- | src/kernel/dev/v86.c | 208 | ||||
-rw-r--r-- | src/kernel/dev/v86asm.s | 39 | ||||
-rw-r--r-- | src/kernel/dev/vesa.c | 84 | ||||
-rw-r--r-- | src/kernel/include/dev/v86.h | 43 | ||||
-rw-r--r-- | src/kernel/include/dev/vesa.h | 7 | ||||
-rw-r--r-- | src/kernel/user/pager.c | 2 |
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 // |