/* SPDX-License-Identifier: GPL-2.0-only */ #ifndef X86_ASM_IDT_H #define X86_ASM_IDT_H #include #include #include #define IST_NONE 0 #define IST_MCE 1 #define IST_NMI 2 #define IST_DB 3 #define IST_DF 4 #define IST_MAX 4 typedef union { struct { uint64_t a, b; }; struct { uint16_t addr0; uint16_t cs; uint8_t ist; /* :3, 5 bits rsvd, but this yields far better code. */ uint8_t type:4, s:1, dpl:2, p:1; uint16_t addr1; uint32_t addr2; /* 32 bits rsvd. */ }; } idt_entry_t; extern idt_entry_t bsp_idt[X86_IDT_VECTORS]; DECLARE_PER_CPU(idt_entry_t *, idt); /* * Set the Interrupt Stack Table used by a particular IDT entry. Typically * used on a live IDT, so volatile to disuade clever optimisations. */ static inline void set_ist(volatile idt_entry_t *idt, unsigned int ist) { /* IST is a 3 bit field, 32 bits into the IDT entry. */ ASSERT(ist <= IST_MAX); idt->ist = ist; } static inline void enable_each_ist(idt_entry_t *idt) { set_ist(&idt[X86_EXC_DF], IST_DF); set_ist(&idt[X86_EXC_NMI], IST_NMI); set_ist(&idt[X86_EXC_MC], IST_MCE); set_ist(&idt[X86_EXC_DB], IST_DB); } static inline void disable_each_ist(idt_entry_t *idt) { set_ist(&idt[X86_EXC_DF], IST_NONE); set_ist(&idt[X86_EXC_NMI], IST_NONE); set_ist(&idt[X86_EXC_MC], IST_NONE); set_ist(&idt[X86_EXC_DB], IST_NONE); } /* * Write the lower 64 bits of an IDT Entry. This relies on the upper 32 * bits of the address not changing, which is a safe assumption as all * functions we are likely to load will live inside the 1GB * code/data/bss address range. */ static inline void _write_gate_lower(volatile idt_entry_t *gate, const idt_entry_t *new) { ASSERT(gate->b == new->b); gate->a = new->a; } static inline void _set_gate_lower(idt_entry_t *gate, unsigned long type, unsigned long dpl, void *addr) { idt_entry_t idte; idte.b = gate->b; idte.a = (((unsigned long)(addr) & 0xFFFF0000UL) << 32) | ((unsigned long)(dpl) << 45) | ((unsigned long)(type) << 40) | ((unsigned long)(addr) & 0xFFFFUL) | ((unsigned long)__HYPERVISOR_CS << 16) | (1UL << 47); _write_gate_lower(gate, &idte); } /* * Update the lower half handler of an IDT entry, without changing any other * configuration. */ static inline void _update_gate_addr_lower(idt_entry_t *gate, void *_addr) { unsigned long addr = (unsigned long)_addr; unsigned int addr1 = addr & 0xffff0000U; /* GCC: force better codegen. */ idt_entry_t idte; idte.b = addr >> 32; idte.a = gate->a & 0x0000ffffffff0000UL; idte.a |= (unsigned long)addr1 << 32; idte.a |= addr & 0xffff; _write_gate_lower(gate, &idte); } #endif /* X86_ASM_IDT_H */