summaryrefslogtreecommitdiff
path: root/i386/pc/rv86/rv86_real_int.c
diff options
context:
space:
mode:
Diffstat (limited to 'i386/pc/rv86/rv86_real_int.c')
-rw-r--r--i386/pc/rv86/rv86_real_int.c276
1 files changed, 0 insertions, 276 deletions
diff --git a/i386/pc/rv86/rv86_real_int.c b/i386/pc/rv86/rv86_real_int.c
deleted file mode 100644
index d9c35b6..0000000
--- a/i386/pc/rv86/rv86_real_int.c
+++ /dev/null
@@ -1,276 +0,0 @@
-/*
- * Copyright (c) 1995-1994 The University of Utah and
- * the Computer Systems Laboratory at the University of Utah (CSL).
- * All rights reserved.
- *
- * Permission to use, copy, modify and distribute this software is hereby
- * granted provided that (1) source code retains these copyright, permission,
- * and disclaimer notices, and (2) redistributions including binaries
- * reproduce the notices in supporting documentation, and (3) all advertising
- * materials mentioning features or use of this software display the following
- * acknowledgement: ``This product includes software developed by the
- * Computer Systems Laboratory at the University of Utah.''
- *
- * THE UNIVERSITY OF UTAH AND CSL ALLOW FREE USE OF THIS SOFTWARE IN ITS "AS
- * IS" CONDITION. THE UNIVERSITY OF UTAH AND CSL DISCLAIM ANY LIABILITY OF
- * ANY KIND FOR ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE.
- *
- * CSL requests users of this software to return to csl-dist@cs.utah.edu any
- * improvements that they make and grant CSL redistribution rights.
- *
- * Author: Bryan Ford, University of Utah CSL
- */
-
-#include <mach/machine/seg.h>
-#include <mach/machine/proc_reg.h>
-#include <mach/machine/far_ptr.h>
-#include <mach/machine/eflags.h>
-
-#include "vm_param.h"
-#include "real.h"
-#include "real_tss.h"
-#include "cpu.h"
-#include "debug.h"
-
-
-/*
-
- There seem to be three main ways to handle v86 mode:
-
- * The v86 environment is just an extension of the normal kernel environment:
- you can switch to and from v86 mode just as you can change any other processor state.
- You always keep running on the separate "logical" stack,
- which is the kernel stack when running in protected mode,
- or the user stack when running in v86 mode.
- When in v86 mode, the "actual" kernel stack is just a stub
- big enough to switch back to the "normal" kernel stack,
- which was being used as the user stack while running in v86 mode.
- Thus, v86 and protected-mode "segments" of stack data
- can be interleaved together on the same logical stack.
-
- - To make a real int call from kernel pmode,
- switch to v86 mode and execute an int instruction,
- then switch back to protected mode.
-
- - To reflect an interrupt to v86 mode:
-
- > If the processor was running in v86 mode,
- just adjust the kernel and user stacks
- to emulate a real-mode interrupt, and return.
-
- > If the processor was running in pmode,
- switch to v86 mode and re-trigger the interrupt
- with a software int instruction.
-
- - To handle an interrupt in pmode:
-
- > If the processor was running in v86 mode,
- switch from the stub stack to the user stack that was in use
- (could be different from the stack we set originally,
- because BIOS/DOS code might have switched stacks!),
- call the interrupt handler, switch back, and return.
-
- > If the processor was running in pmode,
- just call the interrupt handler and return.
-
- This method only works if the whole "kernel" is <64KB
- and generally compatible with real-mode execution.
- This is the model my DOS extender currently uses.
-
- One major disadvantage of this method
- is that interrupt handlers can't run "general" protected-mode code,
- such as typical code compiled by GCC.
- This is because, if an interrupt occurs while in v86 mode,
- the v86-mode ss:sp may point basically anywhere in the low 1MB,
- and it therefore it can't be used directly as a pmode stack;
- and the only other stack available is the miniscule stub stack.
- Since "general" protected-mode code expects a full-size stack
- with an SS equal to the normal protected-mode DS,
- neither of these available stacks will suffice.
- It is impossible to switch back to the original kernel stack
- because arbitrary DOS or BIOS code might have switched from it
- to a different stack somewhere else in the low 1MB,
- and we have no way of telling where the SP was when that happened.
- The upshot is that interrupt handlers must be extremely simple;
- in MOSS, all they do is post a signal to "the process,"
- and return immediately without actually handling the interrupt.
-
- * The v86 environment is a separate "task" with its own user and kernel stacks;
- you switch back and forth as if between multiple ordinary tasks,
- the tasks can preempt each other, go idle waiting for events, etc.
-
- - To make a real int call from kernel pmode,
- the task making the call essentially does a synchronous IPC to the v86 task.
- If the v86 task is busy with another request or a reflected interrupt,
- the calling task will go idle until the v86 task is available.
-
- - Reflecting an interrupt to v86 mode
- basically amounts to sending a Unix-like "signal" to the v86 task:
-
- > If the processor was running in the v86 task,
- just adjust the kernel and user stacks
- to emulate a real-mode interrupt, and return.
-
- > If the processor was running in a protected-mode task
- (or another v86-mode task),
- post a signal to the v86 task, wake it up if it's asleep,
- and invoke the scheduler to switch to the v86 task
- if it has a higher priority than the currently running task.
-
- - To handle an interrupt in pmode,
- just call the interrupt handler and return.
- It doesn't matter whether the interrupt was from v86 or pmode,
- because the kernel stacks look the same in either case.
-
- One big problem with this method is that if interrupts are to be handled in v86 mode,
- all the typical problems of handling interrupts in user-mode tasks pop up.
- In particular, an interrupt can now cause preemption,
- so this will break an interruptible but nonpreemptible environment.
- (The problem is not that the interrupted task is "preempted"
- to switch temporarily to the v86 task to handle the interrupt;
- the problem is that when the v86 task is done handling the interrupt,
- the scheduler will be invoked and some task other than the interrupted task may be run.)
-
- Of course, this is undoubtedly the right solution
- if that's the interrupt model the OS is using anyway
- (i.e. if the OS already supports user-level protected-mode interrupts).
-
- * A bastardization of the two above approaches:
- treat the v86 environment as a separate "task",
- but a special one that doesn't behave at all like other tasks.
- The v86 "task" in this case is more of an "interrupt co-stack"
- that grows and shrinks alongside the normal interrupt stack
- (or the current kernel stack, if interrupts are handled on the kernel stack).
- Interrupts and real calls can cause switches between these two interrupt stacks,
- but they can't cause preemption in the normal sense.
- The route taken while building the stacks is exactly the opposite
- the route taken while tearing it down.
-
- Now two "kernel stack pointers" have to be maintained all the time instead of one.
- When running in protected mode:
-
- - The ESP register contains the pmode stack pointer.
- - Some global variable contains the v86 stack pointer.
-
- When running in v86 mode:
-
- - The ESP register contains the v86 stack pointer.
- (Note that BIOS/DOS code can switch stacks,
- so at any given time it may point practically anywhere!)
- - The current tss's esp0 contains the pmode stack pointer.
-
- Whenever a switch is made, a stack frame is placed on the new co-stack
- indicating that the switch was performed.
-
- - To make a real int call from kernel pmode,
- build a real-mode interrupt stack frame on the v86 interrupt stack,
- build a v86-mode trap stack frame on the pmode stack,
- set the tss's esp0 to point to the end of that stack frame,
- and iret from it.
- Then when the magic "done-with-real-call" int instruction is hit,
- the pmode interrupt handler will see it
- and know to simply destroy the v86 trap stack on the pmode stack.
-
- - Handling an interrupt can always be thought of as going "through" pmode:
- switching from the v86 stack to the pmode stack
- if the processor was in v86 mode when the interrupt was taken,
- and switching from the pmode stack back to the v86 stack as described above
- if the interrupt is to be reflected to v86 mode.
-
- Of course, optimized paths are possible:
-
- - To reflect an interrupt to v86 mode:
-
- > If the processor was running in v86 mode,
- just adjust the kernel and user stack frames and return.
-
- > If the processor was running in pmode,
- do as described above for explicit real int calls.
-
- - To handle an interrupt in pmode:
-
- > If the processor was running in v86 mode,
- switch to the pmode stack,
- stash the old v86 stack pointer variable on the pmode stack,
- and set the v86 stack pointer variable to the new location.
- Call the interrupt handler,
- then tear down everything and return to v86 mode.
-
- Observation:
- In the first and third models,
- explicit real int calls are entirely symmetrical
- to hardware interrupts from pmode to v86 mode.
- This is valid because of the interruptible but nonpreemptible model:
- no scheduling is involved, and the stack(s) will always be torn down
- in exactly the opposite order in which they were built up.
- In the second model,
- explicit real calls are quite different,
- because the BIOS is interruptible but nonpreemptible:
- you can reflect an interrupt into the v86 task at any time,
- but you can only make an explicit request to that task when it's ready
- (i.e. no other requests or interrupts are outstanding).
-
-*/
-
-
-
-#define RV86_USTACK_SIZE 1024
-
-vm_offset_t rv86_ustack_pa;
-vm_offset_t rv86_return_int_pa;
-struct far_pointer_32 rv86_usp;
-struct far_pointer_16 rv86_rp;
-
-void rv86_real_int(int intnum, struct real_call_data *rcd)
-{
- unsigned short old_tr;
- unsigned int old_eflags;
-
- /* If this is the first time this routine is being called,
- initialize the kernel stack. */
- if (!rv86_ustack_pa)
- {
- rv86_ustack_pa = 0xa0000 - RV86_USTACK_SIZE; /* XXX */
-
- assert(rv86_ustack_pa < 0x100000);
-
- /* Use the top two bytes of the ustack for an 'int $0xff' instruction. */
- rv86_return_int_pa = rv86_ustack_pa + RV86_USTACK_SIZE - 2;
- *(short*)phystokv(rv86_return_int_pa) = 0xffcd;
-
- /* Set up the v86 stack pointer. */
- rv86_usp.seg = rv86_rp.seg = rv86_ustack_pa >> 4;
- rv86_usp.ofs = rv86_rp.ofs = (rv86_ustack_pa & 0xf) + RV86_USTACK_SIZE - 2;
-
- /* Pre-allocate a real-mode interrupt stack frame. */
- rv86_usp.ofs -= 6;
- }
-
- /* Make sure interrupts are disabled. */
- old_eflags = get_eflags();
-
- /* Switch to the TSS to use in v86 mode. */
- old_tr = get_tr();
- cpu[0].tables.gdt[REAL_TSS_IDX].access &= ~ACC_TSS_BUSY;
- set_tr(REAL_TSS);
-
- asm volatile("
- pushl %%ebp
- pushl %%eax
- call rv86_real_int_asm
- popl %%eax
- popl %%ebp
- " :
- : "a" (rcd), "S" (intnum)
- : "eax", "ebx", "ecx", "edx", "esi", "edi");
-
- /* Switch to the original TSS. */
- cpu[0].tables.gdt[old_tr/8].access &= ~ACC_TSS_BUSY;
- set_tr(old_tr);
-
- /* Restore the original processor flags. */
- set_eflags(old_eflags);
-}
-
-void (*real_int)(int intnum, struct real_call_data *rcd) = rv86_real_int;
-