Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

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>

authored by

Andy Lutomirski and committed by
Ingo Molnar
425be567 c2affbf9

+42 -27
+12 -2
arch/x86/include/asm/segment.h
··· 231 231 #define TLS_SIZE (GDT_ENTRY_TLS_ENTRIES* 8) 232 232 233 233 #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 + 234 244 #ifndef __ASSEMBLY__ 235 245 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]; 237 247 #ifdef CONFIG_TRACING 238 - # define trace_early_idt_handlers early_idt_handlers 248 + # define trace_early_idt_handler_array early_idt_handler_array 239 249 #endif 240 250 241 251 /*
+1 -1
arch/x86/kernel/head64.c
··· 167 167 clear_bss(); 168 168 169 169 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]); 171 171 load_idt((const struct desc_ptr *)&idt_descr); 172 172 173 173 copy_bootdata(__va(real_mode_data));
+18 -15
arch/x86/kernel/head_32.S
··· 478 478 __INIT 479 479 setup_once: 480 480 /* 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. 485 486 */ 486 487 487 488 movl $idt_table,%edi 488 - movl $early_idt_handlers,%eax 489 + movl $early_idt_handler_array,%eax 489 490 movl $NUM_EXCEPTION_VECTORS,%ecx 490 491 1: 491 492 movl %eax,(%edi) 492 493 movl %eax,4(%edi) 493 494 /* interrupt gate, dpl=0, present */ 494 495 movl $(0x8E000000 + __KERNEL_CS),2(%edi) 495 - addl $9,%eax 496 + addl $EARLY_IDT_HANDLER_SIZE,%eax 496 497 addl $8,%edi 497 498 loop 1b 498 499 ··· 525 524 andl $0,setup_once_ref /* Once is enough, thanks */ 526 525 ret 527 526 528 - ENTRY(early_idt_handlers) 527 + ENTRY(early_idt_handler_array) 529 528 # 36(%esp) %eflags 530 529 # 32(%esp) %cs 531 530 # 28(%esp) %eip 532 531 # 24(%rsp) error code 533 532 i = 0 534 533 .rept NUM_EXCEPTION_VECTORS 535 - .if (EXCEPTION_ERRCODE_MASK >> i) & 1 536 - ASM_NOP2 537 - .else 534 + .ifeq (EXCEPTION_ERRCODE_MASK >> i) & 1 538 535 pushl $0 # Dummy error code, to make stack frame uniform 539 536 .endif 540 537 pushl $i # 20(%esp) Vector number 541 - jmp early_idt_handler 538 + jmp early_idt_handler_common 542 539 i = i + 1 540 + .fill early_idt_handler_array + i*EARLY_IDT_HANDLER_SIZE - ., 1, 0xcc 543 541 .endr 544 - ENDPROC(early_idt_handlers) 542 + ENDPROC(early_idt_handler_array) 545 543 546 - /* This is global to keep gas from relaxing the jumps */ 547 - ENTRY(early_idt_handler) 544 + early_idt_handler_common: 545 + /* 546 + * The stack is the hardware frame, an error code or zero, and the 547 + * vector number. 548 + */ 548 549 cld 549 550 550 551 cmpl $2,(%esp) # X86_TRAP_NMI ··· 606 603 is_nmi: 607 604 addl $8,%esp /* drop vector number and error code */ 608 605 iret 609 - ENDPROC(early_idt_handler) 606 + ENDPROC(early_idt_handler_common) 610 607 611 608 /* This is the default interrupt "handler" :-) */ 612 609 ALIGN
+11 -9
arch/x86/kernel/head_64.S
··· 321 321 jmp bad_address 322 322 323 323 __INIT 324 - .globl early_idt_handlers 325 - early_idt_handlers: 324 + ENTRY(early_idt_handler_array) 326 325 # 104(%rsp) %rflags 327 326 # 96(%rsp) %cs 328 327 # 88(%rsp) %rip 329 328 # 80(%rsp) error code 330 329 i = 0 331 330 .rept NUM_EXCEPTION_VECTORS 332 - .if (EXCEPTION_ERRCODE_MASK >> i) & 1 333 - ASM_NOP2 334 - .else 331 + .ifeq (EXCEPTION_ERRCODE_MASK >> i) & 1 335 332 pushq $0 # Dummy error code, to make stack frame uniform 336 333 .endif 337 334 pushq $i # 72(%rsp) Vector number 338 - jmp early_idt_handler 335 + jmp early_idt_handler_common 339 336 i = i + 1 337 + .fill early_idt_handler_array + i*EARLY_IDT_HANDLER_SIZE - ., 1, 0xcc 340 338 .endr 339 + ENDPROC(early_idt_handler_array) 341 340 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 + */ 344 346 cld 345 347 346 348 cmpl $2,(%rsp) # X86_TRAP_NMI ··· 414 412 is_nmi: 415 413 addq $16,%rsp # drop vector number and error code 416 414 INTERRUPT_RETURN 417 - ENDPROC(early_idt_handler) 415 + ENDPROC(early_idt_handler_common) 418 416 419 417 __INITDATA 420 418