diff options
Diffstat (limited to 'Source/Kernel/TaskManager/V86/V86Thread.class.cpp')
-rw-r--r-- | Source/Kernel/TaskManager/V86/V86Thread.class.cpp | 200 |
1 files changed, 200 insertions, 0 deletions
diff --git a/Source/Kernel/TaskManager/V86/V86Thread.class.cpp b/Source/Kernel/TaskManager/V86/V86Thread.class.cpp new file mode 100644 index 0000000..babf5b6 --- /dev/null +++ b/Source/Kernel/TaskManager/V86/V86Thread.class.cpp @@ -0,0 +1,200 @@ +#include "V86Thread.class.h" +#include <TaskManager/Task.ns.h> + +u16int V86Thread::allocSeg = V86_ALLOC_START; + +u16int V86Thread::alloc(u16int length) { + if (length & 0xF) length = (length & 0xFFFF0) + 0x10; + u16int segments = length / 16; + if (allocSeg < V86_ALLOC_START) allocSeg = V86_ALLOC_START; + if (allocSeg + segments > V86_ALLOC_END) allocSeg = V86_ALLOC_START; + u16int ret = allocSeg; + allocSeg += segments; + return ret; +} + +void V86Thread::runV86(V86Thread* thread, u32int data, u32int ss, u32int cs) { + thread->m_process->getPagedir()->switchTo(); + //Setup values on user stack + u32int *stack = (u32int*)(FP_TO_LINEAR(ss, V86_STACKSIZE)); + stack--; *stack = data; + + u32int sp = V86_STACKSIZE - 4; + + //Setup a false iret structure on the kernel stack, containing (first pushed first) : + // - gs = cs + // - fs = cs + // - ds = cs + // - es = cs + // - stack segment = ss (temporarily in ecx) + // - stack pointer = sp (temp in ebx) + // - flags (OR'ed with EFLAGS.VM) + // - code segment = cs (temp in eax) + // - instruction pointer = ip (is 0) + asm volatile(" \ + pushl %%eax; \ + pushl %%eax; \ + pushl %%eax; \ + pushl %%eax; \ + pushl %%ecx; \ + pushl %%ebx; \ + pushf; \ + pop %%ebx; \ + or $0x200, %%ebx; \ + or $0x20000, %%ebx; \ + push %%ebx; \ + pushl %%eax; \ + pushl $0; \ + iret; \ + " : : "a"(cs), "c"(ss), "b"(sp)); +} + +/* + * Set up a V86 task : + * Allocate space for the kernel stack + * Map frames in lower 1MB : IVT (first page), BDA (0xA0000 to 0xFFFFF) + * Find somewhere to put the stack and the code, still in lower 1MB + * Map that space, and copy the 16bit code to that place + * Setup values on the kernel stack for starting the thread (V86Thread::runV86), + * giving it entry point and stack location + */ + +V86Thread::V86Thread(v86_function_t* entry, v86_retval_t* ret, u32int data) : Thread() { + m_ret = ret; m_ret->finished = false; + m_xchgspace = 0; + m_isKernel = true; + m_if = true; + m_process = Task::currProcess(); + m_kernelStack.addr = Mem::alloc(STACKSIZE); + m_kernelStack.size = STACKSIZE; + + m_process->getPagedir()->switchTo(); + + m_process->getPagedir()->allocFrame(0, true, true); //Map IVT frame + for (u32int i = 0xA0000; i < 0xFFFFF; i += 0x1000) { //Map BDA frames + m_process->getPagedir()->allocFrame(i, true, true); + } + + u16int cs = alloc(entry->size); //Alocate segments for the code to run in + u8int* codeptr = (u8int*)(FP_TO_LINEAR(cs, 0)); + for (u32int i = ((u32int)(codeptr) & 0xFFFFF000); i < (u32int)(codeptr) + entry->size; i += 0x1000) { + m_process->getPagedir()->allocFrame(i, true, true); + } + memcpy(codeptr, entry->data, entry->size); //Copy the code there + + u16int ss = alloc(V86_STACKSIZE); + u8int* stackptr = (u8int*)(FP_TO_LINEAR(ss, 0)); + for (u32int i = ((u32int)stackptr & 0xFFFFF000); i < (u32int)stackptr + V86_STACKSIZE; i += 0x1000) { + m_process->getPagedir()->allocFrame(i, true, true); + } + + u32int* stack = (u32int*)((u32int)m_kernelStack.addr + m_kernelStack.size); + stack--; *stack = cs; //Pass code segment (ip = 0) + stack--; *stack = ss; //Pass stack segment (sp = V86_STACKSIZE) + stack--; *stack = data; //Pass data for thread + stack--; *stack = (u32int)this; //Pass thread pointer + stack--; *stack = 0; + m_esp = (u32int)stack; + m_ebp = m_esp + 8; + m_eip = (u32int)runV86; + + m_state = T_RUNNING; + m_process->registerThread(this); + Task::registerThread(this); +} + +bool V86Thread::handleV86GPF(registers_t *regs) { + u8int* ip = (u8int*)FP_TO_LINEAR(regs->cs, regs->eip); + u16int *ivt = 0; + u16int *stack = (u16int*)FP_TO_LINEAR(regs->ss, regs->useresp); + u32int *stack32 = (u32int*)stack; + bool is_operand32 = false, is_address32 = false; + + while (true) { + switch (ip[0]) { + case 0x66: // O32 + is_operand32 = true; + ip++; regs->eip = (u16int)(regs->eip + 1); + break; + case 0x67: // A32 + is_address32 = true; + ip++; regs->eip = (u16int)(regs->eip + 1); + break; + case 0x9C: // PUSHF + if (is_operand32) { + regs->esp = ((regs->esp & 0xFFFF) - 4) & 0xFFFF; + stack32--; + *stack32 = regs->eflags & VALID_FLAGS; + if (m_if) + *stack32 |= EFLAGS_IF; + else + *stack32 &= ~EFLAGS_IF; + } else { + regs->esp = ((regs->esp & 0xFFFF) - 2) & 0xFFFF; + stack--; + *stack = regs->eflags; + if (m_if) + *stack |= EFLAGS_IF; + else + *stack &= ~EFLAGS_IF; + } + regs->eip = (u16int)(regs->eip + 1); + return true; + case 0x9D: // POPF + if (is_operand32) { + regs->eflags = EFLAGS_IF | EFLAGS_VM | (stack32[0] & VALID_FLAGS); + m_if = (stack32[0] & EFLAGS_IF) != 0; + regs->esp = ((regs->esp & 0xFFFF) + 4) & 0xFFFF; + } else { + regs->eflags = EFLAGS_IF | EFLAGS_VM | stack[0]; + m_if = (stack[0] & EFLAGS_IF) != 0; + regs->esp = ((regs->esp & 0xFFFF) + 2) & 0xFFFF; + } + regs->eip = (u16int)(regs->eip + 1); + return true; + case 0xCD: // INT N + if (ip[1] == 3) return false; //Breakpoint exception, here used for telling that thread has ended + + stack -= 3; + regs->useresp = ((regs->useresp & 0xFFFF) - 6) & 0xFFFF; + + stack[0] = (u16int)(regs->eip + 2); + stack[1] = regs->cs; + stack[2] = (u16int)regs->eflags; + + regs->cs = ivt[ip[1] * 2 + 1]; + regs->eip = ivt[ip[1] * 2]; + return true; + case 0xCF: // IRET + regs->eip = stack[0]; + regs->cs = stack[1]; + regs->eflags = EFLAGS_IF | EFLAGS_VM | stack[2]; + m_if = (stack[2] & EFLAGS_IF) != 0; + regs->esp = (u16int)(regs->esp + 6); + return true; + case 0xFA: // CLI + m_if = false; + regs->eip = (u16int)(regs->eip + 1); + return true; + case 0xFB: // STI + m_if = true; + regs->eip = (u16int)(regs->eip + 1); + return true; + default: + return false; + } + } +} + +void V86Thread::handleException(registers_t *regs, int no) { + if (no == 13) { //General protection fault + if (!handleV86GPF(regs)) { + m_ret->finished = true; + m_ret->regs = *regs; + Task::currentThreadExits(0); + return; + } + } else { + Thread::handleException(regs, no); + } +} |