[PATCH 5/5] Virtual 8086 mode support for kernel mode drivers.

Jan Klötzke <[email protected]> Fri, 16 May 2008 20:13:45 +0200
Newsgroups gmane.os.openbeos.kernel.devel
Message-ID <[email protected]>
The new function vm86_do_int(struct vm86_state *state, uint8 vec) provides a
facility to call BIOS interupt handlers. The function must only be called from
a user thread context because the lower 1MB of the address space is used.
---
 headers/private/kernel/arch/x86/arch_cpu.h   |   31 ++
 headers/private/kernel/arch/x86/vm86.h       |   38 ++
 src/system/kernel/arch/x86/Jamfile           |    1 +
 src/system/kernel/arch/x86/arch_int.c        |    7 +
 src/system/kernel/arch/x86/arch_interrupts.S |  102 ++++
 src/system/kernel/arch/x86/asm_offsets.cpp   |    4 +
 src/system/kernel/arch/x86/vm86.cpp          |  649 ++++++++++++++++++++++++++
 7 files changed, 832 insertions(+), 0 deletions(-)
 create mode 100644 headers/private/kernel/arch/x86/vm86.h
 create mode 100644 src/system/kernel/arch/x86/vm86.cpp

diff --git a/headers/private/kernel/arch/x86/arch_cpu.h b/headers/private/kernel/arch/x86/arch_cpu.h
index 11e412f..1575595 100644
--- a/headers/private/kernel/arch/x86/arch_cpu.h
+++ b/headers/private/kernel/arch/x86/arch_cpu.h
@@ -157,6 +157,37 @@ struct iframe {
 	uint32 user_ss;
 };
 
+struct vm86_iframe {
+	uint32 type;	// iframe type
+	uint32 __null_gs;
+	uint32 __null_fs;
+	uint32 __null_es;
+	uint32 __null_ds;
+	uint32 edi;
+	uint32 esi;
+	uint32 ebp;
+	uint32 __kern_esp;
+	uint32 ebx;
+	uint32 edx;
+	uint32 ecx;
+	uint32 eax;
+	uint32 orig_eax;
+	uint32 orig_edx;
+	uint32 vector;
+	uint32 error_code;
+	uint32 eip;
+	uint16 cs, __csh;
+	uint32 flags;
+	uint32 esp;
+	uint16 ss, __ssh;
+
+	/* vm86 mode specific part */
+	uint16 es, __esh;
+	uint16 ds, __dsh;
+	uint16 fs, __fsh;
+	uint16 gs, __gsh;
+};
+
 #define IFRAME_IS_USER(f) ( ((f)->cs == USER_CODE_SEG) \
                             || (((f)->flags & 0x20000) != 0 ))
 #define IFRAME_IS_VM86(f) ( ((f)->flags & 0x20000) != 0 )
diff --git a/headers/private/kernel/arch/x86/vm86.h b/headers/private/kernel/arch/x86/vm86.h
new file mode 100644
index 0000000..4689431
--- /dev/null
+++ b/headers/private/kernel/arch/x86/vm86.h
@@ -0,0 +1,38 @@
+/*
+ * Copyright 2008 Jan Klötzke
+ * All rights reserved. Distributed under the terms of the MIT License.
+ */
+#ifndef _VM86_H
+#define _VM86_H
+
+#include <OS.h>
+#include <arch_cpu.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#define VM86_MIN_RAM_SIZE 0x1000
+#define RETURN_TO_32_INT  255
+
+struct vm86_state {
+	struct vm86_iframe regs;
+	area_id bios_area;
+	area_id ram_area;
+	unsigned int if_flag : 1;
+};
+
+status_t x86_vm86_enter(struct vm86_iframe *frame);
+void x86_vm86_return(struct vm86_iframe *frame, status_t retval);
+
+status_t vm86_prepare(struct vm86_state *state, unsigned int ram_size);
+void vm86_cleanup(struct vm86_state *state);
+status_t vm86_do_int(struct vm86_state *state, uint8 vec);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
+
+
diff --git a/src/system/kernel/arch/x86/Jamfile b/src/system/kernel/arch/x86/Jamfile
index 7e97605..04e80a9 100644
--- a/src/system/kernel/arch/x86/Jamfile
+++ b/src/system/kernel/arch/x86/Jamfile
@@ -33,6 +33,7 @@ KernelStaticLibrary libx86 :
 	bios.cpp
 	cpuid.S
 	syscall.S
+	vm86.cpp
 
 	generic_vm_physical_page_mapper.cpp
 	:
diff --git a/src/system/kernel/arch/x86/arch_int.c b/src/system/kernel/arch/x86/arch_int.c
index 4d2b472..aebe32b 100644
--- a/src/system/kernel/arch/x86/arch_int.c
+++ b/src/system/kernel/arch/x86/arch_int.c
@@ -23,6 +23,7 @@
 #include <arch/vm.h>
 
 #include <arch/x86/descriptors.h>
+#include <arch/x86/vm86.h>
 
 #include "interrupts.h"
 
@@ -355,6 +356,12 @@ unexpected_exception(struct iframe* frame)
 	debug_exception_type type;
 	int signal;
 
+	if (IFRAME_IS_VM86(frame)) {
+		x86_vm86_return((struct vm86_iframe *)frame, (frame->vector == 13) ?
+			B_OK : B_ERROR);
+		// won't get here
+	}
+
 	switch (frame->vector) {
 		case 0:		// Divide Error Exception (#DE)
 			type = B_DIVIDE_ERROR;
diff --git a/src/system/kernel/arch/x86/arch_interrupts.S b/src/system/kernel/arch/x86/arch_interrupts.S
index 5c19920..d63ecbb 100644
--- a/src/system/kernel/arch/x86/arch_interrupts.S
+++ b/src/system/kernel/arch/x86/arch_interrupts.S
@@ -246,6 +246,8 @@ int_bottom:
 
 	movl	%esp, %ebp		// frame pointer is the iframe
 
+	testl	$0x20000, IFRAME_flags(%ebp)	// VM86 mode
+	jnz		int_bottom_vm86
 	cmp		$USER_CODE_SEG, IFRAME_cs(%ebp)
 	je		int_bottom_user
 
@@ -292,6 +294,33 @@ int_bottom_user:
 	POP_IFRAME_AND_RETURN()
 
 
+int_bottom_vm86:
+	movl	$KERNEL_DATA_SEG,%eax
+	cld
+	movl	%eax,%ds
+	movl	%eax,%es
+
+	// update the thread's user time
+	movl	%dr3, %edi				// thread pointer
+	cli
+	UPDATE_THREAD_USER_TIME()
+
+	// leave interrupts disabled -- the handler will enable them, if
+	// necessary
+
+	pushl	%ebp
+	movl	IFRAME_vector(%ebp), %eax
+	call	*gInterruptHandlerTable(, %eax, 4)
+
+	// update the thread's kernel time and return
+	cli
+	UPDATE_THREAD_KERNEL_TIME()
+	lea		20(%ebp), %esp;
+	popa;
+	addl	$16,%esp;
+	iret
+
+
 // test interrupt handler for performance measurements
 .align 16
 .globl trap98
@@ -537,3 +566,76 @@ FUNCTION(i386_restore_frame_from_syscall):
 	// update the thread's kernel time and return
 	UPDATE_THREAD_KERNEL_TIME()
 	POP_IFRAME_AND_RETURN()
+
+
+/* status_t x86_vm86_enter(struct vm86_iframe *frame) */
+FUNCTION(x86_vm86_enter):
+	// save critical registers
+	pushf
+	pushl	%edi
+	pushl	%esi
+	pushl	%ebp
+	pushl	%ebx
+	push	%gs
+	push	%fs
+
+	// get pointers
+	movl	32(%esp), %esi	// vm86 iframe
+	push	%esi			// ... store iframe addr for x86_vm86_return
+	movl	%dr3, %edi		// struct thread
+
+	// make sure eflags are in right shape
+	movl	VM86_IFRAME_flags(%esi), %eax
+	andl	$0x200CD5, %eax		// leave ID,OF,DF,SF,ZF,AF,PF,CF flags
+	orl		$0x20202, %eax		// set VM and IF flags (+10b)
+	movl	%eax, VM86_IFRAME_flags(%esi)
+
+	// update kernel_stack_top and tss.esp0
+	pushl	THREAD_kernel_stack_top(%edi)
+	movl	%esp, THREAD_kernel_stack_top(%edi)
+	pushl	%esp
+	call	i386_set_tss_and_kstack
+
+	// go to vm86 mode
+	cli
+	UPDATE_THREAD_KERNEL_TIME()
+	lea		20(%esi), %esp
+	popa
+	addl	$16, %esp
+	iret
+
+
+/* void x86_vm86_return(struct vm86_iframe *frame, status_t retval) */
+FUNCTION(x86_vm86_return):
+	// set stack to where x86_vm86_enter was left
+	movl 8(%esp), %ebx
+	movl 4(%esp), %esi
+	cli
+	movl %esi, %esp
+	addl $VM86_IFRAME_sizeof, %esp
+	
+	// save old iframe
+	popl	%eax	// old kernel stack top
+	popl	%edi
+	movl	$(VM86_IFRAME_sizeof >> 2), %ecx
+	cld
+	rep movsl
+	
+	// adjust kernel_stack_top and tss.esp0
+	movl	%dr3, %edi
+	movl	%eax, THREAD_kernel_stack_top(%edi)
+	push	%eax
+	call	i386_set_tss_and_kstack
+	addl	$4, %esp
+
+	// restore registers
+	movl %ebx, %eax
+	pop %fs
+	pop %gs
+	popl %ebx
+	popl %ebp
+	popl %esi
+	popl %edi
+	popf
+	ret
+
diff --git a/src/system/kernel/arch/x86/asm_offsets.cpp b/src/system/kernel/arch/x86/asm_offsets.cpp
index e66cea3..77ea318 100644
--- a/src/system/kernel/arch/x86/asm_offsets.cpp
+++ b/src/system/kernel/arch/x86/asm_offsets.cpp
@@ -45,6 +45,10 @@ dummy()
 	DEFINE_OFFSET_MACRO(IFRAME, iframe, flags);
 	DEFINE_OFFSET_MACRO(IFRAME, iframe, user_esp);
 
+	// struct vm86_iframe
+	DEFINE_SIZEOF_MACRO(VM86_IFRAME, vm86_iframe);
+	DEFINE_OFFSET_MACRO(VM86_IFRAME, vm86_iframe, flags);
+
 	// struct syscall_info
 	DEFINE_SIZEOF_MACRO(SYSCALL_INFO, syscall_info);
 	DEFINE_OFFSET_MACRO(SYSCALL_INFO, syscall_info, function);
diff --git a/src/system/kernel/arch/x86/vm86.cpp b/src/system/kernel/arch/x86/vm86.cpp
new file mode 100644
index 0000000..c8307bc
--- /dev/null
+++ b/src/system/kernel/arch/x86/vm86.cpp
@@ -0,0 +1,649 @@
+/*
+ * Copyright 2008 Jan Klötzke
+ * All rights reserved. Distributed under the terms of the MIT License.
+ *
+ * Emulation based on the Linux Real Mode Interface library.
+ * Copyright (C) 1998 by Josh Vanderhoof
+ */
+#include <vm.h>
+#include <thread.h>
+#include <vm86.h>
+#include <arch_cpu.h>
+#include <kernel.h>
+#include <ksignal.h>
+
+#include <string.h>
+#include <stdlib.h>
+
+//#define TRACE_VM86
+#ifdef TRACE_VM86
+#define TRACE(x...)    kprintf("[vm86] " x)
+#define TRACE_NP(x...) kprintf(x)
+#else
+#define TRACE(x...)
+#define TRACE_NP(x...)
+#endif
+
+#define CLI     0xfa
+#define INB     0xec
+#define INBI    0xe4
+#define INSB    0x6c
+#define INSW    0x6d
+#define INTn    0xcd
+#define INW     0xed
+#define INWI    0xe5
+#define IRET    0xcf
+#define OUTB    0xee
+#define OUTBI   0xe6
+#define OUTSB   0x6e
+#define OUTSW   0x6f
+#define OUTW    0xef
+#define OUTWI   0xe7
+#define POPF    0x9d
+#define PUSHF   0x9c
+#define STI     0xfb
+
+#define I_FLAG          (1 << 9)
+#define DIRECTION_FLAG  (1 << 10)
+
+#define CSEG 0x2e
+#define SSEG 0x36
+#define DSEG 0x3e
+#define ESEG 0x26
+#define FSEG 0x64
+#define GSEG 0x65
+
+
+static inline uint16
+get_int_seg(int i)
+{
+	return *(uint16 *)(i * 4 + 2);
+}
+
+
+static inline uint16
+get_int_off(int i)
+{
+	return *(uint16 *)(i * 4);
+}
+
+
+static inline void
+pushw(struct vm86_iframe *regs, uint16 i)
+{
+	regs->esp -= 2;
+	*(uint16 *)(((uint32)regs->ss << 4) + regs->esp) = i;
+}
+
+
+static inline void
+pushl(struct vm86_iframe *regs, uint32 i)
+{
+	regs->esp -= 4;
+	*(uint32 *)(((uint32)regs->ss << 4) + regs->esp) = i;
+}
+
+
+static inline uint16
+popw(struct vm86_iframe *regs)
+{
+	uint16 ret = *(uint16 *)(((uint32)regs->ss << 4) + regs->esp);
+	regs->esp += 2;
+	return ret;
+}
+
+
+static inline uint32
+popl(struct vm86_iframe *regs)
+{
+	uint32 ret = *(uint32 *)(((uint32)regs->ss << 4) + regs->esp);
+	regs->esp += 4;
+	return ret;
+}
+
+
+static void
+em_ins(struct vm86_iframe *regs, int size)
+{
+	unsigned int edx, edi;
+
+	edx = regs->edx & 0xffff;
+	edi = regs->edi & 0xffff;
+	edi += (unsigned int)regs->es << 4;
+
+	if (regs->flags & DIRECTION_FLAG) {
+		if (size == 4)
+			asm volatile ("std; insl; cld"
+			 : "=D" (edi) : "d" (edx), "0" (edi));
+		else if (size == 2)
+			asm volatile ("std; insw; cld"
+			 : "=D" (edi) : "d" (edx), "0" (edi));
+		else
+			asm volatile ("std; insb; cld"
+			 : "=D" (edi) : "d" (edx), "0" (edi));
+	} else {
+		if (size == 4)
+			asm volatile ("cld; insl"
+			 : "=D" (edi) : "d" (edx), "0" (edi));
+		else if (size == 2)
+			asm volatile ("cld; insw"
+			 : "=D" (edi) : "d" (edx), "0" (edi));
+		else
+			asm volatile ("cld; insb"
+			 : "=D" (edi) : "d" (edx), "0" (edi));
+	}
+
+	edi -= (unsigned int)regs->es << 4;
+
+	regs->edi &= 0xffff0000;
+	regs->edi |= edi & 0xffff;
+}
+
+
+static void
+em_rep_ins(struct vm86_iframe *regs, int size)
+{
+	unsigned int cx;
+
+	cx = regs->ecx & 0xffff;
+
+	while (cx--)
+		em_ins(regs, size);
+
+	regs->ecx &= 0xffff0000;
+}
+
+
+static void
+em_outs(struct vm86_iframe *regs, int size, int seg)
+{
+	unsigned int edx, esi, base;
+
+	edx = regs->edx & 0xffff;
+	esi = regs->esi & 0xffff;
+
+	switch (seg) {
+	case CSEG: base = regs->cs; break;
+	case SSEG: base = regs->ss; break;
+	case ESEG: base = regs->es; break;
+	case FSEG: base = regs->fs; break;
+	case GSEG: base = regs->gs; break;
+	default:
+	case DSEG: base = regs->ds; break;
+	}
+
+	esi += base << 4;
+
+	if (regs->flags & DIRECTION_FLAG) {
+		if (size == 4)
+			asm volatile ("std; outsl; cld"
+			 : "=S" (esi) : "d" (edx), "0" (esi));
+		else if (size == 2)
+			asm volatile ("std; outsw; cld"
+			 : "=S" (esi) : "d" (edx), "0" (esi));
+		else
+			asm volatile ("std; outsb; cld"
+			 : "=S" (esi) : "d" (edx), "0" (esi));
+	} else {
+		if (size == 4)
+			asm volatile ("cld; outsl"
+			 : "=S" (esi) : "d" (edx), "0" (esi));
+		else if (size == 2)
+			asm volatile ("cld; outsw"
+			 : "=S" (esi) : "d" (edx), "0" (esi));
+		else
+			asm volatile ("cld; outsb"
+			 : "=S" (esi) : "d" (edx), "0" (esi));
+	}
+
+	esi -= base << 4;
+
+	regs->esi &= 0xffff0000;
+	regs->esi |= esi & 0xffff;
+}
+
+
+static void
+em_rep_outs(struct vm86_iframe *regs, int size, int seg)
+{
+	unsigned int cx;
+
+	cx = regs->ecx & 0xffff;
+
+	while (cx--)
+		em_outs(regs, size, seg);
+
+	regs->ecx &= 0xffff0000;
+}
+
+
+static inline void
+em_inb(struct vm86_iframe *regs, uint32 port)
+{
+	asm volatile ("inb %w1, %b0"
+	 : "=a" (regs->eax)
+	 : "d" (port), "0" (regs->eax));
+}
+
+
+static inline void
+em_inw(struct vm86_iframe *regs, uint32 port)
+{
+	asm volatile ("inw %w1, %w0"
+	 : "=a" (regs->eax)
+	 : "d" (port), "0" (regs->eax));
+}
+
+
+static inline void
+em_inl(struct vm86_iframe *regs, uint32 port)
+{
+	asm volatile ("inl %w1, %0"
+	 : "=a" (regs->eax)
+	 : "d" (port));
+}
+
+
+static inline void
+em_outb(struct vm86_iframe *regs, uint32 port)
+{
+	asm volatile ("outb %b0, %w1"
+	 : : "a" (regs->eax),
+	 "d" (port));
+}
+
+
+static inline void
+em_outw(struct vm86_iframe *regs, uint32 port)
+{
+	asm volatile ("outw %w0, %w1"
+	 : : "a" (regs->eax),
+	 "d" (port));
+}
+
+
+static inline void
+em_outl(struct vm86_iframe *regs, uint32 port)
+{
+	asm volatile ("outl %0, %w1"
+	 : : "a" (regs->eax),
+	 "d" (port));
+}
+
+
+static int
+emulate(struct vm86_state *state)
+{
+	unsigned char *insn;
+	struct {
+		unsigned char seg;
+		unsigned int size : 1;
+		unsigned int rep : 1;
+	} prefix = { DSEG, 0, 0 };
+	int ret = 0;
+
+	insn = (unsigned char *)((unsigned int)state->regs.cs << 4);
+	insn += state->regs.eip;
+
+	TRACE("emulate: ");
+
+	/* handle prefixes */
+	do {
+		switch (*insn) {
+			case 0x66:
+				TRACE_NP("[SIZE] ");
+				prefix.size = 1 - prefix.size;
+				state->regs.eip++; insn++;
+				break;
+			case 0xf3:
+				TRACE_NP("REP ");
+				prefix.rep = 1;
+				state->regs.eip++; insn++;
+				break;
+			case CSEG:
+			case SSEG:
+			case DSEG:
+			case ESEG:
+			case FSEG:
+			case GSEG:
+				TRACE_NP("SEG(0x%02x) ", *insn);
+				prefix.seg = *insn;
+				state->regs.eip++; insn++;
+				break;
+			case 0xf0:
+			case 0xf2:
+			case 0x67:
+				/* these prefixes are just ignored */
+				TRACE_NP("IGN(0x%02x) ", *insn);
+				state->regs.eip++; insn++;
+				break;
+			default:
+				ret = 1;
+		}
+	} while (!ret);
+
+	/* handle actual instruction */
+	ret = 0;
+	switch (*insn) {
+		case INSB:
+			TRACE_NP("INSB");
+			if (prefix.rep)
+				em_rep_ins(&state->regs, 1);
+			else
+				em_ins(&state->regs, 1);
+			state->regs.eip++;
+			break;
+		case INSW:
+			TRACE_NP("INSW");
+			if (prefix.rep) {
+				if (prefix.size)
+					em_rep_ins(&state->regs, 4);
+				else
+					em_rep_ins(&state->regs, 2);
+			} else {
+				if (prefix.size)
+					em_ins(&state->regs, 4);
+				else
+					em_ins(&state->regs, 2);
+			}
+			state->regs.eip++;
+			break;
+		case OUTSB:
+			TRACE_NP("OUTSB");
+			if (prefix.rep)
+				em_rep_outs(&state->regs, 1, prefix.seg);
+			else
+				em_outs(&state->regs, 1, prefix.seg);
+			state->regs.eip++;
+			break;
+		case OUTSW:
+			TRACE_NP("OUTSW");
+			if (prefix.rep) {
+				if (prefix.size)
+					em_rep_outs(&state->regs, 4, prefix.seg);
+				else
+					em_rep_outs(&state->regs, 2, prefix.seg);
+			} else {
+				if (prefix.size)
+					em_outs(&state->regs, 4, prefix.seg);
+				else
+					em_outs(&state->regs, 2, prefix.seg);
+			}
+			state->regs.eip++;
+			break;
+		case INBI:
+			em_inb(&state->regs, insn[1]);
+			TRACE_NP("IN al(=0x%02lx), 0x%02x", state->regs.eax&0xFF, insn[1]);
+			state->regs.eip+=2;
+			break;
+		case INWI:
+			if (prefix.size) {
+				em_inl(&state->regs, insn[1]);
+				TRACE_NP("IN eax(=0x%08lx), 0x%02x", state->regs.eax, insn[1]);
+			} else {
+				em_inw(&state->regs, insn[1]);
+				TRACE_NP("IN ax(=0x%04lx), 0x%02x", state->regs.eax&0xFFFF, insn[1]);
+			}
+			state->regs.eip+=2;
+			break;
+		case INB:
+			em_inb(&state->regs, state->regs.edx);
+			TRACE_NP("IN al(=0x%02lx), dx(0x%04lx)", state->regs.edx&0xFF,
+				state->regs.edx&0xFFFF);
+			state->regs.eip++;
+			break;
+		case INW:
+			if (prefix.size) {
+				em_inl(&state->regs, state->regs.edx);
+				TRACE_NP("IN eax(=0x%08lx), dx(0x%04lx)", state->regs.edx,
+					state->regs.edx&0xFFFF);
+			} else {
+				em_inw(&state->regs, state->regs.edx);
+				TRACE_NP("IN ax(=0x%04lx), dx(0x%04lx)", state->regs.edx&0xFFFF,
+					state->regs.edx&0xFFFF);
+			}
+			state->regs.eip++;
+			break;
+		case OUTBI:
+			em_outb(&state->regs, insn[1]);
+			TRACE_NP("OUT 0x%02x, al(0x%02lx)", insn[1], state->regs.eax&0xFF);
+			state->regs.eip+=2;
+			break;
+		case OUTWI:
+			if (prefix.size) {
+				em_outl(&state->regs, insn[1]);
+				TRACE_NP("OUT 0x%02x, eax(0x%08lx)", insn[1], state->regs.eax);
+			} else {
+				em_outw(&state->regs, insn[1]);
+				TRACE_NP("OUT 0x%02x, ax(0x%04lx)", insn[1], state->regs.eax&0xFFFF);
+			}
+			state->regs.eip+=2;
+			break;
+		case OUTB:
+			em_outb(&state->regs, state->regs.edx);
+			TRACE_NP("OUT dx(0x%04lx), al(0x%02lx)", state->regs.edx&0xFFFF,
+				state->regs.eax&0xFF);
+			state->regs.eip++;
+			break;
+		case OUTW:
+			if (prefix.size) {
+				em_outl(&state->regs, state->regs.edx);
+				TRACE_NP("OUT dx(0x%04lx), eax(0x%08lx)", state->regs.edx&0xFFFF,
+					state->regs.eax);
+			} else {
+				em_outw(&state->regs, state->regs.edx);
+				TRACE_NP("OUT dx(0x%04lx), ax(0x%04lx)", state->regs.edx&0xFFFF,
+					state->regs.eax&0xFFFF);
+			}
+			state->regs.eip++;
+			break;
+		case INTn:
+			TRACE_NP("INT 0x%02x", insn[1]);
+			if (insn[1] == RETURN_TO_32_INT) {
+				ret = 1;
+			} else {
+				uint16 flags = state->regs.flags;
+
+				/* store real IF */
+				flags &= I_FLAG;
+				flags |= (uint16)state->if_flag << 9;
+
+				pushw(&state->regs, flags);
+				pushw(&state->regs, state->regs.cs);
+				pushw(&state->regs, state->regs.eip + 2);
+
+				state->regs.cs = get_int_seg(insn[1]);
+				state->regs.eip = get_int_off(insn[1]);
+				state->if_flag = 0;
+			}
+			break;
+		case IRET:
+			TRACE_NP("IRET");
+			state->regs.eip   = popw(&state->regs);
+			state->regs.cs    = popw(&state->regs);
+			state->regs.flags = popw(&state->regs);
+			/* restore IF */
+			state->if_flag = (state->regs.flags >> 9) & 0x01;
+			break;
+		case CLI:
+			TRACE_NP("CLI");
+			state->if_flag = 0;
+			state->regs.eip++;
+			break;
+		case STI:
+			TRACE_NP("STI");
+			state->if_flag = 1;
+			state->regs.eip++;
+			break;
+		case PUSHF:
+		{
+			TRACE_NP("PUSHF");
+			uint16 flags = state->regs.flags & I_FLAG;
+
+			/* store real IF */
+			flags |= (uint16)state->if_flag << 9;
+			if (prefix.size)
+				pushl(&state->regs, flags);
+			else
+				pushw(&state->regs, flags);
+			state->regs.eip++;
+			break;
+		}
+		case POPF:
+		{
+			TRACE_NP("POPF");
+			if (prefix.size)
+				state->regs.flags = popl(&state->regs);
+			else
+				state->regs.flags = popw(&state->regs);
+			/* restore IF */
+			state->if_flag = (state->regs.flags >> 9) & 0x01;
+			state->regs.eip++;
+			break;
+		}
+		default:
+			TRACE_NP("UNK");
+			ret = -1;
+	}
+
+	TRACE_NP("\n");
+	return ret;
+}
+
+
+static bool
+vm86_fault_callback(addr_t address, addr_t faultAddress, bool isWrite)
+{
+	struct iframe *frame = i386_get_user_iframe();
+
+	// we shouldn't have unhandled page faults in vm86 mode
+	x86_vm86_return((struct vm86_iframe *)frame, B_BAD_ADDRESS);
+
+	// not reached
+	return false;
+}
+
+
+// exported interface
+
+
+/*! Prepare the thread to execute BIOS code in virtual 8086 mode. Initializes
+	the given \a state to default values and maps the BIOS into the teams
+	address space. The size of the available conventional RAM is given in
+	\a ramSize in bytes which should be greater or equal to VM86_MIN_RAM_SIZE.
+*/
+extern "C" status_t
+vm86_prepare(struct vm86_state *state, unsigned int ramSize)
+{
+	struct team *team = thread_get_current_thread()->team;
+	void *address;
+	status_t ret = B_OK;
+	area_id vectors;
+
+	state->bios_area = B_ERROR;
+	state->ram_area  = B_ERROR;
+
+	// create RAM area
+	if (ramSize < VM86_MIN_RAM_SIZE)
+		ramSize = VM86_MIN_RAM_SIZE;
+	address = (void *)0;
+	state->ram_area = create_area_etc(team, "dos", &address, B_EXACT_ADDRESS,
+		ramSize, B_NO_LOCK, B_KERNEL_READ_AREA | B_KERNEL_WRITE_AREA
+		| B_READ_AREA | B_WRITE_AREA);
+	if (state->ram_area < B_OK) {
+		ret = state->ram_area;
+		goto error;
+	}
+
+	// copy int vectors and BIOS data area
+	vectors = map_physical_memory("int vectors", (void *)0, 0x502,
+		B_ANY_KERNEL_BLOCK_ADDRESS, B_KERNEL_READ_AREA | B_KERNEL_WRITE_AREA,
+		&address);
+	if (vectors < B_OK) {
+		ret = vectors;
+		goto error;
+	}
+	ret = user_memcpy((void *)0, address, 0x502);
+	*((uint32 *)0) = 0xDEADBEEF;
+	delete_area(vectors);
+	if (ret != B_OK)
+		goto error;
+
+	// map vga/bios area
+	address = (void *)0xA0000;
+	state->bios_area = vm_map_physical_memory(team->id, "bios",
+		&address, B_EXACT_ADDRESS, 0x60000, B_KERNEL_READ_AREA
+		| B_KERNEL_WRITE_AREA | B_READ_AREA | B_WRITE_AREA, (addr_t)0xA0000);
+	if (state->bios_area < B_OK) {
+		ret = state->bios_area;
+		goto error;
+	}
+
+	return B_OK;
+
+error:
+	if (state->bios_area > B_OK)
+		delete_area_etc(team, state->bios_area);
+	if (state->ram_area > B_OK)
+		delete_area_etc(team, state->ram_area);
+	return ret;
+}
+
+
+/*!	Free ressources which were allocated by vm86_prepare().
+*/
+extern "C" void
+vm86_cleanup(struct vm86_state *state)
+{
+	struct team *team = thread_get_current_thread()->team;
+
+	if (state->bios_area > B_OK)
+		delete_area_etc(team, state->bios_area);
+	if (state->ram_area > B_OK)
+		delete_area_etc(team, state->ram_area);
+}
+
+
+/*! Execute a BIOS call of interrupt \a vec. The given \a state must be
+	initialized by vm86_prepare() before, any registers needed by the BIOS too.
+
+	The function will return B_OK if the BIOS was called successfully,
+	otherwise an apropriate error code. After the call the registers are
+	copied back to \a state to reflect the status after the BIOS returned.
+*/
+extern "C" status_t
+vm86_do_int(struct vm86_state *state, uint8 vec)
+{
+	int8 *ip;
+	int emu_state = 0;
+	struct thread *thread = thread_get_current_thread();
+	status_t ret;
+
+	// prepare environment
+	state->regs.ss  = 0x600 >> 4;
+	state->regs.esp = (0x1000 - 0x600);
+	state->regs.cs = get_int_seg(vec);
+	state->regs.eip = get_int_off(vec);
+	state->if_flag = 0;
+
+	pushw(&state->regs, state->regs.flags);
+	pushw(&state->regs, 0x0000); /* CS */
+	pushw(&state->regs, 0x0600); /* IP */
+
+	ip    = (int8 *)0x600;
+	*ip++ = INTn;
+	*ip++ = RETURN_TO_32_INT;
+
+	// execute interrupt
+	thread->fault_callback = &vm86_fault_callback;
+	do {
+		ret = x86_vm86_enter(&state->regs);
+		if (ret != B_OK)
+			break;
+		emu_state = emulate(state);
+	} while (emu_state == 0);
+	thread->fault_callback = NULL;
+
+	return (emu_state < 0) ? B_BAD_DATA : ret;
+}
+
-- 
1.5.4.2

-------------------------------------------------------------------------
This SF.net email is sponsored by: Microsoft 
Defy all challenges. Microsoft(R) Visual Studio 2008. 
http://clk.atdmt.com/MRT/go/vse0120000070mrt/direct/01/

_______________________________________________
Open-beos-kernel-devel mailing list
Open-beos-kernel-devel-5NWGOfrQmneRv+LV9MX5uipxlwaOVQ5f@public.gmane.org
https://lists.sourceforge.net/lists/listinfo/open-beos-kernel-devel