diff options
author | Linus Torvalds <torvalds@linux-foundation.org> | 2015-06-05 13:03:48 -0400 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2015-06-05 13:03:48 -0400 |
commit | 51d0f0cb3ae0da0ce5bb0bff8d1305345e6ec7ec (patch) | |
tree | 9f1be28982bca81b19887f47cd85dc7898c6f831 | |
parent | a0e9c6efa585897f00eb22e67887e5a25482b1b8 (diff) | |
parent | 425be5679fd292a3c36cb1fe423086708a99f11a (diff) |
Merge branch 'x86-urgent-for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/tip/tip
Pull x86 fixes from Ingo Molnar:
"Misc fixes:
- early_idt_handlers[] fix that fixes the build with bleeding edge
tooling
- build warning fix on GCC 5.1
- vm86 fix plus self-test to make it harder to break it again"
* 'x86-urgent-for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/tip/tip:
x86/asm/irq: Stop relying on magic JMP behavior for early_idt_handlers
x86/asm/entry/32, selftests: Add a selftest for kernel entries from VM86 mode
x86/boot: Add CONFIG_PARAVIRT_SPINLOCKS quirk to arch/x86/boot/compressed/misc.h
x86/asm/entry/32: Really make user_mode() work correctly for VM86 mode
-rw-r--r-- | arch/x86/boot/compressed/misc.h | 11 | ||||
-rw-r--r-- | arch/x86/include/asm/ptrace.h | 2 | ||||
-rw-r--r-- | arch/x86/include/asm/segment.h | 14 | ||||
-rw-r--r-- | arch/x86/kernel/head64.c | 2 | ||||
-rw-r--r-- | arch/x86/kernel/head_32.S | 33 | ||||
-rw-r--r-- | arch/x86/kernel/head_64.S | 20 | ||||
-rw-r--r-- | tools/testing/selftests/x86/Makefile | 6 | ||||
-rw-r--r-- | tools/testing/selftests/x86/entry_from_vm86.c | 114 |
8 files changed, 166 insertions, 36 deletions
diff --git a/arch/x86/boot/compressed/misc.h b/arch/x86/boot/compressed/misc.h index 89dd0d78013a..805d25ca5f1d 100644 --- a/arch/x86/boot/compressed/misc.h +++ b/arch/x86/boot/compressed/misc.h | |||
@@ -2,15 +2,14 @@ | |||
2 | #define BOOT_COMPRESSED_MISC_H | 2 | #define BOOT_COMPRESSED_MISC_H |
3 | 3 | ||
4 | /* | 4 | /* |
5 | * we have to be careful, because no indirections are allowed here, and | 5 | * Special hack: we have to be careful, because no indirections are allowed here, |
6 | * paravirt_ops is a kind of one. As it will only run in baremetal anyway, | 6 | * and paravirt_ops is a kind of one. As it will only run in baremetal anyway, |
7 | * we just keep it from happening | 7 | * we just keep it from happening. (This list needs to be extended when new |
8 | * paravirt and debugging variants are added.) | ||
8 | */ | 9 | */ |
9 | #undef CONFIG_PARAVIRT | 10 | #undef CONFIG_PARAVIRT |
11 | #undef CONFIG_PARAVIRT_SPINLOCKS | ||
10 | #undef CONFIG_KASAN | 12 | #undef CONFIG_KASAN |
11 | #ifdef CONFIG_X86_32 | ||
12 | #define _ASM_X86_DESC_H 1 | ||
13 | #endif | ||
14 | 13 | ||
15 | #include <linux/linkage.h> | 14 | #include <linux/linkage.h> |
16 | #include <linux/screen_info.h> | 15 | #include <linux/screen_info.h> |
diff --git a/arch/x86/include/asm/ptrace.h b/arch/x86/include/asm/ptrace.h index 19507ffa5d28..5fabf1362942 100644 --- a/arch/x86/include/asm/ptrace.h +++ b/arch/x86/include/asm/ptrace.h | |||
@@ -107,7 +107,7 @@ static inline unsigned long regs_return_value(struct pt_regs *regs) | |||
107 | static inline int user_mode(struct pt_regs *regs) | 107 | static inline int user_mode(struct pt_regs *regs) |
108 | { | 108 | { |
109 | #ifdef CONFIG_X86_32 | 109 | #ifdef CONFIG_X86_32 |
110 | return (regs->cs & SEGMENT_RPL_MASK) == USER_RPL; | 110 | return ((regs->cs & SEGMENT_RPL_MASK) | (regs->flags & X86_VM_MASK)) >= USER_RPL; |
111 | #else | 111 | #else |
112 | return !!(regs->cs & 3); | 112 | return !!(regs->cs & 3); |
113 | #endif | 113 | #endif |
diff --git a/arch/x86/include/asm/segment.h b/arch/x86/include/asm/segment.h index 5a9856eb12ba..7d5a1929d76b 100644 --- a/arch/x86/include/asm/segment.h +++ b/arch/x86/include/asm/segment.h | |||
@@ -231,11 +231,21 @@ | |||
231 | #define TLS_SIZE (GDT_ENTRY_TLS_ENTRIES* 8) | 231 | #define TLS_SIZE (GDT_ENTRY_TLS_ENTRIES* 8) |
232 | 232 | ||
233 | #ifdef __KERNEL__ | 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 | #ifndef __ASSEMBLY__ | 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 | #ifdef CONFIG_TRACING | 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 | #endif | 249 | #endif |
240 | 250 | ||
241 | /* | 251 | /* |
diff --git a/arch/x86/kernel/head64.c b/arch/x86/kernel/head64.c index 2b55ee6db053..5a4668136e98 100644 --- a/arch/x86/kernel/head64.c +++ b/arch/x86/kernel/head64.c | |||
@@ -167,7 +167,7 @@ asmlinkage __visible void __init x86_64_start_kernel(char * real_mode_data) | |||
167 | clear_bss(); | 167 | clear_bss(); |
168 | 168 | ||
169 | for (i = 0; i < NUM_EXCEPTION_VECTORS; i++) | 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 | load_idt((const struct desc_ptr *)&idt_descr); | 171 | load_idt((const struct desc_ptr *)&idt_descr); |
172 | 172 | ||
173 | copy_bootdata(__va(real_mode_data)); | 173 | copy_bootdata(__va(real_mode_data)); |
diff --git a/arch/x86/kernel/head_32.S b/arch/x86/kernel/head_32.S index d031bad9e07e..53eeb226657c 100644 --- a/arch/x86/kernel/head_32.S +++ b/arch/x86/kernel/head_32.S | |||
@@ -478,21 +478,22 @@ is486: | |||
478 | __INIT | 478 | __INIT |
479 | setup_once: | 479 | setup_once: |
480 | /* | 480 | /* |
481 | * Set up a idt with 256 entries pointing to ignore_int, | 481 | * Set up a idt with 256 interrupt gates that push zero if there |
482 | * interrupt gates. It doesn't actually load idt - that needs | 482 | * is no error code and then jump to early_idt_handler_common. |
483 | * to be done on each CPU. Interrupts are enabled elsewhere, | 483 | * It doesn't actually load the idt - that needs to be done on |
484 | * when we can be relatively sure everything is ok. | 484 | * each CPU. Interrupts are enabled elsewhere, when we can be |
485 | * relatively sure everything is ok. | ||
485 | */ | 486 | */ |
486 | 487 | ||
487 | movl $idt_table,%edi | 488 | movl $idt_table,%edi |
488 | movl $early_idt_handlers,%eax | 489 | movl $early_idt_handler_array,%eax |
489 | movl $NUM_EXCEPTION_VECTORS,%ecx | 490 | movl $NUM_EXCEPTION_VECTORS,%ecx |
490 | 1: | 491 | 1: |
491 | movl %eax,(%edi) | 492 | movl %eax,(%edi) |
492 | movl %eax,4(%edi) | 493 | movl %eax,4(%edi) |
493 | /* interrupt gate, dpl=0, present */ | 494 | /* interrupt gate, dpl=0, present */ |
494 | movl $(0x8E000000 + __KERNEL_CS),2(%edi) | 495 | movl $(0x8E000000 + __KERNEL_CS),2(%edi) |
495 | addl $9,%eax | 496 | addl $EARLY_IDT_HANDLER_SIZE,%eax |
496 | addl $8,%edi | 497 | addl $8,%edi |
497 | loop 1b | 498 | loop 1b |
498 | 499 | ||
@@ -524,26 +525,28 @@ setup_once: | |||
524 | andl $0,setup_once_ref /* Once is enough, thanks */ | 525 | andl $0,setup_once_ref /* Once is enough, thanks */ |
525 | ret | 526 | ret |
526 | 527 | ||
527 | ENTRY(early_idt_handlers) | 528 | ENTRY(early_idt_handler_array) |
528 | # 36(%esp) %eflags | 529 | # 36(%esp) %eflags |
529 | # 32(%esp) %cs | 530 | # 32(%esp) %cs |
530 | # 28(%esp) %eip | 531 | # 28(%esp) %eip |
531 | # 24(%rsp) error code | 532 | # 24(%rsp) error code |
532 | i = 0 | 533 | i = 0 |
533 | .rept NUM_EXCEPTION_VECTORS | 534 | .rept NUM_EXCEPTION_VECTORS |
534 | .if (EXCEPTION_ERRCODE_MASK >> i) & 1 | 535 | .ifeq (EXCEPTION_ERRCODE_MASK >> i) & 1 |
535 | ASM_NOP2 | ||
536 | .else | ||
537 | pushl $0 # Dummy error code, to make stack frame uniform | 536 | pushl $0 # Dummy error code, to make stack frame uniform |
538 | .endif | 537 | .endif |
539 | pushl $i # 20(%esp) Vector number | 538 | pushl $i # 20(%esp) Vector number |
540 | jmp early_idt_handler | 539 | jmp early_idt_handler_common |
541 | i = i + 1 | 540 | i = i + 1 |
541 | .fill early_idt_handler_array + i*EARLY_IDT_HANDLER_SIZE - ., 1, 0xcc | ||
542 | .endr | 542 | .endr |
543 | ENDPROC(early_idt_handlers) | 543 | ENDPROC(early_idt_handler_array) |
544 | 544 | ||
545 | /* This is global to keep gas from relaxing the jumps */ | 545 | early_idt_handler_common: |
546 | ENTRY(early_idt_handler) | 546 | /* |
547 | * The stack is the hardware frame, an error code or zero, and the | ||
548 | * vector number. | ||
549 | */ | ||
547 | cld | 550 | cld |
548 | 551 | ||
549 | cmpl $2,(%esp) # X86_TRAP_NMI | 552 | cmpl $2,(%esp) # X86_TRAP_NMI |
@@ -603,7 +606,7 @@ ex_entry: | |||
603 | is_nmi: | 606 | is_nmi: |
604 | addl $8,%esp /* drop vector number and error code */ | 607 | addl $8,%esp /* drop vector number and error code */ |
605 | iret | 608 | iret |
606 | ENDPROC(early_idt_handler) | 609 | ENDPROC(early_idt_handler_common) |
607 | 610 | ||
608 | /* This is the default interrupt "handler" :-) */ | 611 | /* This is the default interrupt "handler" :-) */ |
609 | ALIGN | 612 | ALIGN |
diff --git a/arch/x86/kernel/head_64.S b/arch/x86/kernel/head_64.S index ae6588b301c2..df7e78057ae0 100644 --- a/arch/x86/kernel/head_64.S +++ b/arch/x86/kernel/head_64.S | |||
@@ -321,26 +321,28 @@ bad_address: | |||
321 | jmp bad_address | 321 | jmp bad_address |
322 | 322 | ||
323 | __INIT | 323 | __INIT |
324 | .globl early_idt_handlers | 324 | ENTRY(early_idt_handler_array) |
325 | early_idt_handlers: | ||
326 | # 104(%rsp) %rflags | 325 | # 104(%rsp) %rflags |
327 | # 96(%rsp) %cs | 326 | # 96(%rsp) %cs |
328 | # 88(%rsp) %rip | 327 | # 88(%rsp) %rip |
329 | # 80(%rsp) error code | 328 | # 80(%rsp) error code |
330 | i = 0 | 329 | i = 0 |
331 | .rept NUM_EXCEPTION_VECTORS | 330 | .rept NUM_EXCEPTION_VECTORS |
332 | .if (EXCEPTION_ERRCODE_MASK >> i) & 1 | 331 | .ifeq (EXCEPTION_ERRCODE_MASK >> i) & 1 |
333 | ASM_NOP2 | ||
334 | .else | ||
335 | pushq $0 # Dummy error code, to make stack frame uniform | 332 | pushq $0 # Dummy error code, to make stack frame uniform |
336 | .endif | 333 | .endif |
337 | pushq $i # 72(%rsp) Vector number | 334 | pushq $i # 72(%rsp) Vector number |
338 | jmp early_idt_handler | 335 | jmp early_idt_handler_common |
339 | i = i + 1 | 336 | i = i + 1 |
337 | .fill early_idt_handler_array + i*EARLY_IDT_HANDLER_SIZE - ., 1, 0xcc | ||
340 | .endr | 338 | .endr |
339 | ENDPROC(early_idt_handler_array) | ||
341 | 340 | ||
342 | /* This is global to keep gas from relaxing the jumps */ | 341 | early_idt_handler_common: |
343 | ENTRY(early_idt_handler) | 342 | /* |
343 | * The stack is the hardware frame, an error code or zero, and the | ||
344 | * vector number. | ||
345 | */ | ||
344 | cld | 346 | cld |
345 | 347 | ||
346 | cmpl $2,(%rsp) # X86_TRAP_NMI | 348 | cmpl $2,(%rsp) # X86_TRAP_NMI |
@@ -412,7 +414,7 @@ ENTRY(early_idt_handler) | |||
412 | is_nmi: | 414 | is_nmi: |
413 | addq $16,%rsp # drop vector number and error code | 415 | addq $16,%rsp # drop vector number and error code |
414 | INTERRUPT_RETURN | 416 | INTERRUPT_RETURN |
415 | ENDPROC(early_idt_handler) | 417 | ENDPROC(early_idt_handler_common) |
416 | 418 | ||
417 | __INITDATA | 419 | __INITDATA |
418 | 420 | ||
diff --git a/tools/testing/selftests/x86/Makefile b/tools/testing/selftests/x86/Makefile index 5bdb781163d1..9b0d8baf2934 100644 --- a/tools/testing/selftests/x86/Makefile +++ b/tools/testing/selftests/x86/Makefile | |||
@@ -5,8 +5,10 @@ include ../lib.mk | |||
5 | .PHONY: all all_32 all_64 warn_32bit_failure clean | 5 | .PHONY: all all_32 all_64 warn_32bit_failure clean |
6 | 6 | ||
7 | TARGETS_C_BOTHBITS := sigreturn single_step_syscall | 7 | TARGETS_C_BOTHBITS := sigreturn single_step_syscall |
8 | TARGETS_C_32BIT_ONLY := entry_from_vm86 | ||
8 | 9 | ||
9 | BINARIES_32 := $(TARGETS_C_BOTHBITS:%=%_32) | 10 | TARGETS_C_32BIT_ALL := $(TARGETS_C_BOTHBITS) $(TARGETS_C_32BIT_ONLY) |
11 | BINARIES_32 := $(TARGETS_C_32BIT_ALL:%=%_32) | ||
10 | BINARIES_64 := $(TARGETS_C_BOTHBITS:%=%_64) | 12 | BINARIES_64 := $(TARGETS_C_BOTHBITS:%=%_64) |
11 | 13 | ||
12 | CFLAGS := -O2 -g -std=gnu99 -pthread -Wall | 14 | CFLAGS := -O2 -g -std=gnu99 -pthread -Wall |
@@ -32,7 +34,7 @@ all_64: $(BINARIES_64) | |||
32 | clean: | 34 | clean: |
33 | $(RM) $(BINARIES_32) $(BINARIES_64) | 35 | $(RM) $(BINARIES_32) $(BINARIES_64) |
34 | 36 | ||
35 | $(TARGETS_C_BOTHBITS:%=%_32): %_32: %.c | 37 | $(TARGETS_C_32BIT_ALL:%=%_32): %_32: %.c |
36 | $(CC) -m32 -o $@ $(CFLAGS) $(EXTRA_CFLAGS) $^ -lrt -ldl | 38 | $(CC) -m32 -o $@ $(CFLAGS) $(EXTRA_CFLAGS) $^ -lrt -ldl |
37 | 39 | ||
38 | $(TARGETS_C_BOTHBITS:%=%_64): %_64: %.c | 40 | $(TARGETS_C_BOTHBITS:%=%_64): %_64: %.c |
diff --git a/tools/testing/selftests/x86/entry_from_vm86.c b/tools/testing/selftests/x86/entry_from_vm86.c new file mode 100644 index 000000000000..5c38a187677b --- /dev/null +++ b/tools/testing/selftests/x86/entry_from_vm86.c | |||
@@ -0,0 +1,114 @@ | |||
1 | /* | ||
2 | * entry_from_vm86.c - tests kernel entries from vm86 mode | ||
3 | * Copyright (c) 2014-2015 Andrew Lutomirski | ||
4 | * | ||
5 | * This exercises a few paths that need to special-case vm86 mode. | ||
6 | * | ||
7 | * GPL v2. | ||
8 | */ | ||
9 | |||
10 | #define _GNU_SOURCE | ||
11 | |||
12 | #include <assert.h> | ||
13 | #include <stdlib.h> | ||
14 | #include <sys/syscall.h> | ||
15 | #include <sys/signal.h> | ||
16 | #include <sys/ucontext.h> | ||
17 | #include <unistd.h> | ||
18 | #include <stdio.h> | ||
19 | #include <string.h> | ||
20 | #include <inttypes.h> | ||
21 | #include <sys/mman.h> | ||
22 | #include <err.h> | ||
23 | #include <stddef.h> | ||
24 | #include <stdbool.h> | ||
25 | #include <errno.h> | ||
26 | #include <sys/vm86.h> | ||
27 | |||
28 | static unsigned long load_addr = 0x10000; | ||
29 | static int nerrs = 0; | ||
30 | |||
31 | asm ( | ||
32 | ".pushsection .rodata\n\t" | ||
33 | ".type vmcode_bound, @object\n\t" | ||
34 | "vmcode:\n\t" | ||
35 | "vmcode_bound:\n\t" | ||
36 | ".code16\n\t" | ||
37 | "bound %ax, (2048)\n\t" | ||
38 | "int3\n\t" | ||
39 | "vmcode_sysenter:\n\t" | ||
40 | "sysenter\n\t" | ||
41 | ".size vmcode, . - vmcode\n\t" | ||
42 | "end_vmcode:\n\t" | ||
43 | ".code32\n\t" | ||
44 | ".popsection" | ||
45 | ); | ||
46 | |||
47 | extern unsigned char vmcode[], end_vmcode[]; | ||
48 | extern unsigned char vmcode_bound[], vmcode_sysenter[]; | ||
49 | |||
50 | static void do_test(struct vm86plus_struct *v86, unsigned long eip, | ||
51 | const char *text) | ||
52 | { | ||
53 | long ret; | ||
54 | |||
55 | printf("[RUN]\t%s from vm86 mode\n", text); | ||
56 | v86->regs.eip = eip; | ||
57 | ret = vm86(VM86_ENTER, v86); | ||
58 | |||
59 | if (ret == -1 && errno == ENOSYS) { | ||
60 | printf("[SKIP]\tvm86 not supported\n"); | ||
61 | return; | ||
62 | } | ||
63 | |||
64 | if (VM86_TYPE(ret) == VM86_INTx) { | ||
65 | char trapname[32]; | ||
66 | int trapno = VM86_ARG(ret); | ||
67 | if (trapno == 13) | ||
68 | strcpy(trapname, "GP"); | ||
69 | else if (trapno == 5) | ||
70 | strcpy(trapname, "BR"); | ||
71 | else if (trapno == 14) | ||
72 | strcpy(trapname, "PF"); | ||
73 | else | ||
74 | sprintf(trapname, "%d", trapno); | ||
75 | |||
76 | printf("[OK]\tExited vm86 mode due to #%s\n", trapname); | ||
77 | } else if (VM86_TYPE(ret) == VM86_UNKNOWN) { | ||
78 | printf("[OK]\tExited vm86 mode due to unhandled GP fault\n"); | ||
79 | } else { | ||
80 | printf("[OK]\tExited vm86 mode due to type %ld, arg %ld\n", | ||
81 | VM86_TYPE(ret), VM86_ARG(ret)); | ||
82 | } | ||
83 | } | ||
84 | |||
85 | int main(void) | ||
86 | { | ||
87 | struct vm86plus_struct v86; | ||
88 | unsigned char *addr = mmap((void *)load_addr, 4096, | ||
89 | PROT_READ | PROT_WRITE | PROT_EXEC, | ||
90 | MAP_ANONYMOUS | MAP_PRIVATE, -1,0); | ||
91 | if (addr != (unsigned char *)load_addr) | ||
92 | err(1, "mmap"); | ||
93 | |||
94 | memcpy(addr, vmcode, end_vmcode - vmcode); | ||
95 | addr[2048] = 2; | ||
96 | addr[2050] = 3; | ||
97 | |||
98 | memset(&v86, 0, sizeof(v86)); | ||
99 | |||
100 | v86.regs.cs = load_addr / 16; | ||
101 | v86.regs.ss = load_addr / 16; | ||
102 | v86.regs.ds = load_addr / 16; | ||
103 | v86.regs.es = load_addr / 16; | ||
104 | |||
105 | assert((v86.regs.cs & 3) == 0); /* Looks like RPL = 0 */ | ||
106 | |||
107 | /* #BR -- should deliver SIG??? */ | ||
108 | do_test(&v86, vmcode_bound - vmcode, "#BR"); | ||
109 | |||
110 | /* SYSENTER -- should cause #GP or #UD depending on CPU */ | ||
111 | do_test(&v86, vmcode_sysenter - vmcode, "SYSENTER"); | ||
112 | |||
113 | return (nerrs == 0 ? 0 : 1); | ||
114 | } | ||