|
|
1.1 ! root 1: /* ! 2: * Copyright (c) 1995-1994 The University of Utah and ! 3: * the Computer Systems Laboratory at the University of Utah (CSL). ! 4: * All rights reserved. ! 5: * ! 6: * Permission to use, copy, modify and distribute this software is hereby ! 7: * granted provided that (1) source code retains these copyright, permission, ! 8: * and disclaimer notices, and (2) redistributions including binaries ! 9: * reproduce the notices in supporting documentation, and (3) all advertising ! 10: * materials mentioning features or use of this software display the following ! 11: * acknowledgement: ``This product includes software developed by the ! 12: * Computer Systems Laboratory at the University of Utah.'' ! 13: * ! 14: * THE UNIVERSITY OF UTAH AND CSL ALLOW FREE USE OF THIS SOFTWARE IN ITS "AS ! 15: * IS" CONDITION. THE UNIVERSITY OF UTAH AND CSL DISCLAIM ANY LIABILITY OF ! 16: * ANY KIND FOR ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE. ! 17: * ! 18: * CSL requests users of this software to return to [email protected] any ! 19: * improvements that they make and grant CSL redistribution rights. ! 20: * ! 21: * Author: Bryan Ford, University of Utah CSL ! 22: */ ! 23: ! 24: #include <mach/machine/seg.h> ! 25: #include <mach/machine/proc_reg.h> ! 26: #include <mach/machine/far_ptr.h> ! 27: #include <mach/machine/eflags.h> ! 28: ! 29: #include "vm_param.h" ! 30: #include "real.h" ! 31: #include "real_tss.h" ! 32: #include "cpu.h" ! 33: #include "debug.h" ! 34: ! 35: ! 36: /* ! 37: ! 38: There seem to be three main ways to handle v86 mode: ! 39: ! 40: * The v86 environment is just an extension of the normal kernel environment: ! 41: you can switch to and from v86 mode just as you can change any other processor state. ! 42: You always keep running on the separate "logical" stack, ! 43: which is the kernel stack when running in protected mode, ! 44: or the user stack when running in v86 mode. ! 45: When in v86 mode, the "actual" kernel stack is just a stub ! 46: big enough to switch back to the "normal" kernel stack, ! 47: which was being used as the user stack while running in v86 mode. ! 48: Thus, v86 and protected-mode "segments" of stack data ! 49: can be interleaved together on the same logical stack. ! 50: ! 51: - To make a real int call from kernel pmode, ! 52: switch to v86 mode and execute an int instruction, ! 53: then switch back to protected mode. ! 54: ! 55: - To reflect an interrupt to v86 mode: ! 56: ! 57: > If the processor was running in v86 mode, ! 58: just adjust the kernel and user stacks ! 59: to emulate a real-mode interrupt, and return. ! 60: ! 61: > If the processor was running in pmode, ! 62: switch to v86 mode and re-trigger the interrupt ! 63: with a software int instruction. ! 64: ! 65: - To handle an interrupt in pmode: ! 66: ! 67: > If the processor was running in v86 mode, ! 68: switch from the stub stack to the user stack that was in use ! 69: (could be different from the stack we set originally, ! 70: because BIOS/DOS code might have switched stacks!), ! 71: call the interrupt handler, switch back, and return. ! 72: ! 73: > If the processor was running in pmode, ! 74: just call the interrupt handler and return. ! 75: ! 76: This method only works if the whole "kernel" is <64KB ! 77: and generally compatible with real-mode execution. ! 78: This is the model my DOS extender currently uses. ! 79: ! 80: One major disadvantage of this method ! 81: is that interrupt handlers can't run "general" protected-mode code, ! 82: such as typical code compiled by GCC. ! 83: This is because, if an interrupt occurs while in v86 mode, ! 84: the v86-mode ss:sp may point basically anywhere in the low 1MB, ! 85: and it therefore it can't be used directly as a pmode stack; ! 86: and the only other stack available is the miniscule stub stack. ! 87: Since "general" protected-mode code expects a full-size stack ! 88: with an SS equal to the normal protected-mode DS, ! 89: neither of these available stacks will suffice. ! 90: It is impossible to switch back to the original kernel stack ! 91: because arbitrary DOS or BIOS code might have switched from it ! 92: to a different stack somewhere else in the low 1MB, ! 93: and we have no way of telling where the SP was when that happened. ! 94: The upshot is that interrupt handlers must be extremely simple; ! 95: in MOSS, all they do is post a signal to "the process," ! 96: and return immediately without actually handling the interrupt. ! 97: ! 98: * The v86 environment is a separate "task" with its own user and kernel stacks; ! 99: you switch back and forth as if between multiple ordinary tasks, ! 100: the tasks can preempt each other, go idle waiting for events, etc. ! 101: ! 102: - To make a real int call from kernel pmode, ! 103: the task making the call essentially does a synchronous IPC to the v86 task. ! 104: If the v86 task is busy with another request or a reflected interrupt, ! 105: the calling task will go idle until the v86 task is available. ! 106: ! 107: - Reflecting an interrupt to v86 mode ! 108: basically amounts to sending a Unix-like "signal" to the v86 task: ! 109: ! 110: > If the processor was running in the v86 task, ! 111: just adjust the kernel and user stacks ! 112: to emulate a real-mode interrupt, and return. ! 113: ! 114: > If the processor was running in a protected-mode task ! 115: (or another v86-mode task), ! 116: post a signal to the v86 task, wake it up if it's asleep, ! 117: and invoke the scheduler to switch to the v86 task ! 118: if it has a higher priority than the currently running task. ! 119: ! 120: - To handle an interrupt in pmode, ! 121: just call the interrupt handler and return. ! 122: It doesn't matter whether the interrupt was from v86 or pmode, ! 123: because the kernel stacks look the same in either case. ! 124: ! 125: One big problem with this method is that if interrupts are to be handled in v86 mode, ! 126: all the typical problems of handling interrupts in user-mode tasks pop up. ! 127: In particular, an interrupt can now cause preemption, ! 128: so this will break an interruptible but nonpreemptible environment. ! 129: (The problem is not that the interrupted task is "preempted" ! 130: to switch temporarily to the v86 task to handle the interrupt; ! 131: the problem is that when the v86 task is done handling the interrupt, ! 132: the scheduler will be invoked and some task other than the interrupted task may be run.) ! 133: ! 134: Of course, this is undoubtedly the right solution ! 135: if that's the interrupt model the OS is using anyway ! 136: (i.e. if the OS already supports user-level protected-mode interrupts). ! 137: ! 138: * A bastardization of the two above approaches: ! 139: treat the v86 environment as a separate "task", ! 140: but a special one that doesn't behave at all like other tasks. ! 141: The v86 "task" in this case is more of an "interrupt co-stack" ! 142: that grows and shrinks alongside the normal interrupt stack ! 143: (or the current kernel stack, if interrupts are handled on the kernel stack). ! 144: Interrupts and real calls can cause switches between these two interrupt stacks, ! 145: but they can't cause preemption in the normal sense. ! 146: The route taken while building the stacks is exactly the opposite ! 147: the route taken while tearing it down. ! 148: ! 149: Now two "kernel stack pointers" have to be maintained all the time instead of one. ! 150: When running in protected mode: ! 151: ! 152: - The ESP register contains the pmode stack pointer. ! 153: - Some global variable contains the v86 stack pointer. ! 154: ! 155: When running in v86 mode: ! 156: ! 157: - The ESP register contains the v86 stack pointer. ! 158: (Note that BIOS/DOS code can switch stacks, ! 159: so at any given time it may point practically anywhere!) ! 160: - The current tss's esp0 contains the pmode stack pointer. ! 161: ! 162: Whenever a switch is made, a stack frame is placed on the new co-stack ! 163: indicating that the switch was performed. ! 164: ! 165: - To make a real int call from kernel pmode, ! 166: build a real-mode interrupt stack frame on the v86 interrupt stack, ! 167: build a v86-mode trap stack frame on the pmode stack, ! 168: set the tss's esp0 to point to the end of that stack frame, ! 169: and iret from it. ! 170: Then when the magic "done-with-real-call" int instruction is hit, ! 171: the pmode interrupt handler will see it ! 172: and know to simply destroy the v86 trap stack on the pmode stack. ! 173: ! 174: - Handling an interrupt can always be thought of as going "through" pmode: ! 175: switching from the v86 stack to the pmode stack ! 176: if the processor was in v86 mode when the interrupt was taken, ! 177: and switching from the pmode stack back to the v86 stack as described above ! 178: if the interrupt is to be reflected to v86 mode. ! 179: ! 180: Of course, optimized paths are possible: ! 181: ! 182: - To reflect an interrupt to v86 mode: ! 183: ! 184: > If the processor was running in v86 mode, ! 185: just adjust the kernel and user stack frames and return. ! 186: ! 187: > If the processor was running in pmode, ! 188: do as described above for explicit real int calls. ! 189: ! 190: - To handle an interrupt in pmode: ! 191: ! 192: > If the processor was running in v86 mode, ! 193: switch to the pmode stack, ! 194: stash the old v86 stack pointer variable on the pmode stack, ! 195: and set the v86 stack pointer variable to the new location. ! 196: Call the interrupt handler, ! 197: then tear down everything and return to v86 mode. ! 198: ! 199: Observation: ! 200: In the first and third models, ! 201: explicit real int calls are entirely symmetrical ! 202: to hardware interrupts from pmode to v86 mode. ! 203: This is valid because of the interruptible but nonpreemptible model: ! 204: no scheduling is involved, and the stack(s) will always be torn down ! 205: in exactly the opposite order in which they were built up. ! 206: In the second model, ! 207: explicit real calls are quite different, ! 208: because the BIOS is interruptible but nonpreemptible: ! 209: you can reflect an interrupt into the v86 task at any time, ! 210: but you can only make an explicit request to that task when it's ready ! 211: (i.e. no other requests or interrupts are outstanding). ! 212: ! 213: */ ! 214: ! 215: ! 216: ! 217: #define RV86_USTACK_SIZE 1024 ! 218: ! 219: vm_offset_t rv86_ustack_pa; ! 220: vm_offset_t rv86_return_int_pa; ! 221: struct far_pointer_32 rv86_usp; ! 222: struct far_pointer_16 rv86_rp; ! 223: ! 224: void rv86_real_int(int intnum, struct real_call_data *rcd) ! 225: { ! 226: unsigned short old_tr; ! 227: unsigned int old_eflags; ! 228: ! 229: /* If this is the first time this routine is being called, ! 230: initialize the kernel stack. */ ! 231: if (!rv86_ustack_pa) ! 232: { ! 233: rv86_ustack_pa = 0xa0000 - RV86_USTACK_SIZE; /* XXX */ ! 234: ! 235: assert(rv86_ustack_pa < 0x100000); ! 236: ! 237: /* Use the top two bytes of the ustack for an 'int $0xff' instruction. */ ! 238: rv86_return_int_pa = rv86_ustack_pa + RV86_USTACK_SIZE - 2; ! 239: *(short*)phystokv(rv86_return_int_pa) = 0xffcd; ! 240: ! 241: /* Set up the v86 stack pointer. */ ! 242: rv86_usp.seg = rv86_rp.seg = rv86_ustack_pa >> 4; ! 243: rv86_usp.ofs = rv86_rp.ofs = (rv86_ustack_pa & 0xf) + RV86_USTACK_SIZE - 2; ! 244: ! 245: /* Pre-allocate a real-mode interrupt stack frame. */ ! 246: rv86_usp.ofs -= 6; ! 247: } ! 248: ! 249: /* Make sure interrupts are disabled. */ ! 250: old_eflags = get_eflags(); ! 251: ! 252: /* Switch to the TSS to use in v86 mode. */ ! 253: old_tr = get_tr(); ! 254: cpu[0].tables.gdt[REAL_TSS_IDX].access &= ~ACC_TSS_BUSY; ! 255: set_tr(REAL_TSS); ! 256: ! 257: asm volatile(" ! 258: pushl %%ebp ! 259: pushl %%eax ! 260: call rv86_real_int_asm ! 261: popl %%eax ! 262: popl %%ebp ! 263: " : ! 264: : "a" (rcd), "S" (intnum) ! 265: : "eax", "ebx", "ecx", "edx", "esi", "edi"); ! 266: ! 267: /* Switch to the original TSS. */ ! 268: cpu[0].tables.gdt[old_tr/8].access &= ~ACC_TSS_BUSY; ! 269: set_tr(old_tr); ! 270: ! 271: /* Restore the original processor flags. */ ! 272: set_eflags(old_eflags); ! 273: } ! 274: ! 275: void (*real_int)(int intnum, struct real_call_data *rcd) = rv86_real_int; ! 276:
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.