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

x86, trampoline: Common infrastructure for low memory trampolines

Common infrastructure for low memory trampolines. This code installs
the trampolines permanently in low memory very early. It also permits
multiple pieces of code to be used for this purpose.

This code also introduces a standard infrastructure for computing
symbol addresses in the trampoline code.

The only change to the actual SMP trampolines themselves is that the
64-bit trampoline has been made reusable -- the previous version would
overwrite the code with a status variable; this moves the status
variable to a separate location.

Signed-off-by: H. Peter Anvin <hpa@linux.intel.com>
LKML-Reference: <4D5DFBE4.7090104@intel.com>
Cc: Rafael J. Wysocki <rjw@sisk.pl>
Cc: Matthieu Castet <castet.matthieu@free.fr>
Cc: Stephen Rothwell <sfr@canb.auug.org.au>

+73 -58
-4
arch/x86/Kconfig
··· 217 217 def_bool y 218 218 depends on SMP 219 219 220 - config X86_TRAMPOLINE 221 - def_bool y 222 - depends on SMP || (64BIT && ACPI_SLEEP) 223 - 224 220 config X86_32_LAZY_GS 225 221 def_bool y 226 222 depends on X86_32 && !CC_STACKPROTECTOR
+1 -2
arch/x86/kernel/Makefile
··· 47 47 obj-y += pci-iommu_table.o 48 48 obj-y += resource.o 49 49 50 - obj-$(CONFIG_X86_TRAMPOLINE) += trampoline.o 50 + obj-y += trampoline.o trampoline_$(BITS).o 51 51 obj-y += process.o 52 52 obj-y += i387.o xsave.o 53 53 obj-y += ptrace.o ··· 69 69 obj-$(CONFIG_SMP) += smpboot.o tsc_sync.o 70 70 obj-$(CONFIG_SMP) += setup_percpu.o 71 71 obj-$(CONFIG_X86_64_SMP) += tsc_sync.o 72 - obj-$(CONFIG_X86_TRAMPOLINE) += trampoline_$(BITS).o 73 72 obj-$(CONFIG_X86_MPPARSE) += mpparse.o 74 73 obj-y += apic/ 75 74 obj-$(CONFIG_X86_REBOOTFIXUPS) += reboot_fixups_32.o
-9
arch/x86/kernel/head32.c
··· 34 34 { 35 35 memblock_init(); 36 36 37 - #ifdef CONFIG_X86_TRAMPOLINE 38 - /* 39 - * But first pinch a few for the stack/trampoline stuff 40 - * FIXME: Don't need the extra page at 4K, but need to fix 41 - * trampoline before removing it. (see the GDT stuff) 42 - */ 43 - memblock_x86_reserve_range(PAGE_SIZE, PAGE_SIZE + PAGE_SIZE, "EX TRAMPOLINE"); 44 - #endif 45 - 46 37 memblock_x86_reserve_range(__pa_symbol(&_text), __pa_symbol(&__bss_stop), "TEXT DATA BSS"); 47 38 48 39 #ifdef CONFIG_BLK_DEV_INITRD
+1 -2
arch/x86/kernel/head_64.S
··· 136 136 /* Fixup phys_base */ 137 137 addq %rbp, phys_base(%rip) 138 138 139 - #ifdef CONFIG_X86_TRAMPOLINE 139 + /* Fixup trampoline */ 140 140 addq %rbp, trampoline_level4_pgt + 0(%rip) 141 141 addq %rbp, trampoline_level4_pgt + (511*8)(%rip) 142 - #endif 143 142 144 143 /* Due to ENTRY(), sometimes the empty space gets filled with 145 144 * zeros. Better take a jmp than relying on empty space being
+1 -1
arch/x86/kernel/setup.c
··· 935 935 printk(KERN_DEBUG "initial memory mapped : 0 - %08lx\n", 936 936 max_pfn_mapped<<PAGE_SHIFT); 937 937 938 - reserve_trampoline_memory(); 938 + setup_trampolines(); 939 939 940 940 #ifdef CONFIG_ACPI_SLEEP 941 941 /*
+6 -4
arch/x86/kernel/smpboot.c
··· 788 788 stack_start = c_idle.idle->thread.sp; 789 789 790 790 /* start_ip had better be page-aligned! */ 791 - start_ip = setup_trampoline(); 791 + start_ip = trampoline_address(); 792 792 793 793 /* So we see what's up */ 794 794 announce_cpu(cpu, apicid); ··· 797 797 * This grunge runs the startup process for 798 798 * the targeted processor. 799 799 */ 800 + 801 + printk(KERN_DEBUG "smpboot cpu %d: start_ip = %lx\n", cpu, start_ip); 800 802 801 803 atomic_set(&init_deasserted, 0); 802 804 ··· 853 851 pr_debug("CPU%d: has booted.\n", cpu); 854 852 else { 855 853 boot_error = 1; 856 - if (*((volatile unsigned char *)trampoline_base) 857 - == 0xA5) 854 + if (*(volatile u32 *)TRAMPOLINE_SYM(trampoline_status) 855 + == 0xA5A5A5A5) 858 856 /* trampoline started but...? */ 859 857 pr_err("CPU%d: Stuck ??\n", cpu); 860 858 else ··· 880 878 } 881 879 882 880 /* mark "stuck" area as not stuck */ 883 - *((volatile unsigned long *)trampoline_base) = 0; 881 + *(volatile u32 *)TRAMPOLINE_SYM(trampoline_status) = 0; 884 882 885 883 if (get_uv_system_type() != UV_NON_UNIQUE_APIC) { 886 884 /*
+22 -20
arch/x86/kernel/trampoline.c
··· 2 2 #include <linux/memblock.h> 3 3 4 4 #include <asm/trampoline.h> 5 + #include <asm/cacheflush.h> 5 6 #include <asm/pgtable.h> 6 7 7 - #if defined(CONFIG_X86_64) && defined(CONFIG_ACPI_SLEEP) 8 - #define __trampinit 9 - #define __trampinitdata 10 - #else 11 - #define __trampinit __cpuinit 12 - #define __trampinitdata __cpuinitdata 13 - #endif 8 + unsigned char *x86_trampoline_base; 14 9 15 - /* ready for x86_64 and x86 */ 16 - unsigned char *__trampinitdata trampoline_base; 17 - 18 - void __init reserve_trampoline_memory(void) 10 + void __init setup_trampolines(void) 19 11 { 20 12 phys_addr_t mem; 13 + size_t size = PAGE_ALIGN(x86_trampoline_end - x86_trampoline_start); 21 14 22 15 /* Has to be in very low memory so we can execute real-mode AP code. */ 23 - mem = memblock_find_in_range(0, 1<<20, TRAMPOLINE_SIZE, PAGE_SIZE); 16 + mem = memblock_find_in_range(0, 1<<20, size, PAGE_SIZE); 24 17 if (mem == MEMBLOCK_ERROR) 25 18 panic("Cannot allocate trampoline\n"); 26 19 27 - trampoline_base = __va(mem); 28 - memblock_x86_reserve_range(mem, mem + TRAMPOLINE_SIZE, "TRAMPOLINE"); 20 + x86_trampoline_base = __va(mem); 21 + memblock_x86_reserve_range(mem, mem + size, "TRAMPOLINE"); 22 + 23 + printk(KERN_DEBUG "Base memory trampoline at [%p] %llx size %zu\n", 24 + x86_trampoline_base, (unsigned long long)mem, size); 25 + 26 + memcpy(x86_trampoline_base, x86_trampoline_start, size); 29 27 } 30 28 31 29 /* 32 - * Currently trivial. Write the real->protected mode 33 - * bootstrap into the page concerned. The caller 34 - * has made sure it's suitably aligned. 30 + * setup_trampolines() gets called very early, to guarantee the 31 + * availability of low memory. This is before the proper kernel page 32 + * tables are set up, so we cannot set page permissions in that 33 + * function. Thus, we use an arch_initcall instead. 35 34 */ 36 - unsigned long __trampinit setup_trampoline(void) 35 + static int __init configure_trampolines(void) 37 36 { 38 - memcpy(trampoline_base, trampoline_data, TRAMPOLINE_SIZE); 39 - return virt_to_phys(trampoline_base); 37 + size_t size = PAGE_ALIGN(x86_trampoline_end - x86_trampoline_start); 38 + 39 + set_memory_x((unsigned long)x86_trampoline_base, size >> PAGE_SHIFT); 40 + return 0; 40 41 } 42 + arch_initcall(configure_trampolines);
+11 -4
arch/x86/kernel/trampoline_32.S
··· 32 32 #include <asm/segment.h> 33 33 #include <asm/page_types.h> 34 34 35 - /* We can free up trampoline after bootup if cpu hotplug is not supported. */ 36 - __CPUINITRODATA 37 - .code16 35 + #ifdef CONFIG_SMP 36 + 37 + .section ".x86_trampoline","a" 38 + .balign PAGE_SIZE 39 + .code16 38 40 39 41 ENTRY(trampoline_data) 40 42 r_base = . ··· 46 44 47 45 cli # We should be safe anyway 48 46 49 - movl $0xA5A5A5A5, trampoline_data - r_base 47 + movl $0xA5A5A5A5, trampoline_status - r_base 50 48 # write marker for master knows we're running 51 49 52 50 /* GDT tables in non default location kernel can be beyond 16MB and ··· 74 72 .word 0 # idt limit = 0 75 73 .long 0 # idt base = 0L 76 74 75 + ENTRY(trampoline_status) 76 + .long 0 77 + 77 78 .globl trampoline_end 78 79 trampoline_end: 80 + 81 + #endif /* CONFIG_SMP */
+18 -12
arch/x86/kernel/trampoline_64.S
··· 32 32 #include <asm/segment.h> 33 33 #include <asm/processor-flags.h> 34 34 35 - #ifdef CONFIG_ACPI_SLEEP 36 - .section .rodata, "a", @progbits 37 - #else 38 - /* We can free up the trampoline after bootup if cpu hotplug is not supported. */ 39 - __CPUINITRODATA 40 - #endif 41 - .code16 35 + .section ".x86_trampoline","a" 36 + .balign PAGE_SIZE 37 + .code16 42 38 43 39 ENTRY(trampoline_data) 44 40 r_base = . ··· 46 50 mov %ax, %ss 47 51 48 52 49 - movl $0xA5A5A5A5, trampoline_data - r_base 53 + movl $0xA5A5A5A5, trampoline_status - r_base 50 54 # write marker for master knows we're running 51 55 52 56 # Setup stack ··· 60 64 movzx %ax, %esi # Find the 32bit trampoline location 61 65 shll $4, %esi 62 66 63 - # Fixup the vectors 64 - addl %esi, startup_32_vector - r_base 65 - addl %esi, startup_64_vector - r_base 66 - addl %esi, tgdt + 2 - r_base # Fixup the gdt pointer 67 + # Fixup the absolute vectors 68 + leal (startup_32 - r_base)(%esi), %eax 69 + movl %eax, startup_32_vector - r_base 70 + leal (startup_64 - r_base)(%esi), %eax 71 + movl %eax, startup_64_vector - r_base 72 + leal (tgdt - r_base)(%esi), %eax 73 + movl %eax, (tgdt + 2 - r_base) 67 74 68 75 /* 69 76 * GDT tables in non default location kernel can be beyond 16MB and ··· 128 129 jmp no_longmode 129 130 #include "verify_cpu.S" 130 131 132 + .balign 4 131 133 # Careful these need to be in the same 64K segment as the above; 132 134 tidt: 133 135 .word 0 # idt limit = 0 ··· 155 155 startup_64_vector: 156 156 .long startup_64 - r_base 157 157 .word __KERNEL_CS, 0 158 + 159 + .balign 4 160 + fixup_base: 161 + .long 0 162 + ENTRY(trampoline_status) 163 + .long 0 158 164 159 165 trampoline_stack: 160 166 .org 0x1000
+13
arch/x86/kernel/vmlinux.lds.S
··· 240 240 241 241 INIT_DATA_SECTION(16) 242 242 243 + /* 244 + * Code and data for a variety of lowlevel trampolines, to be 245 + * copied into base memory (< 1 MiB) during initialization. 246 + * Since it is copied early, the main copy can be discarded 247 + * afterwards. 248 + */ 249 + .x86_trampoline : AT(ADDR(.x86_trampoline) - LOAD_OFFSET) { 250 + x86_trampoline_start = .; 251 + *(.x86_trampoline) 252 + x86_trampoline_end = .; 253 + } 254 + 243 255 .x86_cpu_dev.init : AT(ADDR(.x86_cpu_dev.init) - LOAD_OFFSET) { 244 256 __x86_cpu_dev_start = .; 245 257 *(.x86_cpu_dev.init) ··· 303 291 *(.iommu_table) 304 292 __iommu_table_end = .; 305 293 } 294 + 306 295 . = ALIGN(8); 307 296 /* 308 297 * .exit.text is discard at runtime, not link time, to deal with