+/*
+ * V86 mode assist for interrupt handling.
+ */
+boolean_t v86_assist_on = TRUE;
+boolean_t v86_unsafe_ok = FALSE;
+boolean_t v86_do_sti_cli = TRUE;
+boolean_t v86_do_sti_immediate = FALSE;
+
+#define V86_IRET_PENDING 0x4000
+
+int cli_count = 0;
+int sti_count = 0;
+
+boolean_t
+v86_assist(
+ thread_t thread,
+ register struct i386_saved_state *regs)
+{
+ register struct v86_assist_state *v86 = &thread->machine.pcb->ims.v86s;
+
+/*
+ * Build an 8086 address. Use only when off is known to be 16 bits.
+ */
+#define Addr8086(seg,off) ((((seg) & 0xffff) << 4) + (off))
+
+#define EFL_V86_SAFE ( EFL_OF | EFL_DF | EFL_TF \
+ | EFL_SF | EFL_ZF | EFL_AF \
+ | EFL_PF | EFL_CF )
+ struct iret_32 {
+ int eip;
+ int cs;
+ int eflags;
+ };
+ struct iret_16 {
+ unsigned short ip;
+ unsigned short cs;
+ unsigned short flags;
+ };
+ union iret_struct {
+ struct iret_32 iret_32;
+ struct iret_16 iret_16;
+ };
+
+ struct int_vec {
+ unsigned short ip;
+ unsigned short cs;
+ };
+
+ if (!v86_assist_on)
+ return FALSE;
+
+ /*
+ * If delayed STI pending, enable interrupts.
+ * Turn off tracing if on only to delay STI.
+ */
+ if (v86->flags & V86_IF_PENDING) {
+ v86->flags &= ~V86_IF_PENDING;
+ v86->flags |= EFL_IF;
+ if ((v86->flags & EFL_TF) == 0)
+ regs->efl &= ~EFL_TF;
+ }
+
+ if (regs->trapno == T_DEBUG) {
+
+ if (v86->flags & EFL_TF) {
+ /*
+ * Trace flag was also set - it has priority
+ */
+ return FALSE; /* handle as single-step */
+ }
+ /*
+ * Fall through to check for interrupts.
+ */
+ }
+ else if (regs->trapno == T_GENERAL_PROTECTION) {
+ /*
+ * General protection error - must be an 8086 instruction
+ * to emulate.
+ */
+ register int eip;
+ boolean_t addr_32 = FALSE;
+ boolean_t data_32 = FALSE;
+ int io_port;
+
+ /*
+ * Set up error handler for bad instruction/data
+ * fetches.
+ */
+ __asm__("movl $(addr_error), %0" : : "m" (thread->recover));
+
+ eip = regs->eip;
+ while (TRUE) {
+ unsigned char opcode;
+
+ if (eip > 0xFFFF) {
+ thread->recover = 0;
+ return FALSE; /* GP fault: IP out of range */
+ }
+
+ opcode = *(unsigned char *)Addr8086(regs->cs,eip);
+ eip++;
+ switch (opcode) {
+ case 0xf0: /* lock */
+ case 0xf2: /* repne */
+ case 0xf3: /* repe */
+ case 0x2e: /* cs */
+ case 0x36: /* ss */
+ case 0x3e: /* ds */
+ case 0x26: /* es */
+ case 0x64: /* fs */
+ case 0x65: /* gs */
+ /* ignore prefix */
+ continue;
+
+ case 0x66: /* data size */
+ data_32 = TRUE;
+ continue;
+
+ case 0x67: /* address size */
+ addr_32 = TRUE;
+ continue;
+
+ case 0xe4: /* inb imm */
+ case 0xe5: /* inw imm */
+ case 0xe6: /* outb imm */
+ case 0xe7: /* outw imm */
+ io_port = *(unsigned char *)Addr8086(regs->cs, eip);
+ eip++;
+ goto do_in_out;
+
+ case 0xec: /* inb dx */
+ case 0xed: /* inw dx */
+ case 0xee: /* outb dx */
+ case 0xef: /* outw dx */
+ case 0x6c: /* insb */
+ case 0x6d: /* insw */
+ case 0x6e: /* outsb */
+ case 0x6f: /* outsw */
+ io_port = regs->edx & 0xffff;
+
+ do_in_out:
+ if (!data_32)
+ opcode |= 0x6600; /* word IO */
+
+ switch (emulate_io(regs, opcode, io_port)) {
+ case EM_IO_DONE:
+ /* instruction executed */
+ break;
+ case EM_IO_RETRY:
+ /* port mapped, retry instruction */
+ thread->recover = 0;
+ return TRUE;
+ case EM_IO_ERROR:
+ /* port not mapped */
+ thread->recover = 0;
+ return FALSE;
+ }
+ break;
+
+ case 0xfa: /* cli */
+ if (!v86_do_sti_cli) {
+ thread->recover = 0;
+ return (FALSE);
+ }
+
+ v86->flags &= ~EFL_IF;
+ /* disable simulated interrupts */
+ cli_count++;
+ break;
+
+ case 0xfb: /* sti */
+ if (!v86_do_sti_cli) {
+ thread->recover = 0;
+ return (FALSE);
+ }
+
+ if ((v86->flags & EFL_IF) == 0) {
+ if (v86_do_sti_immediate) {
+ v86->flags |= EFL_IF;
+ } else {
+ v86->flags |= V86_IF_PENDING;
+ regs->efl |= EFL_TF;
+ }
+ /* single step to set IF next inst. */
+ }
+ sti_count++;
+ break;
+
+ case 0x9c: /* pushf */
+ {
+ int flags;
+ vm_offset_t sp;
+ unsigned int size;
+
+ flags = regs->efl;
+ if ((v86->flags & EFL_IF) == 0)
+ flags &= ~EFL_IF;
+
+ if ((v86->flags & EFL_TF) == 0)
+ flags &= ~EFL_TF;
+ else flags |= EFL_TF;
+
+ sp = regs->uesp;
+ if (!addr_32)
+ sp &= 0xffff;
+ else if (sp > 0xffff)
+ goto stack_error;
+ size = (data_32) ? 4 : 2;
+ if (sp < size)
+ goto stack_error;
+ sp -= size;
+ if (copyout((char *)&flags,
+ (user_addr_t)Addr8086(regs->ss,sp),
+ size))
+ goto addr_error;
+ if (addr_32)
+ regs->uesp = sp;
+ else
+ regs->uesp = (regs->uesp & 0xffff0000) | sp;
+ break;
+ }
+
+ case 0x9d: /* popf */
+ {
+ vm_offset_t sp;
+ int nflags;
+
+ sp = regs->uesp;
+ if (!addr_32)
+ sp &= 0xffff;
+ else if (sp > 0xffff)
+ goto stack_error;
+
+ if (data_32) {
+ if (sp > 0xffff - sizeof(int))
+ goto stack_error;
+ nflags = *(int *)Addr8086(regs->ss,sp);
+ sp += sizeof(int);
+ }
+ else {
+ if (sp > 0xffff - sizeof(short))
+ goto stack_error;
+ nflags = *(unsigned short *)
+ Addr8086(regs->ss,sp);
+ sp += sizeof(short);
+ }
+ if (addr_32)
+ regs->uesp = sp;
+ else
+ regs->uesp = (regs->uesp & 0xffff0000) | sp;
+
+ if (v86->flags & V86_IRET_PENDING) {
+ v86->flags = nflags & (EFL_TF | EFL_IF);
+ v86->flags |= V86_IRET_PENDING;
+ } else {
+ v86->flags = nflags & (EFL_TF | EFL_IF);
+ }
+ regs->efl = (regs->efl & ~EFL_V86_SAFE)
+ | (nflags & EFL_V86_SAFE);
+ break;
+ }
+ case 0xcf: /* iret */
+ {
+ vm_offset_t sp;
+ int nflags;
+ union iret_struct iret_struct;
+
+ v86->flags &= ~V86_IRET_PENDING;
+ sp = regs->uesp;
+ if (!addr_32)
+ sp &= 0xffff;
+ else if (sp > 0xffff)
+ goto stack_error;
+
+ if (data_32) {
+ if (sp > 0xffff - sizeof(struct iret_32))
+ goto stack_error;
+ iret_struct.iret_32 =
+ *(struct iret_32 *) Addr8086(regs->ss,sp);
+ sp += sizeof(struct iret_32);
+ }
+ else {
+ if (sp > 0xffff - sizeof(struct iret_16))
+ goto stack_error;
+ iret_struct.iret_16 =
+ *(struct iret_16 *) Addr8086(regs->ss,sp);
+ sp += sizeof(struct iret_16);
+ }
+ if (addr_32)
+ regs->uesp = sp;
+ else
+ regs->uesp = (regs->uesp & 0xffff0000) | sp;
+
+ if (data_32) {
+ eip = iret_struct.iret_32.eip;
+ regs->cs = iret_struct.iret_32.cs & 0xffff;
+ nflags = iret_struct.iret_32.eflags;
+ }
+ else {
+ eip = iret_struct.iret_16.ip;
+ regs->cs = iret_struct.iret_16.cs;
+ nflags = iret_struct.iret_16.flags;
+ }
+
+ v86->flags = nflags & (EFL_TF | EFL_IF);
+ regs->efl = (regs->efl & ~EFL_V86_SAFE)
+ | (nflags & EFL_V86_SAFE);
+ break;
+ }
+ default:
+ /*
+ * Instruction not emulated here.
+ */
+ thread->recover = 0;
+ return FALSE;
+ }
+ break; /* exit from 'while TRUE' */
+ }
+ regs->eip = (regs->eip & 0xffff0000) | eip;
+ }
+ else {
+ /*
+ * Not a trap we handle.
+ */
+ thread->recover = 0;
+ return FALSE;
+ }
+
+ if ((v86->flags & EFL_IF) && ((v86->flags & V86_IRET_PENDING)==0)) {
+
+ struct v86_interrupt_table *int_table;
+ int int_count;
+ int vec;
+ int i;
+
+ int_table = (struct v86_interrupt_table *) v86->int_table;
+ int_count = v86->int_count;
+
+ vec = 0;
+ for (i = 0; i < int_count; int_table++, i++) {
+ if (!int_table->mask && int_table->count > 0) {
+ int_table->count--;
+ vec = int_table->vec;
+ break;
+ }
+ }
+ if (vec != 0) {
+ /*
+ * Take this interrupt
+ */
+ vm_offset_t sp;
+ struct iret_16 iret_16;
+ struct int_vec int_vec;
+
+ sp = regs->uesp & 0xffff;
+ if (sp < sizeof(struct iret_16))
+ goto stack_error;
+ sp -= sizeof(struct iret_16);
+ iret_16.ip = regs->eip;
+ iret_16.cs = regs->cs;
+ iret_16.flags = regs->efl & 0xFFFF;
+ if ((v86->flags & EFL_TF) == 0)
+ iret_16.flags &= ~EFL_TF;
+ else iret_16.flags |= EFL_TF;
+
+ (void) memcpy((char *) &int_vec,
+ (char *) (sizeof(struct int_vec) * vec),
+ sizeof (struct int_vec));
+ if (copyout((char *)&iret_16,
+ (user_addr_t)Addr8086(regs->ss,sp),
+ sizeof(struct iret_16)))
+ goto addr_error;
+ regs->uesp = (regs->uesp & 0xFFFF0000) | (sp & 0xffff);
+ regs->eip = int_vec.ip;
+ regs->cs = int_vec.cs;
+ regs->efl &= ~EFL_TF;
+ v86->flags &= ~(EFL_IF | EFL_TF);
+ v86->flags |= V86_IRET_PENDING;
+ }
+ }
+
+ thread->recover = 0;
+ return TRUE;
+
+ /*
+ * On address error, report a page fault.
+ * XXX report GP fault - we don`t save
+ * the faulting address.
+ */
+ addr_error:
+ __asm__("addr_error:;");
+ thread->recover = 0;
+ return FALSE;
+
+ /*
+ * On stack address error, return stack fault (12).
+ */
+ stack_error:
+ thread->recover = 0;
+ regs->trapno = T_STACK_FAULT;
+ return FALSE;
+}