📄 interrupt.s
字号:
.file "interrupt.S"/* * Copyright (C) 1998, 1999, 2001, Jonathan S. Shapiro. * * This file is part of the EROS Operating System. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2, * or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #include <eros/i486/asm.h>#include <eros/i486/target-asm.h>#include <arch-kerninc/PTE.hxx>#include <eros/ProcessState.h>#include <eros/Invoke.h>#define SPURIOUS_CHECK/* #define V86_SUPPORT */#define DEBUG_NESTED_IRET#if 0#define DEBUG_GP_IPC /* requires TRAP_DEBUG */#endif#define TRAP_DEBUG /* Following are now in the Makefile: #define FAST_IPC_ARG_VALIDATE #define ASM_VALIDATE_STRINGS */#include "process_asm_offsets.h"#if 0#define FAST_IPC#define FAST_IPC_RETURN#define FAST_IPC_STRINGS#define FAST_IPC_REDSEG/* #define FAST_IPC_BPT */#define UNFAST_IPC_BPT#endif/* Offsets into a virgin trap frame */#define TR_OFF_SS 24#define TR_OFF_ESP 20#define TR_OFF_EFLAGS 16#define TR_OFF_CS 12#define TR_OFF_EIP 8#define TR_OFF_Error 4/* #define TR_OFF_ExceptNo 0 */ /* Offsets into stack-based IPC frame */#define IPC_invType 0#define IPC_invKey 4#define IPC_sndLen 8#define IPC_sndPtr 12#define IPC_sndKeys 16#define IPC_rcvLen 20#define IPC_rcvPtr 24#define IPC_rcvKeys 28#ifdef OPTION_OB_MOD_CHECK#define OBHDR_SZ 0x34#else#define OBHDR_SZ 0x30#endif#define OFF_THRD_CTXT 8/* * Space for the interrupt stack. * * The interrupt stack used to have an unmapped page at the bottom * to catch stack overflow. This was never a particularly bright idea. * If you actually managed to hit that point, you would page fault, * which would take an exception that couldn't win (because the page * was unmapped), which would in turn double fault, and the system * would just mysteriously reboot. * * Better to do an overflow test in the dispatch code until we are * confident. * */ .globl EXT(InterruptStackBottom) .globl EXT(InterruptStackLimit) .globl EXT(InterruptStackTop) .bss .align 4LEXT(InterruptStackBottom) .space 128 /* fluff for overflow check! */LEXT(InterruptStackLimit) .space 12160 /* total stack is 8192 */LEXT(InterruptStackTop) .data#ifdef OPTION_KERN_PROFILEGEXT(KernelProfileTable) .long 0#endif#ifdef FAST_IPC .align 4redflags: .long 0redw1: .long 0rednodekey: .long 0keyinfo: .long 0#endif#ifdef FAST_IPC_STATSALIGNEDVAR(nFastIpcPath,4) .long 0ALIGNEDVAR(nFastIpcFast,4) .long 0ALIGNEDVAR(nFastIpcRedSeg,4) .long 0ALIGNEDVAR(nFastIpcString,4) .long 0ALIGNEDVAR(nFastIpcSmallString,4) .long 0ALIGNEDVAR(nFastIpcLargeString,4) .long 0ALIGNEDVAR(nFastIpcNoString,4) .long 0ALIGNEDVAR(nFastIpcRcvPf,4) .long 0ALIGNEDVAR(nFastIpcEnd,4) .long 0ALIGNEDVAR(nFastIpcPrepared,4) .long 0/* Following is intended for use as a ``floater'' in the IPC code to find out where things are going wrong. */ALIGNEDVAR(nFastIpcOK,4) .long 0#endif GEXT(DomainTracingScratchpad) .long 0 .long 0/* * On interrupt or trap, we wish to build a stack image that captures * the per-process state. When we are done with all of this, the stack * will look as follows: * * invType; * rcvPtr; * sndLen; * sndPtr; * gs if from user mode, else unused * fs if from user mode, else unused * ds if from user mode, else unused * es if from user mode, else unused * ss if from user mode, else unused * esp if from user mode, else unused * eflags * cs * eip * error code (zero if none) * trap number/interrupt number * eax * ecx * edx * ebx * cr2 if page fault, else unused * ebp * esi * edi * cr3 * 0 <- %esp * * The zero at the bottom of the save area indicates the special * functional units, if any that need to be reloaded to resume this * thread. On entry into the kernel, they hold the right values, so * this word is initially 0. On return, some units may need to be * reloaded, so the value may be non-zero. * * An implication of the 'unused' entries is that not all of the * SaveArea structure is necessarily valid. This is exactly true; the * kernel save area simply doesn't need to include all of the state * that the user save area does. * * ARCHITECTURAL BRAIN DEATH ALERT * * One of the more special "features" of the x86 is that it can take * exceptions on the IRET instruction. This shouldn't happen when * returning to a kernel-mode thread, where we fully control what goes * on to the stack, but there really isn't much we can do to stop user * code from, say, attempting to load invalid segment register values. * * This isn't all that big a problem, given that we can arrange things * so as to recover properly, but one needs to be aware of it in order to * understand how the hell reload works. * * There are 5 instructions in the user process reload sequence that can * cause a cascaded exception: * * popl %es * popl %ds * popl %fs * popl %gs * and * iret * * The cascaded exception happens if any of the segment selectors are * inappropriate, or if fetching the instruction at CS:EIP causes a * page fault. In that event, we will end up taking an exception back * onto the user save area before the old exception has been * completely dealt with. The exceptions that might be taken in such a * case are: * * #GP -- if code seg was bogus * #SS -- if stack seg was bogus * #NP -- if stack segment was not present * #TS -- if returning to invalid task segment * #AC -- if alignment checking enabled * #PF -- if instruction page not present * * If one of these occurs, it will push a minimum of 5 words before we * get a chance to set things right: * * exception number * error code * eip * cs * eflags * * The trick in such a case is to patch up the stack so that it looks * like this exception was generated by the user instruction rather * than by the return path. In the iret case, the 5 words that get * clobbered can be reconstructed from the state on the processor. * Unfortunately, the same is NOT true when a fault occurs during one * of the segment reloads. * * If a cascaded interrupt is taken, we examine the return PC to see * if it was the PC of the IRET instruction. If so, the portion of * the save area that was smashed is: * * SMASHED WITH * error code (zero if none) eflags * trap number/interrupt number kern code cs * eax eip of IRET instr * ecx err code * sp-> edx trap no * * What we do in this case is move the err code and trap number up 3 * words (i.e. copy them into their proper positions), rewrite the * %eax, %ecx, and %edx values from the processor registers, adjust * the stack pointer to point to the bottom of the save area, and * dispatch back into OnTrapOrInterrupt */ .text /* * Interrupt entry point definitions: */ #define DEFENTRY(vecno) \GEXT(istub##vecno) \ pushl $0; \ pushl $vecno; \ jmp EXT(intr_entry) #define DEFENTRY_EC(vecno, label) \GEXT(istub##vecno) \ pushl $vecno; \ jmp EXT(label) /* Steps in the stuff below: * 1. Save enough to check for spurious interrupt. Using pusha * wastes about 2 cycles, but is worth it if we decide to * actually TAKE the interrupt. * * 2. See if the interrupt was spurious. If so, forget it and bail * * 3. Mark the interrupt as pending * * 4. ACK the PIC * * 5. Check if nested, and bail if appropriate */ /* Note that this definition only works because the kernel is * mapped into the user address space!!! */#define DEFIRQ1(pendingbit, vecno, PICbit) \GEXT(istub##vecno) \ /* Save minimal state: */; \ pushl $0; \ pushl $vecno; \ pusha; \ ;; \ /* Disable the interrupt on the PIC: */; \ ss ; \ movb EXT(pic1_mask),%al; \ orb $PICbit,%al; \ outb %al,$0x21; \ ss ; \ movb %al,EXT(pic1_mask); \ /* ACK the PIC: */; \ movb $0x20,%al; \ outb %al,$0x20; \ jmp EXT(intr_common) #define DEFIRQ2(pendingbit, vecno, PICbit) \GEXT(istub##vecno) \ /* Save minimal state: */; \ pushl $0; \ pushl $vecno; \ pusha; \ ;; \ /* Disable the interrupt on the PIC: */; \ ss ; \ movb EXT(pic2_mask),%al; \ orb $PICbit,%al; \ outb %al,$0xa1; \ ss ; \ movb %al,EXT(pic2_mask); \ movb $0x20,%al; \ /* ACK the secondary PIC: */; \ outb %al,$0xa0; \ /* ACK the primary PIC: */; \ outb %al,$0x20; \ jmp EXT(intr_common) DEFENTRY(0x00)DEFENTRY(0x01)DEFENTRY(0x02)DEFENTRY(0x03)DEFENTRY(0x04)DEFENTRY(0x05)#ifdef TRAP_DEBUGDEFENTRY(0x06)#elseGEXT(istub0x06) pushl $0 pushl $vecno /* below printed at line 8, in case we die. */ ss movl $0x8f4e8f49,0x000b8500 /* "IN" */ ss movl $0x8f548f53,0x000b8504 /* "ST" */ ss movl $0x8f588f20,0x000b8508 /* " X" */ jmp dump_state#endif DEFENTRY(0x07)DEFENTRY(0x08)DEFENTRY(0x09) /* * if invaltss happens in the kernel return path we'll never ses * it, so don't even bother: */DEFENTRY_EC(0x0a, intr_entry)DEFENTRY_EC(0x0b, intr_ec)DEFENTRY_EC(0x0c, intr_ec)DEFENTRY_EC(0x0d, intr_ec)DEFENTRY_EC(0x0e, intr_pagefault)DEFENTRY(0x0f) DEFENTRY(0x10)DEFENTRY_EC(0x11, intr_entry) /* alignment check */DEFENTRY(0x12) /* machine check -- not sure whether this generates an EC or not */DEFENTRY(0x13)DEFENTRY(0x14)DEFENTRY(0x15)DEFENTRY(0x16)DEFENTRY(0x17)DEFENTRY(0x18)DEFENTRY(0x19)DEFENTRY(0x1a)DEFENTRY(0x1b)DEFENTRY(0x1c)DEFENTRY(0x1d)DEFENTRY(0x1e)DEFENTRY(0x1f) /* 0x20 is clock fast path interrupt - see below */DEFIRQ1(0x2, 0x21, 0x2)DEFIRQ1(0x4, 0x22, 0x4)DEFIRQ1(0x8, 0x23, 0x8)DEFIRQ1(0x10, 0x24, 0x10)DEFIRQ1(0x20, 0x25, 0x20)DEFIRQ1(0x40, 0x26, 0x40)#ifndef SPURIOUS_CHECKDEFIRQ1(0x80, 0x27, 0x80)#endif DEFIRQ2(0x100, 0x28, 0x1)DEFIRQ2(0x200, 0x29, 0x2)DEFIRQ2(0x400, 0x2a, 0x4)DEFIRQ2(0x800, 0x2b, 0x8)DEFIRQ2(0x1000, 0x2c, 0x10)DEFIRQ2(0x2000, 0x2d, 0x20)DEFIRQ2(0x4000, 0x2e, 0x40)#ifndef SPURIOUS_CHECKDEFIRQ2(0x8000, 0x2f, 0x80)#endif #ifdef SPURIOUS_CHECK /* Entry point for IRQ's 7 and 15 are a special case, because * the hardware may generate spurious interrupts that we wish * to suppress. */GEXT(istub0x27) /* Save minimal state: */; pushl $0 pushl $0x27 pusha /* Check for spurious interrupt: */ movb $0xb,%al outb %al,$0x20 inb $0x20,%al cmpb $0,%al /* test sign bit -- if clear, spurious */ jge 1f /* Disable the interrupt on the PIC: */ ss movb EXT(pic1_mask),%al orb $0x80,%al outb %al,$0x21 ss movb %al,EXT(pic1_mask) /* ACK the PIC: */ movb $0x20,%al outb %al,$0x20 jmp EXT(intr_common)1: /* spurious interrupt -- ack and bail */ /* ACK the PIC: */ movb $0x20,%al outb %al,$0x20 jmp EXT(.L_fast_int_exit)GEXT(istub0x2f) /* Save minimal state: */; pushl $0 pushl $0x2f pusha /* Check for spurious interrupt: */ movb $0xb,%al outb %al,$0xa0 inb $0xa0,%al cmpb $0,%al /* test sign bit -- if clear, spurious */ jge 1f /* Disable the interrupt on the PIC: */ ss movb EXT(pic2_mask),%al orb $0x80,%al outb %al,$0x21 ss movb %al,EXT(pic2_mask) /* ACK both primary and secondary PIC: */ movb $0x20,%al outb %al,$0x20 outb %al,$0xa0 jmp EXT(intr_common)1: /* spurious interrupt -- ack and bail */ /* ACK the PIC: */ movb $0x20,%al outb %al,$0x20 outb %al,$0xa0 jmp EXT(.L_fast_int_exit)#endif DEFENTRY(0x30) /* INVOCATION interrupt (0x31) is HIGH FREQUENCY, and so has been moved to just above handler for benefit of cache adjacency. */ .data.LC1: .string "Fault stack is 0x%08x\n\0" .text /* * All the interrupts that push an error code can interrupt the * IRET, and we therefore may need to reshuffle the stack: */GEXT(intr_ec) /* If the trap came from v86 code, or from user code, handle it as a user level trap. */#ifdef V86_SUPPORT /* * If we interrupted a V86 task, segment registers have * already been saved, so no need to save them redundantly. */
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -