summaryrefslogblamecommitdiff
path: root/Source/Kernel/TaskManager/V86/V86Thread.class.cpp
blob: 7eab887fe8cfb38e07f49abbf22ce21f52b7cfd2 (plain) (tree)
1
2
3
4


                                
                                   






























































                                                                                             
                                                                                         





                                                                                                             
                                                 






































































                                                                                                                                   









                                                                                                                                              



































                                                                                        
                                               






                                                    
#include "V86Thread.class.h"
#include <TaskManager/Task.ns.h>

#include <TaskManager/V86/V86.ns.h>

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 = V86::allocSeg(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 = V86::allocSeg(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
				if (ip[1] == 60) {		//INT 60 is used so that the real mode code can retrieve some regs from caller
					regs->eax = m_ret->regs->eax;
					regs->ebx = m_ret->regs->ebx;
					regs->ecx = m_ret->regs->ecx;
					regs->edx = m_ret->regs->edx;
					regs->edi = m_ret->regs->edi;
					regs->esi = m_ret->regs->esi;
					regs->eip = (u16int)(regs->eip + 2);
					return true;
				}

				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);
	}
}