Last patch of the vm86 patch series from Jan Klötzke - thanks!:
* 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. git-svn-id: file:///srv/svn/repos/haiku/haiku/trunk@25610 a95241bf-73f2-0310-859d-f6bbb57e9c96
This commit is contained in:
parent
7da0a81c0e
commit
15173df4e9
@ -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 )
|
||||
|
38
headers/private/kernel/arch/x86/vm86.h
Normal file
38
headers/private/kernel/arch/x86/vm86.h
Normal file
@ -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
|
||||
|
||||
|
@ -33,6 +33,7 @@ KernelStaticLibrary libx86 :
|
||||
bios.cpp
|
||||
cpuid.S
|
||||
syscall.S
|
||||
vm86.cpp
|
||||
|
||||
generic_vm_physical_page_mapper.cpp
|
||||
:
|
||||
|
@ -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;
|
||||
|
@ -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
|
||||
|
||||
|
@ -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);
|
||||
|
645
src/system/kernel/arch/x86/vm86.cpp
Normal file
645
src/system/kernel/arch/x86/vm86.cpp
Normal file
@ -0,0 +1,645 @@
|
||||
/*
|
||||
* 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;
|
||||
case DSEG:
|
||||
default:
|
||||
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 *instruction;
|
||||
struct {
|
||||
unsigned char seg;
|
||||
unsigned int size : 1;
|
||||
unsigned int rep : 1;
|
||||
} prefix = { DSEG, 0, 0 };
|
||||
int ret = 0;
|
||||
|
||||
instruction = (unsigned char *)((unsigned int)state->regs.cs << 4);
|
||||
instruction += state->regs.eip;
|
||||
|
||||
TRACE("emulate: ");
|
||||
|
||||
/* handle prefixes */
|
||||
do {
|
||||
switch (*instruction) {
|
||||
case 0x66:
|
||||
TRACE_NP("[SIZE] ");
|
||||
prefix.size = 1 - prefix.size;
|
||||
state->regs.eip++;
|
||||
instruction++;
|
||||
break;
|
||||
case 0xf3:
|
||||
TRACE_NP("REP ");
|
||||
prefix.rep = 1;
|
||||
state->regs.eip++;
|
||||
instruction++;
|
||||
break;
|
||||
case CSEG:
|
||||
case SSEG:
|
||||
case DSEG:
|
||||
case ESEG:
|
||||
case FSEG:
|
||||
case GSEG:
|
||||
TRACE_NP("SEG(0x%02x) ", *instruction);
|
||||
prefix.seg = *instruction;
|
||||
state->regs.eip++;
|
||||
instruction++;
|
||||
break;
|
||||
case 0xf0:
|
||||
case 0xf2:
|
||||
case 0x67:
|
||||
/* these prefixes are just ignored */
|
||||
TRACE_NP("IGN(0x%02x) ", *instruction);
|
||||
state->regs.eip++;
|
||||
instruction++;
|
||||
break;
|
||||
default:
|
||||
ret = 1;
|
||||
}
|
||||
} while (!ret);
|
||||
|
||||
/* handle actual instruction */
|
||||
ret = 0;
|
||||
switch (*instruction) {
|
||||
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, instruction[1]);
|
||||
TRACE_NP("IN al(=0x%02lx), 0x%02x", state->regs.eax & 0xff,
|
||||
instruction[1]);
|
||||
state->regs.eip += 2;
|
||||
break;
|
||||
case INWI:
|
||||
if (prefix.size) {
|
||||
em_inl(&state->regs, instruction[1]);
|
||||
TRACE_NP("IN eax(=0x%08lx), 0x%02x", state->regs.eax,
|
||||
instruction[1]);
|
||||
} else {
|
||||
em_inw(&state->regs, instruction[1]);
|
||||
TRACE_NP("IN ax(=0x%04lx), 0x%02x", state->regs.eax & 0xffff,
|
||||
instruction[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, instruction[1]);
|
||||
TRACE_NP("OUT 0x%02x, al(0x%02lx)", instruction[1],
|
||||
state->regs.eax & 0xff);
|
||||
state->regs.eip += 2;
|
||||
break;
|
||||
case OUTWI:
|
||||
if (prefix.size) {
|
||||
em_outl(&state->regs, instruction[1]);
|
||||
TRACE_NP("OUT 0x%02x, eax(0x%08lx)", instruction[1],
|
||||
state->regs.eax);
|
||||
} else {
|
||||
em_outw(&state->regs, instruction[1]);
|
||||
TRACE_NP("OUT 0x%02x, ax(0x%04lx)", instruction[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", instruction[1]);
|
||||
if (instruction[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(instruction[1]);
|
||||
state->regs.eip = get_int_off(instruction[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;
|
||||
}
|
||||
|
||||
|
||||
// #pragma mark - 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;
|
||||
status_t ret = B_OK;
|
||||
area_id vectors;
|
||||
|
||||
state->bios_area = B_ERROR;
|
||||
|
||||
// create RAM area
|
||||
if (ramSize < VM86_MIN_RAM_SIZE)
|
||||
ramSize = VM86_MIN_RAM_SIZE;
|
||||
|
||||
void *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 emuState = 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;
|
||||
emuState = emulate(state);
|
||||
} while (emuState == 0);
|
||||
thread->fault_callback = NULL;
|
||||
|
||||
return emuState < 0 ? B_BAD_DATA : ret;
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user