Annotation of Gnu-Mach/i386/pc/rv86/rv86_real_int.c, revision 1.1.1.1

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: 

unix.superglobalmegacorp.com

This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.