xref: /linux/arch/x86/kernel/idt.c (revision 3318e9744244a415ee9481ca7e54234caf5e12c5)
1d8ed9d48SThomas Gleixner /*
2d8ed9d48SThomas Gleixner  * Interrupt descriptor table related code
3d8ed9d48SThomas Gleixner  *
4d8ed9d48SThomas Gleixner  * This file is licensed under the GPL V2
5d8ed9d48SThomas Gleixner  */
6d8ed9d48SThomas Gleixner #include <linux/interrupt.h>
7d8ed9d48SThomas Gleixner 
8*3318e974SThomas Gleixner #include <asm/traps.h>
9*3318e974SThomas Gleixner #include <asm/proto.h>
10d8ed9d48SThomas Gleixner #include <asm/desc.h>
11d8ed9d48SThomas Gleixner 
12*3318e974SThomas Gleixner struct idt_data {
13*3318e974SThomas Gleixner 	unsigned int	vector;
14*3318e974SThomas Gleixner 	unsigned int	segment;
15*3318e974SThomas Gleixner 	struct idt_bits	bits;
16*3318e974SThomas Gleixner 	const void	*addr;
17*3318e974SThomas Gleixner };
18*3318e974SThomas Gleixner 
19*3318e974SThomas Gleixner #define DPL0		0x0
20*3318e974SThomas Gleixner #define DPL3		0x3
21*3318e974SThomas Gleixner 
22*3318e974SThomas Gleixner #define DEFAULT_STACK	0
23*3318e974SThomas Gleixner 
24*3318e974SThomas Gleixner #define G(_vector, _addr, _ist, _type, _dpl, _segment)	\
25*3318e974SThomas Gleixner 	{						\
26*3318e974SThomas Gleixner 		.vector		= _vector,		\
27*3318e974SThomas Gleixner 		.bits.ist	= _ist,			\
28*3318e974SThomas Gleixner 		.bits.type	= _type,		\
29*3318e974SThomas Gleixner 		.bits.dpl	= _dpl,			\
30*3318e974SThomas Gleixner 		.bits.p		= 1,			\
31*3318e974SThomas Gleixner 		.addr		= _addr,		\
32*3318e974SThomas Gleixner 		.segment	= _segment,		\
33*3318e974SThomas Gleixner 	}
34*3318e974SThomas Gleixner 
35*3318e974SThomas Gleixner /* Interrupt gate */
36*3318e974SThomas Gleixner #define INTG(_vector, _addr)				\
37*3318e974SThomas Gleixner 	G(_vector, _addr, DEFAULT_STACK, GATE_INTERRUPT, DPL0, __KERNEL_CS)
38*3318e974SThomas Gleixner 
39*3318e974SThomas Gleixner /* System interrupt gate */
40*3318e974SThomas Gleixner #define SYSG(_vector, _addr)				\
41*3318e974SThomas Gleixner 	G(_vector, _addr, DEFAULT_STACK, GATE_INTERRUPT, DPL3, __KERNEL_CS)
42*3318e974SThomas Gleixner 
43*3318e974SThomas Gleixner /* Interrupt gate with interrupt stack */
44*3318e974SThomas Gleixner #define ISTG(_vector, _addr, _ist)			\
45*3318e974SThomas Gleixner 	G(_vector, _addr, _ist, GATE_INTERRUPT, DPL0, __KERNEL_CS)
46*3318e974SThomas Gleixner 
47*3318e974SThomas Gleixner /* Task gate */
48*3318e974SThomas Gleixner #define TSKG(_vector, _gdt)				\
49*3318e974SThomas Gleixner 	G(_vector, NULL, DEFAULT_STACK, GATE_TASK, DPL0, _gdt << 3)
50*3318e974SThomas Gleixner 
51d8ed9d48SThomas Gleixner /* Must be page-aligned because the real IDT is used in a fixmap. */
52d8ed9d48SThomas Gleixner gate_desc idt_table[IDT_ENTRIES] __page_aligned_bss;
53d8ed9d48SThomas Gleixner 
5416bc18d8SThomas Gleixner struct desc_ptr idt_descr __ro_after_init = {
5516bc18d8SThomas Gleixner 	.size		= (IDT_ENTRIES * 2 * sizeof(unsigned long)) - 1,
5616bc18d8SThomas Gleixner 	.address	= (unsigned long) idt_table,
5716bc18d8SThomas Gleixner };
5816bc18d8SThomas Gleixner 
59d8ed9d48SThomas Gleixner #ifdef CONFIG_X86_64
60d8ed9d48SThomas Gleixner /* No need to be aligned, but done to keep all IDTs defined the same way. */
61d8ed9d48SThomas Gleixner gate_desc debug_idt_table[IDT_ENTRIES] __page_aligned_bss;
62d8ed9d48SThomas Gleixner 
63d8ed9d48SThomas Gleixner const struct desc_ptr debug_idt_descr = {
64d8ed9d48SThomas Gleixner 	.size		= IDT_ENTRIES * 16 - 1,
65d8ed9d48SThomas Gleixner 	.address	= (unsigned long) debug_idt_table,
66d8ed9d48SThomas Gleixner };
67d8ed9d48SThomas Gleixner #endif
68e802a51eSThomas Gleixner 
69*3318e974SThomas Gleixner static inline void idt_init_desc(gate_desc *gate, const struct idt_data *d)
70*3318e974SThomas Gleixner {
71*3318e974SThomas Gleixner 	unsigned long addr = (unsigned long) d->addr;
72*3318e974SThomas Gleixner 
73*3318e974SThomas Gleixner 	gate->offset_low	= (u16) addr;
74*3318e974SThomas Gleixner 	gate->segment		= (u16) d->segment;
75*3318e974SThomas Gleixner 	gate->bits		= d->bits;
76*3318e974SThomas Gleixner 	gate->offset_middle	= (u16) (addr >> 16);
77*3318e974SThomas Gleixner #ifdef CONFIG_X86_64
78*3318e974SThomas Gleixner 	gate->offset_high	= (u32) (addr >> 32);
79*3318e974SThomas Gleixner 	gate->reserved		= 0;
80*3318e974SThomas Gleixner #endif
81*3318e974SThomas Gleixner }
82*3318e974SThomas Gleixner 
83*3318e974SThomas Gleixner static __init void
84*3318e974SThomas Gleixner idt_setup_from_table(gate_desc *idt, const struct idt_data *t, int size)
85*3318e974SThomas Gleixner {
86*3318e974SThomas Gleixner 	gate_desc desc;
87*3318e974SThomas Gleixner 
88*3318e974SThomas Gleixner 	for (; size > 0; t++, size--) {
89*3318e974SThomas Gleixner 		idt_init_desc(&desc, t);
90*3318e974SThomas Gleixner 		set_bit(t->vector, used_vectors);
91*3318e974SThomas Gleixner 		write_idt_entry(idt, t->vector, &desc);
92*3318e974SThomas Gleixner 	}
93*3318e974SThomas Gleixner }
94*3318e974SThomas Gleixner 
95e802a51eSThomas Gleixner /**
96588787fdSThomas Gleixner  * idt_setup_early_handler - Initializes the idt table with early handlers
97588787fdSThomas Gleixner  */
98588787fdSThomas Gleixner void __init idt_setup_early_handler(void)
99588787fdSThomas Gleixner {
100588787fdSThomas Gleixner 	int i;
101588787fdSThomas Gleixner 
102588787fdSThomas Gleixner 	for (i = 0; i < NUM_EXCEPTION_VECTORS; i++)
103588787fdSThomas Gleixner 		set_intr_gate(i, early_idt_handler_array[i]);
10487e81786SThomas Gleixner #ifdef CONFIG_X86_32
10587e81786SThomas Gleixner 	for ( ; i < NR_VECTORS; i++)
10687e81786SThomas Gleixner 		set_intr_gate(i, early_ignore_irq);
10787e81786SThomas Gleixner #endif
108588787fdSThomas Gleixner 	load_idt(&idt_descr);
109588787fdSThomas Gleixner }
110588787fdSThomas Gleixner 
111588787fdSThomas Gleixner /**
112e802a51eSThomas Gleixner  * idt_invalidate - Invalidate interrupt descriptor table
113e802a51eSThomas Gleixner  * @addr:	The virtual address of the 'invalid' IDT
114e802a51eSThomas Gleixner  */
115e802a51eSThomas Gleixner void idt_invalidate(void *addr)
116e802a51eSThomas Gleixner {
117e802a51eSThomas Gleixner 	struct desc_ptr idt = { .address = (unsigned long) addr, .size = 0 };
118e802a51eSThomas Gleixner 
119e802a51eSThomas Gleixner 	load_idt(&idt);
120e802a51eSThomas Gleixner }
121