x86/IDT: Generate bsp_idt[] at build time

... rather than dynamically at boot time.  Aside from less runtime overhead,
this approach is less fragile than the preexisting autogen stubs mechanism.

We can manage this with some linker calculations.  See patch comments for full
details.

For simplicity, we create a new set of entry stubs here, and clean up the old
ones in the subsequent patch.  bsp_idt[] needs to move from .bss to .data.

No functional change yet; the boot path still (re)writes bsp_idt[] at this
juncture.

Signed-off-by: Andrew Cooper <andrew.cooper3@citrix.com>
Reviewed-by: Jan Beulich <jbeulich@suse.com>
This commit is contained in:
Andrew Cooper committed 2025-03-07 14:34:08 +00:00
1 parent 9c20d3c591
commit 3da2149cf4
5 files changed
+227 -4

No files matched your search

+122
View File
@@ -0,0 +1,122 @@
/* This file is intended to be included multiple times. */
/*
* Generator for IDT entries.
*
* Caller to provide GEN(vector, symbol, dpl, autogen) macro
*
* Symbols are 'entry_0xYY' if there is no better name available. Regular
* handlers set autogen=1, while manual (autogen=0) require the symbol to be
* implemented somewhere else.
*/
#define DPL0 0
#define DPL1 1
#define DPL3 3
#define manual 0
#define autogen 1
#define GEN16(i) \
GEN(0x ## i ## 0, entry_0x ## i ## 0, DPL0, autogen); \
GEN(0x ## i ## 1, entry_0x ## i ## 1, DPL0, autogen); \
GEN(0x ## i ## 2, entry_0x ## i ## 2, DPL0, autogen); \
GEN(0x ## i ## 3, entry_0x ## i ## 3, DPL0, autogen); \
GEN(0x ## i ## 4, entry_0x ## i ## 4, DPL0, autogen); \
GEN(0x ## i ## 5, entry_0x ## i ## 5, DPL0, autogen); \
GEN(0x ## i ## 6, entry_0x ## i ## 6, DPL0, autogen); \
GEN(0x ## i ## 7, entry_0x ## i ## 7, DPL0, autogen); \
GEN(0x ## i ## 8, entry_0x ## i ## 8, DPL0, autogen); \
GEN(0x ## i ## 9, entry_0x ## i ## 9, DPL0, autogen); \
GEN(0x ## i ## a, entry_0x ## i ## a, DPL0, autogen); \
GEN(0x ## i ## b, entry_0x ## i ## b, DPL0, autogen); \
GEN(0x ## i ## c, entry_0x ## i ## c, DPL0, autogen); \
GEN(0x ## i ## d, entry_0x ## i ## d, DPL0, autogen); \
GEN(0x ## i ## e, entry_0x ## i ## e, DPL0, autogen); \
GEN(0x ## i ## f, entry_0x ## i ## f, DPL0, autogen)
GEN(0x00, entry_DE, DPL0, manual);
GEN(0x01, entry_DB, DPL0, manual);
GEN(0x02, entry_NMI, DPL0, manual);
GEN(0x03, entry_BP, DPL3, manual);
GEN(0x04, entry_OF, DPL3, manual);
GEN(0x05, entry_BR, DPL0, manual);
GEN(0x06, entry_UD, DPL0, manual);
GEN(0x07, entry_NM, DPL0, manual);
GEN(0x08, entry_DF, DPL0, manual);
GEN(0x09, entry_0x09, DPL0, autogen); /* Coprocessor Segment Overrun */
GEN(0x0a, entry_TS, DPL0, manual);
GEN(0x0b, entry_NP, DPL0, manual);
GEN(0x0c, entry_SS, DPL0, manual);
GEN(0x0d, entry_GP, DPL0, manual);
GEN(0x0e, early_page_fault, DPL0, manual);
GEN(0x0f, entry_0x0f, DPL0, autogen); /* PIC Spurious Interrupt Vector */
GEN(0x10, entry_MF, DPL0, manual);
GEN(0x11, entry_AC, DPL0, manual);
GEN(0x12, entry_MC, DPL0, manual);
GEN(0x13, entry_XM, DPL0, manual);
GEN(0x14, entry_VE, DPL0, autogen);
GEN(0x15, entry_CP, DPL0, manual);
GEN(0x16, entry_0x16, DPL0, autogen);
GEN(0x17, entry_0x17, DPL0, autogen);
GEN(0x18, entry_0x18, DPL0, autogen);
GEN(0x19, entry_0x19, DPL0, autogen);
GEN(0x1a, entry_0x1a, DPL0, autogen);
GEN(0x1b, entry_0x1b, DPL0, autogen);
GEN(0x1c, entry_HV, DPL0, autogen);
GEN(0x1d, entry_VC, DPL0, autogen);
GEN(0x1e, entry_SX, DPL0, autogen);
GEN(0x1f, entry_0x1f, DPL0, autogen);
GEN16(2);
GEN16(3);
GEN16(4);
GEN16(5);
GEN16(6);
GEN16(7);
#ifdef CONFIG_PV
GEN(0x80, entry_int80, DPL0, manual);
#else
GEN(0x80, entry_0x80, DPL0, autogen);
#endif
GEN(0x81, entry_0x81, DPL0, autogen);
#ifdef CONFIG_PV32
GEN(0x82, entry_int82, DPL1, manual);
#else
GEN(0x82, entry_0x82, DPL0, autogen);
#endif
GEN(0x83, entry_0x83, DPL0, autogen);
GEN(0x84, entry_0x84, DPL0, autogen);
GEN(0x85, entry_0x85, DPL0, autogen);
GEN(0x86, entry_0x86, DPL0, autogen);
GEN(0x87, entry_0x87, DPL0, autogen);
GEN(0x88, entry_0x88, DPL0, autogen);
GEN(0x89, entry_0x89, DPL0, autogen);
GEN(0x8a, entry_0x8a, DPL0, autogen);
GEN(0x8b, entry_0x8b, DPL0, autogen);
GEN(0x8c, entry_0x8c, DPL0, autogen);
GEN(0x8d, entry_0x8d, DPL0, autogen);
GEN(0x8e, entry_0x8e, DPL0, autogen);
GEN(0x8f, entry_0x8f, DPL0, autogen);
GEN16(9);
GEN16(a);
GEN16(b);
GEN16(c);
GEN16(d);
GEN16(e);
GEN16(f);
#undef autogen
#undef manual
#undef DPL3
#undef DPL1
#undef DPL0
#undef GEN16
+27
View File
@@ -0,0 +1,27 @@
/*
* Linker file fragment to help format the IDT correctly
*
* The IDT, having grown compatibly since the 16 bit days, has the entrypoint
* address field split into 3. x86 ELF lacks the @lo/@hi/etc relocation forms
* commonly found in other architectures for accessing a part of a resolved
* symbol address.
*
* However, the linker can perform the necessary calculations and provide them
* under new symbol names. We use this to generate the low and next 16 bits
* of the address for each handler.
*
* The upper 32 bits are always a constant as Xen's .text/data/rodata sits in
* a single aligned 1G range, so do not need calculating in this manner.
*/
#ifndef X86_IDT_GEN_LDS_H
#define X86_IDT_GEN_LDS_H
#define GEN(vec, sym, dpl, auto) \
PROVIDE_HIDDEN(IDT_ ## sym ## _ADDR1 = ABSOLUTE(((sym) & 0xffff))); \
PROVIDE_HIDDEN(IDT_ ## sym ## _ADDR2 = ABSOLUTE((((sym) >> 16) & 0xffff)))
#include <asm/gen-idt.h>
#undef GEN
#endif /* X86_IDT_GEN_LDS_H */
-4
View File
@@ -3,9 +3,5 @@
* Configuration of event handling for all CPUs.
*/
#include <asm/idt.h>
#include <asm/page.h>
idt_entry_t __section(".bss.page_aligned") __aligned(PAGE_SIZE)
bsp_idt[X86_IDT_VECTORS];
DEFINE_PER_CPU_READ_MOSTLY(idt_entry_t *, idt);
+76
View File
@@ -1229,6 +1229,82 @@ FUNC(trap_nop, 0)
iretq
END(trap_nop)
/*
* Automatically generated entrypoints, and IDT
*/
.pushsection .data.page_aligned, "aw", @progbits
DATA(bsp_idt, PAGE_SIZE)
.popsection
/*
* Write an IDT Entry. The linker provides us new _ADDR1/2 symbols calculated
* from \sym.
*/
.macro write_idte sym, dpl
.pushsection .data.page_aligned, "aw", @progbits
.word IDT_\sym\()_ADDR1
.word __HYPERVISOR_CS
.word 0x8e00 | (\dpl << 13) /* Present, DPL, Interrupt Gate */
.word IDT_\sym\()_ADDR2
.long __XEN_VIRT_START >> 32
.long 0
.popsection
.endm
/*
* Write an automatically generated stub. Vectors in the exception range keep
* the stack properly aligned by judging whether the CPU pushed an error code
* or not.
*
* Alignment is forced to 16 because that's the size of the interrupt stub
* with CET active.
*/
.macro gen_entry_stub vec, sym
FUNC(\sym, 16)
ENDBR64
.if \vec < 0x20 /* Exception. */
test $8, %spl /* 64bit exception frames are 16 byte aligned, but the word */
jz 1f /* size is 8 bytes. Check whether the processor gave us an */
pushq $0 /* error code, and insert an empty one if not. */
1: movb $\vec, EFRAME_entry_vector(%rsp)
jmp handle_exception
.else /* Interrupt. */
pushq $0
movb $\vec, EFRAME_entry_vector(%rsp)
jmp common_interrupt
.endif
END(\sym)
.endm
/*
* Generator. Write an entrypoint if necessary, and record an IDT entry.
*/
.macro gen vec, sym, dpl, auto
.if \auto
gen_entry_stub \vec, \sym
.endif
write_idte \sym, \dpl
.endm
#define GEN(v, s, d, a) gen vec=v, sym=s, dpl=d auto=a
#include <asm/gen-idt.h>
#undef GEN
.pushsection .data.page_aligned, "aw", @progbits
END(bsp_idt)
.if . - bsp_idt != PAGE_SIZE
.error "Bad bsp_idt size, should be PAGE_SIZE"
.endif
.popsection
/* Table of automatically generated entry points. One per vector. */
.pushsection .init.rodata, "a", @progbits
DATA(autogen_entrypoints, 8)
+2
View File
@@ -8,6 +8,8 @@
# define DECL_SECTION_WITH_LADDR
#endif
#include <xen/xen.lds.h>
#include <asm/gen-idt.lds.h>
#include <asm/page.h>
#include <asm/trampoline.h>
Reference in new issue
Block a user
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.