diff --git a/headers/private/kernel/arch/x86/arch_cpu.h b/headers/private/kernel/arch/x86/arch_cpu.h index 11e412f52c..1575595eed 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 0000000000..4689431021 --- /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 +#include + +#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 7e976059b3..04e80a955c 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 4d2b4720bb..aebe32bc18 100644 --- a/src/system/kernel/arch/x86/arch_int.c +++ b/src/system/kernel/arch/x86/arch_int.c @@ -23,6 +23,7 @@ #include #include +#include #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 5c19920c5e..d63ecbbb52 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 e66cea33a5..77ea318efd 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 0000000000..41833070aa --- /dev/null +++ b/src/system/kernel/arch/x86/vm86.cpp @@ -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 +#include +#include +#include +#include +#include + +#include +#include + +//#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; +} +