Skip to content

Commit 425be56

Browse files
amlutoIngo Molnar
authored and
Ingo Molnar
committed
x86/asm/irq: Stop relying on magic JMP behavior for early_idt_handlers
The early_idt_handlers asm code generates an array of entry points spaced nine bytes apart. It's not really clear from that code or from the places that reference it what's going on, and the code only works in the first place because GAS never generates two-byte JMP instructions when jumping to global labels. Clean up the code to generate the correct array stride (member size) explicitly. This should be considerably more robust against screw-ups, as GAS will warn if a .fill directive has a negative count. Using '. =' to advance would have been even more robust (it would generate an actual error if it tried to move backwards), but it would pad with nulls, confusing anyone who tries to disassemble the code. The new scheme should be much clearer to future readers. While we're at it, improve the comments and rename the array and common code. Binutils may start relaxing jumps to non-weak labels. If so, this change will fix our build, and we may need to backport this change. Before, on x86_64: 0000000000000000 <early_idt_handlers>: 0: 6a 00 pushq $0x0 2: 6a 00 pushq $0x0 4: e9 00 00 00 00 jmpq 9 <early_idt_handlers+0x9> 5: R_X86_64_PC32 early_idt_handler-0x4 ... 48: 66 90 xchg %ax,%ax 4a: 6a 08 pushq $0x8 4c: e9 00 00 00 00 jmpq 51 <early_idt_handlers+0x51> 4d: R_X86_64_PC32 early_idt_handler-0x4 ... 117: 6a 00 pushq $0x0 119: 6a 1f pushq $0x1f 11b: e9 00 00 00 00 jmpq 120 <early_idt_handler> 11c: R_X86_64_PC32 early_idt_handler-0x4 After: 0000000000000000 <early_idt_handler_array>: 0: 6a 00 pushq $0x0 2: 6a 00 pushq $0x0 4: e9 14 01 00 00 jmpq 11d <early_idt_handler_common> ... 48: 6a 08 pushq $0x8 4a: e9 d1 00 00 00 jmpq 120 <early_idt_handler_common> 4f: cc int3 50: cc int3 ... 117: 6a 00 pushq $0x0 119: 6a 1f pushq $0x1f 11b: eb 03 jmp 120 <early_idt_handler_common> 11d: cc int3 11e: cc int3 11f: cc int3 Signed-off-by: Andy Lutomirski <luto@kernel.org> Acked-by: H. Peter Anvin <hpa@linux.intel.com> Cc: Binutils <binutils@sourceware.org> Cc: Borislav Petkov <bp@alien8.de> Cc: H.J. Lu <hjl.tools@gmail.com> Cc: Jan Beulich <JBeulich@suse.com> Cc: Linus Torvalds <torvalds@linux-foundation.org> Cc: Peter Zijlstra <peterz@infradead.org> Cc: Thomas Gleixner <tglx@linutronix.de> Cc: <stable@vger.kernel.org> Link: http://lkml.kernel.org/r/ac027962af343b0c599cbfcf50b945ad2ef3d7a8.1432336324.git.luto@kernel.org Signed-off-by: Ingo Molnar <mingo@kernel.org>
1 parent c2affbf commit 425be56

File tree

4 files changed

+42
-27
lines changed

4 files changed

+42
-27
lines changed

arch/x86/include/asm/segment.h

+12-2
Original file line numberDiff line numberDiff line change
@@ -231,11 +231,21 @@
231231
#define TLS_SIZE (GDT_ENTRY_TLS_ENTRIES* 8)
232232

233233
#ifdef __KERNEL__
234+
235+
/*
236+
* early_idt_handler_array is an array of entry points referenced in the
237+
* early IDT. For simplicity, it's a real array with one entry point
238+
* every nine bytes. That leaves room for an optional 'push $0' if the
239+
* vector has no error code (two bytes), a 'push $vector_number' (two
240+
* bytes), and a jump to the common entry code (up to five bytes).
241+
*/
242+
#define EARLY_IDT_HANDLER_SIZE 9
243+
234244
#ifndef __ASSEMBLY__
235245

236-
extern const char early_idt_handlers[NUM_EXCEPTION_VECTORS][2+2+5];
246+
extern const char early_idt_handler_array[NUM_EXCEPTION_VECTORS][EARLY_IDT_HANDLER_SIZE];
237247
#ifdef CONFIG_TRACING
238-
# define trace_early_idt_handlers early_idt_handlers
248+
# define trace_early_idt_handler_array early_idt_handler_array
239249
#endif
240250

241251
/*

arch/x86/kernel/head64.c

+1-1
Original file line numberDiff line numberDiff line change
@@ -167,7 +167,7 @@ asmlinkage __visible void __init x86_64_start_kernel(char * real_mode_data)
167167
clear_bss();
168168

169169
for (i = 0; i < NUM_EXCEPTION_VECTORS; i++)
170-
set_intr_gate(i, early_idt_handlers[i]);
170+
set_intr_gate(i, early_idt_handler_array[i]);
171171
load_idt((const struct desc_ptr *)&idt_descr);
172172

173173
copy_bootdata(__va(real_mode_data));

arch/x86/kernel/head_32.S

+18-15
Original file line numberDiff line numberDiff line change
@@ -478,21 +478,22 @@ is486:
478478
__INIT
479479
setup_once:
480480
/*
481-
* Set up a idt with 256 entries pointing to ignore_int,
482-
* interrupt gates. It doesn't actually load idt - that needs
483-
* to be done on each CPU. Interrupts are enabled elsewhere,
484-
* when we can be relatively sure everything is ok.
481+
* Set up a idt with 256 interrupt gates that push zero if there
482+
* is no error code and then jump to early_idt_handler_common.
483+
* It doesn't actually load the idt - that needs to be done on
484+
* each CPU. Interrupts are enabled elsewhere, when we can be
485+
* relatively sure everything is ok.
485486
*/
486487

487488
movl $idt_table,%edi
488-
movl $early_idt_handlers,%eax
489+
movl $early_idt_handler_array,%eax
489490
movl $NUM_EXCEPTION_VECTORS,%ecx
490491
1:
491492
movl %eax,(%edi)
492493
movl %eax,4(%edi)
493494
/* interrupt gate, dpl=0, present */
494495
movl $(0x8E000000 + __KERNEL_CS),2(%edi)
495-
addl $9,%eax
496+
addl $EARLY_IDT_HANDLER_SIZE,%eax
496497
addl $8,%edi
497498
loop 1b
498499

@@ -524,26 +525,28 @@ setup_once:
524525
andl $0,setup_once_ref /* Once is enough, thanks */
525526
ret
526527

527-
ENTRY(early_idt_handlers)
528+
ENTRY(early_idt_handler_array)
528529
# 36(%esp) %eflags
529530
# 32(%esp) %cs
530531
# 28(%esp) %eip
531532
# 24(%rsp) error code
532533
i = 0
533534
.rept NUM_EXCEPTION_VECTORS
534-
.if (EXCEPTION_ERRCODE_MASK >> i) & 1
535-
ASM_NOP2
536-
.else
535+
.ifeq (EXCEPTION_ERRCODE_MASK >> i) & 1
537536
pushl $0 # Dummy error code, to make stack frame uniform
538537
.endif
539538
pushl $i # 20(%esp) Vector number
540-
jmp early_idt_handler
539+
jmp early_idt_handler_common
541540
i = i + 1
541+
.fill early_idt_handler_array + i*EARLY_IDT_HANDLER_SIZE - ., 1, 0xcc
542542
.endr
543-
ENDPROC(early_idt_handlers)
543+
ENDPROC(early_idt_handler_array)
544544

545-
/* This is global to keep gas from relaxing the jumps */
546-
ENTRY(early_idt_handler)
545+
early_idt_handler_common:
546+
/*
547+
* The stack is the hardware frame, an error code or zero, and the
548+
* vector number.
549+
*/
547550
cld
548551

549552
cmpl $2,(%esp) # X86_TRAP_NMI
@@ -603,7 +606,7 @@ ex_entry:
603606
is_nmi:
604607
addl $8,%esp /* drop vector number and error code */
605608
iret
606-
ENDPROC(early_idt_handler)
609+
ENDPROC(early_idt_handler_common)
607610

608611
/* This is the default interrupt "handler" :-) */
609612
ALIGN

arch/x86/kernel/head_64.S

+11-9
Original file line numberDiff line numberDiff line change
@@ -321,26 +321,28 @@ bad_address:
321321
jmp bad_address
322322

323323
__INIT
324-
.globl early_idt_handlers
325-
early_idt_handlers:
324+
ENTRY(early_idt_handler_array)
326325
# 104(%rsp) %rflags
327326
# 96(%rsp) %cs
328327
# 88(%rsp) %rip
329328
# 80(%rsp) error code
330329
i = 0
331330
.rept NUM_EXCEPTION_VECTORS
332-
.if (EXCEPTION_ERRCODE_MASK >> i) & 1
333-
ASM_NOP2
334-
.else
331+
.ifeq (EXCEPTION_ERRCODE_MASK >> i) & 1
335332
pushq $0 # Dummy error code, to make stack frame uniform
336333
.endif
337334
pushq $i # 72(%rsp) Vector number
338-
jmp early_idt_handler
335+
jmp early_idt_handler_common
339336
i = i + 1
337+
.fill early_idt_handler_array + i*EARLY_IDT_HANDLER_SIZE - ., 1, 0xcc
340338
.endr
339+
ENDPROC(early_idt_handler_array)
341340

342-
/* This is global to keep gas from relaxing the jumps */
343-
ENTRY(early_idt_handler)
341+
early_idt_handler_common:
342+
/*
343+
* The stack is the hardware frame, an error code or zero, and the
344+
* vector number.
345+
*/
344346
cld
345347

346348
cmpl $2,(%rsp) # X86_TRAP_NMI
@@ -412,7 +414,7 @@ ENTRY(early_idt_handler)
412414
is_nmi:
413415
addq $16,%rsp # drop vector number and error code
414416
INTERRUPT_RETURN
415-
ENDPROC(early_idt_handler)
417+
ENDPROC(early_idt_handler_common)
416418

417419
__INITDATA
418420

0 commit comments

Comments
 (0)