Annotation of Gnu-Mach/i386/pc/rv86/rv86_real_int.c, revision 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.