root/sys/arch/i386/i386/vector.s
/*      $OpenBSD: vector.s,v 1.24 2022/12/08 01:25:44 guenther Exp $    */
/*      $NetBSD: vector.s,v 1.32 1996/01/07 21:29:47 mycroft Exp $      */

/*
 * Copyright (c) 1993, 1994, 1995 Charles M. Hannum.  All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *      notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *      notice, this list of conditions and the following disclaimer in the
 *      documentation and/or other materials provided with the distribution.
 * 3. All advertising materials mentioning features or use of this software
 *      must display the following acknowledgement:
 *      This product includes software developed by Charles M. Hannum.
 * 4. The name of the author may not be used to endorse or promote products
 *      derived from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */

#include <machine/i8259.h>
#include <dev/isa/isareg.h>

/*
 * Macros for interrupt entry, call to handler, and exit.
 *
 * XXX
 * The interrupt frame is set up to look like a trap frame.  This may be a
 * waste.  The only handler which needs a frame is the clock handler, and it
 * only needs a few bits.  Xdoreti() needs a trap frame for handling ASTs, but
 * it could easily convert the frame on demand.
 *
 * The direct costs of setting up a trap frame are two pushl's (error code and
 * trap number), an addl to get rid of these, and pushing and popping the
 * callee-saved registers %esi, %edi, %ebx, and %ebp twice.
 *
 * If the interrupt frame is made more flexible,  INTR can push %eax first and
 * decide the ipending case with less overhead, e.g., by avoiding loading the
 * segment registers.
 */

        .globl  isa_strayintr

#define voidop(num)

/*
 * Normal vectors.
 *
 * We cdr down the intrhand chain, calling each handler with its appropriate
 * argument (0 meaning a pointer to the frame, for clock interrupts).
 *
 * The handler returns one of three values:
 *   0 - This interrupt wasn't for me.
 *   1 - This interrupt was for me.
 *  -1 - This interrupt might have been for me, but I don't know.
 * If there are no handlers, or they all return 0, we flag it as a `stray'
 * interrupt.  On a system with level-triggered interrupts, we could terminate
 * immediately when one of them returns 1; but this is a PC.
 *
 * On exit, we jump to Xdoreti(), to process soft interrupts and ASTs.
 */
#define INTRSTUB(name, num, early_ack, late_ack, mask, unmask, level_mask) \
KIDTVEC(resume_##name##num)                                             ;\
        push    %ebx                                                    ;\
        cli                                                             ;\
        jmp     1f                                                      ;\
KIDTVEC(recurse_##name##num)                                            ;\
        pushfl                                                          ;\
        pushl   %cs                                                     ;\
        pushl   %esi                                                    ;\
        subl    $8,%esp                 /* space for tf_{err,trapno} */ ;\
        movl    %ebx,%esi                                               ;\
        INTRENTRY(recurse_##name##num)                                  ;\
        push    %esi                                                    ;\
        cli                                                             ;\
        jmp     1f                                                      ;\
IDTVEC(intr_##name##num)                                                ;\
        subl    $8,%esp                 /* space for tf_{err,trapno} */ ;\
        INTRENTRY(intr_##name##num)                                     ;\
        mask(num)                       /* mask it in hardware */       ;\
        early_ack(num)                  /* and allow other intrs */     ;\
        incl    uvmexp+V_INTR           /* statistical info */          ;\
        movl    iminlevel + (num) * 4, %eax                             ;\
        movl    CPL,%ebx                                                ;\
        cmpl    %eax,%ebx                                               ;\
        jae     Xhold_##name##num/* currently masked; hold it */;\
        pushl   %ebx                    /* cpl to restore on exit */    ;\
1:                                                                      ;\
        movl    imaxlevel + (num) * 4,%eax                              ;\
        movl    %eax,CPL                /* block enough for this irq */ ;\
        sti                             /* safe to take intrs now */    ;\
        movl    intrhand + (num) * 4,%ebx               /* head of chain */ ;\
        testl   %ebx,%ebx                                               ;\
        jz      Xstray_##name##num              /* no handlers; we're stray */  ;\
        STRAY_INITIALIZE                /* nobody claimed it yet */     ;\
        incl    CPUVAR(IDEPTH)                                          ;\
7:      movl    %esp, %eax              /* save frame pointer in eax */ ;\
        pushl   %ebx                    /* arg 2: ih structure */       ;\
        pushl   %eax                    /* arg 1: frame pointer */      ;\
        call    intr_handler            /* call it */                   ;\
        addl    $8, %esp                /* toss args */                 ;\
        STRAY_INTEGRATE                 /* maybe he claimed it */       ;\
        orl     %eax,%eax               /* should it be counted? */     ;\
        jz      5f                      /* no, skip it */               ;\
        addl    $1,IH_COUNT(%ebx)       /* count the intrs */           ;\
        adcl    $0,IH_COUNT+4(%ebx)                                     ;\
        cmpl    $0,intr_shared_edge                                     ;\
        jne     5f                       /* if no shared edges ... */   ;\
        orl     %eax,%eax               /* ... 1 means stop trying */   ;\
        jns     8f                                                      ;\
5:      movl    IH_NEXT(%ebx),%ebx      /* next handler in chain */     ;\
        testl   %ebx,%ebx                                               ;\
        jnz     7b                                                      ;\
8:      decl    CPUVAR(IDEPTH)                                          ;\
        STRAY_TEST(name,num)            /* see if it's a stray */       ;\
6:      unmask(num)                     /* unmask it in hardware */     ;\
        late_ack(num)                                                   ;\
        jmp     Xdoreti                 /* lower spl and do ASTs */     ;\
KIDTVEC(stray_##name##num)                                              ;\
        pushl   $num                                                    ;\
        call    isa_strayintr                                           ;\
        addl    $4,%esp                                                 ;\
        jmp     6b                                                      ;\
KIDTVEC(hold_##name##num)                                               ;\
        orb     $IRQ_BIT(num),CPUVAR(IPENDING) + IRQ_BYTE(num)  ;\
        CLIDEBUG                                                        ;\
        INTRFASTEXIT

#if defined(DIAGNOSTIC)
#define CLIDEBUG \
        movl    $0xfa,%esi
#else
#define CLIDEBUG
#endif

#if defined(DEBUG)
#define STRAY_INITIALIZE \
        xorl    %esi,%esi
#define STRAY_INTEGRATE \
        orl     %eax,%esi
#define STRAY_TEST(name,num) \
        testl   %esi,%esi                                               ;\
        jz      Xstray_##name##num
#else /* !DEBUG */
#define STRAY_INITIALIZE
#define STRAY_INTEGRATE
#define STRAY_TEST(name,num)
#endif /* DEBUG */

#define ICUADDR IO_ICU1

INTRSTUB(legacy,0, i8259_asm_ack1, voidop, i8259_asm_mask, i8259_asm_unmask,
    voidop)
INTRSTUB(legacy,1, i8259_asm_ack1, voidop, i8259_asm_mask, i8259_asm_unmask,
    voidop)
INTRSTUB(legacy,2, i8259_asm_ack1, voidop, i8259_asm_mask, i8259_asm_unmask,
    voidop)
INTRSTUB(legacy,3, i8259_asm_ack1, voidop, i8259_asm_mask, i8259_asm_unmask,
    voidop)
INTRSTUB(legacy,4, i8259_asm_ack1, voidop, i8259_asm_mask, i8259_asm_unmask,
    voidop)
INTRSTUB(legacy,5, i8259_asm_ack1, voidop, i8259_asm_mask, i8259_asm_unmask,
    voidop)
INTRSTUB(legacy,6, i8259_asm_ack1, voidop, i8259_asm_mask, i8259_asm_unmask,
    voidop)
INTRSTUB(legacy,7, i8259_asm_ack1, voidop, i8259_asm_mask, i8259_asm_unmask,
    voidop)

#undef ICUADDR
#define ICUADDR IO_ICU2

INTRSTUB(legacy,8, i8259_asm_ack2, voidop, i8259_asm_mask, i8259_asm_unmask,
    voidop)
INTRSTUB(legacy,9, i8259_asm_ack2, voidop, i8259_asm_mask, i8259_asm_unmask,
    voidop)
INTRSTUB(legacy,10, i8259_asm_ack2, voidop, i8259_asm_mask, i8259_asm_unmask,
    voidop)
INTRSTUB(legacy,11, i8259_asm_ack2, voidop, i8259_asm_mask, i8259_asm_unmask,
    voidop)
INTRSTUB(legacy,12, i8259_asm_ack2, voidop, i8259_asm_mask, i8259_asm_unmask,
    voidop)
INTRSTUB(legacy,13, i8259_asm_ack2, voidop, i8259_asm_mask, i8259_asm_unmask,
    voidop)
INTRSTUB(legacy,14, i8259_asm_ack2, voidop, i8259_asm_mask, i8259_asm_unmask,
    voidop)
INTRSTUB(legacy,15, i8259_asm_ack2, voidop, i8259_asm_mask, i8259_asm_unmask,
    voidop)

        .section .rodata
/*
 * These tables are used by the ISA configuration code.
 */
/* interrupt service routine entry points */
IDTVEC(intr)
        .long   Xintr_legacy0, Xintr_legacy1
        .long   Xintr_legacy2, Xintr_legacy3
        .long   Xintr_legacy4, Xintr_legacy5
        .long   Xintr_legacy6, Xintr_legacy7
        .long   Xintr_legacy8, Xintr_legacy9
        .long   Xintr_legacy10, Xintr_legacy11
        .long   Xintr_legacy12, Xintr_legacy13
        .long   Xintr_legacy14, Xintr_legacy15

/*
 * These tables are used by Xdoreti() and Xspllower().
 */
/* resume points for suspended interrupts */
IDTVEC(resume)
        .long   Xresume_legacy0, Xresume_legacy1
        .long   Xresume_legacy2, Xresume_legacy3
        .long   Xresume_legacy4, Xresume_legacy5
        .long   Xresume_legacy6, Xresume_legacy7
        .long   Xresume_legacy8, Xresume_legacy9
        .long   Xresume_legacy10, Xresume_legacy11
        .long   Xresume_legacy12, Xresume_legacy13
        .long   Xresume_legacy14, Xresume_legacy15
        /* for soft interrupts */
        .long   0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
        .long   Xsofttty, Xsoftnet, Xsoftclock
        .long   0, 0
/* fake interrupts to resume from splx() */
IDTVEC(recurse)
        .long   Xrecurse_legacy0, Xrecurse_legacy1
        .long   Xrecurse_legacy2, Xrecurse_legacy3
        .long   Xrecurse_legacy4, Xrecurse_legacy5
        .long   Xrecurse_legacy6, Xrecurse_legacy7
        .long   Xrecurse_legacy8, Xrecurse_legacy9
        .long   Xrecurse_legacy10, Xrecurse_legacy11
        .long   Xrecurse_legacy12, Xrecurse_legacy13
        .long   Xrecurse_legacy14, Xrecurse_legacy15
        /* for soft interrupts */
        .long   0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
        .long   Xsofttty, Xsoftnet, Xsoftclock
        .long   0, 0