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