summaryrefslogtreecommitdiff
path: root/x86_64/interrupt.S
blob: fcd5a03272b39b732b02b83d9c73ffdee4aa9b7f (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
/* 
 * Copyright (c) 1995 Shantanu Goel
 * All Rights Reserved.
 * 
 * Permission to use, copy, modify and distribute this software and its
 * documentation is hereby granted, provided that both the copyright
 * notice and this permission notice appear in all copies of the
 * software, derivative works or modified versions, and any portions
 * thereof, and that both notices appear in supporting documentation.
 * 
 * THE AUTHOR ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS"
 * CONDITION.  THE AUTHOR DISCLAIMS ANY LIABILITY OF ANY KIND FOR
 * ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE.
 */

#include <mach/machine/asm.h>

#include <i386/ipl.h>
#ifdef APIC
# include <i386/apic.h>
#else
# include <i386/pic.h>
#endif
#include <i386/i386asm.h>

#define READ_ISR	(OCW_TEMPLATE|READ_NEXT_RD|READ_IS_ONRD)

/*
 * Generic interrupt handler.
 *
 * On entry, %eax contains the irq number.
 *
 * Note: kdb_kintr needs to know our stack usage
 */

#define S_REGS 24(%rsp)
#define S_RET 16(%rsp)
#define S_IRQ 8(%rsp)
#define S_IPL 0(%rsp)

ENTRY(interrupt)
#ifdef APIC
	cmpl	$255,%eax		/* was this a spurious intr? */
	jne	1f
	ret				/* if so, just return */
1:
#endif
#if NCPUS > 1
	cmpl	$CALL_PMAP_UPDATE,%eax	/* was this a SMP pmap_update request? */
	je	_call_single

	cmpl	$CALL_AST_CHECK,%eax	/* was this a SMP remote -> local ast request? */
	je	_call_local_ast
#endif

	subq	$16,%rsp		/* Two local variables */
	movl	%eax,S_IRQ		/* save irq number */

	call	spl7			/* set ipl */
	movl	%eax,S_IPL		/* save previous ipl */

	movl	S_IRQ,%ecx		/* restore irq number */

#ifndef APIC
	movl	$1,%eax
	shll	%cl,%eax		/* get corresponding IRQ mask */
	orl	EXT(curr_pic_mask),%eax /* add current mask */

	cmpl	$8,%ecx			/* do we need to ack slave? */
	jl	1f			/* no, only master */

	/* EOI on slave */
	movb	%ah,%al
	outb	%al,$(PIC_SLAVE_OCW)	/* mask slave out */

	movb	$(SPECIFIC_EOI),%al	/* specific EOI for this irq */
	andb	$7,%cl			/* irq number for the slave */
	orb	%cl,%al			/* combine them */
	outb	%al,$(PIC_SLAVE_ICW)	/* ack interrupt to slave */

	movb	$(SPECIFIC_EOI + I_AM_SLAVE_2),%al	/* specific master EOI for cascaded slave */
	outb	%al,$(PIC_MASTER_ICW)	/* ack interrupt to master */

	movl	EXT(curr_pic_mask),%eax /* restore original mask */
	movb	%ah,%al
	outb	%al,$(PIC_SLAVE_OCW)	/* unmask slave */
	jmp	2f

1:
	/* EOI on master */
	outb	%al,$(PIC_MASTER_OCW)	/* mask master out */

	movb	$(SPECIFIC_EOI),%al	/* specific EOI for this irq */
	orb	%cl,%al			/* combine with irq number */
	outb	%al,$(PIC_MASTER_ICW)	/* ack interrupt to master */

	movl	EXT(curr_pic_mask),%eax /* restore original mask */
	outb	%al,$(PIC_MASTER_OCW)	/* unmask master */
2:
#else
	cmpl    $16,%ecx		/* was this a low ISA intr? */
	jge      _no_eoi		/* no, must be PCI (let irq_ack handle EOI) */
	movl	%ecx,%edi		/* load irq number as 1st arg */
	call	EXT(ioapic_irq_eoi)	/* ioapic irq specific EOI */
_no_eoi:
#endif

	;
	movq	S_IPL,S_ARG1		/* previous ipl as 2nd arg */

	;
	movq	S_RET,S_ARG2		/* return address as 3th arg */

	;
	movq	S_REGS,S_ARG3		/* address of interrupted registers as 4th arg */

	movl	S_IRQ,%eax		/* copy irq number */
	shll	$2,%eax			/* irq * 4 */
	movl	EXT(iunit)(%rax),%edi	/* get device unit number as 1st arg */

	shll	$1,%eax			/* irq * 8 */
	call	*EXT(ivect)(%rax)	/* call interrupt handler */

	movl	S_IPL,%edi		/* restore previous ipl */
	call	splx_cli		/* restore previous ipl */

	addq	$16,%rsp		/* pop local variables */
	ret

#if NCPUS > 1
_call_single:
	cli                             /* no nested interrupts */
	call	EXT(lapic_eoi)		/* lapic EOI before the handler to allow extra update */
	call	EXT(pmap_update_interrupt)
	ret

_call_local_ast:
	call	EXT(ast_check)		/* AST check on this cpu */
	call	EXT(lapic_eoi)		/* lapic EOI */
	ret
#endif
END(interrupt)