1 /* SPDX-License-Identifier: GPL-2.0-only */
2 #ifndef X86_ASM_IDT_H
3 #define X86_ASM_IDT_H
4
5 #include <xen/bug.h>
6 #include <xen/percpu.h>
7
8 #include <asm/x86-defns.h>
9
10 #define IST_NONE 0
11 #define IST_MCE 1
12 #define IST_NMI 2
13 #define IST_DB 3
14 #define IST_DF 4
15 #define IST_MAX 4
16
17 typedef union {
18 struct {
19 uint64_t a, b;
20 };
21 struct {
22 uint16_t addr0;
23 uint16_t cs;
24 uint8_t ist; /* :3, 5 bits rsvd, but this yields far better code. */
25 uint8_t type:4, s:1, dpl:2, p:1;
26 uint16_t addr1;
27 uint32_t addr2;
28 /* 32 bits rsvd. */
29 };
30 } idt_entry_t;
31
32 extern idt_entry_t bsp_idt[X86_IDT_VECTORS];
33 DECLARE_PER_CPU(idt_entry_t *, idt);
34
35 /*
36 * Set the Interrupt Stack Table used by a particular IDT entry. Typically
37 * used on a live IDT, so volatile to disuade clever optimisations.
38 */
set_ist(volatile idt_entry_t * idt,unsigned int ist)39 static inline void set_ist(volatile idt_entry_t *idt, unsigned int ist)
40 {
41 /* IST is a 3 bit field, 32 bits into the IDT entry. */
42 ASSERT(ist <= IST_MAX);
43
44 idt->ist = ist;
45 }
46
enable_each_ist(idt_entry_t * idt)47 static inline void enable_each_ist(idt_entry_t *idt)
48 {
49 set_ist(&idt[X86_EXC_DF], IST_DF);
50 set_ist(&idt[X86_EXC_NMI], IST_NMI);
51 set_ist(&idt[X86_EXC_MC], IST_MCE);
52 set_ist(&idt[X86_EXC_DB], IST_DB);
53 }
54
disable_each_ist(idt_entry_t * idt)55 static inline void disable_each_ist(idt_entry_t *idt)
56 {
57 set_ist(&idt[X86_EXC_DF], IST_NONE);
58 set_ist(&idt[X86_EXC_NMI], IST_NONE);
59 set_ist(&idt[X86_EXC_MC], IST_NONE);
60 set_ist(&idt[X86_EXC_DB], IST_NONE);
61 }
62
63 /*
64 * Write the lower 64 bits of an IDT Entry. This relies on the upper 32
65 * bits of the address not changing, which is a safe assumption as all
66 * functions we are likely to load will live inside the 1GB
67 * code/data/bss address range.
68 */
_write_gate_lower(volatile idt_entry_t * gate,const idt_entry_t * new)69 static inline void _write_gate_lower(volatile idt_entry_t *gate,
70 const idt_entry_t *new)
71 {
72 ASSERT(gate->b == new->b);
73 gate->a = new->a;
74 }
75
_set_gate_lower(idt_entry_t * gate,unsigned long type,unsigned long dpl,void * addr)76 static inline void _set_gate_lower(idt_entry_t *gate, unsigned long type,
77 unsigned long dpl, void *addr)
78 {
79 idt_entry_t idte;
80 idte.b = gate->b;
81 idte.a =
82 (((unsigned long)(addr) & 0xFFFF0000UL) << 32) |
83 ((unsigned long)(dpl) << 45) |
84 ((unsigned long)(type) << 40) |
85 ((unsigned long)(addr) & 0xFFFFUL) |
86 ((unsigned long)__HYPERVISOR_CS << 16) |
87 (1UL << 47);
88 _write_gate_lower(gate, &idte);
89 }
90
91 /*
92 * Update the lower half handler of an IDT entry, without changing any other
93 * configuration.
94 */
_update_gate_addr_lower(idt_entry_t * gate,void * _addr)95 static inline void _update_gate_addr_lower(idt_entry_t *gate, void *_addr)
96 {
97 unsigned long addr = (unsigned long)_addr;
98 unsigned int addr1 = addr & 0xffff0000U; /* GCC: force better codegen. */
99 idt_entry_t idte;
100
101 idte.b = addr >> 32;
102 idte.a = gate->a & 0x0000ffffffff0000UL;
103 idte.a |= (unsigned long)addr1 << 32;
104 idte.a |= addr & 0xffff;
105
106 _write_gate_lower(gate, &idte);
107 }
108
109 #endif /* X86_ASM_IDT_H */
110