[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