x86, trampoline: Common infrastructure for low memory trampolines
authorH. Peter Anvin <hpa@linux.intel.com>
Mon, 14 Feb 2011 23:34:57 +0000 (15:34 -0800)
committerH. Peter Anvin <hpa@linux.intel.com>
Fri, 18 Feb 2011 05:02:43 +0000 (21:02 -0800)
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>
arch/x86/Kconfig
arch/x86/kernel/Makefile
arch/x86/kernel/head32.c
arch/x86/kernel/head_64.S
arch/x86/kernel/setup.c
arch/x86/kernel/smpboot.c
arch/x86/kernel/trampoline.c
arch/x86/kernel/trampoline_32.S
arch/x86/kernel/trampoline_64.S
arch/x86/kernel/vmlinux.lds.S

index d5ed94d30aadf056130464e5184461654c99ad8a..1359bc9f4fd397a898e3df9eca7073067d715455 100644 (file)
@@ -217,10 +217,6 @@ config X86_HT
        def_bool y
        depends on SMP
 
-config X86_TRAMPOLINE
-       def_bool y
-       depends on SMP || (64BIT && ACPI_SLEEP)
-
 config X86_32_LAZY_GS
        def_bool y
        depends on X86_32 && !CC_STACKPROTECTOR
index 34244b2cd880cff373e744ec34193adb8287ab2a..2e8ce0deae4ae9a29378c0a0a95d357928bf3797 100644 (file)
@@ -47,7 +47,7 @@ obj-y                 += tsc.o io_delay.o rtc.o
 obj-y                  += pci-iommu_table.o
 obj-y                  += resource.o
 
-obj-$(CONFIG_X86_TRAMPOLINE)   += trampoline.o
+obj-y                          += trampoline.o trampoline_$(BITS).o
 obj-y                          += process.o
 obj-y                          += i387.o xsave.o
 obj-y                          += ptrace.o
@@ -69,7 +69,6 @@ obj-$(CONFIG_SMP)             += smp.o
 obj-$(CONFIG_SMP)              += smpboot.o tsc_sync.o
 obj-$(CONFIG_SMP)              += setup_percpu.o
 obj-$(CONFIG_X86_64_SMP)       += tsc_sync.o
-obj-$(CONFIG_X86_TRAMPOLINE)   += trampoline_$(BITS).o
 obj-$(CONFIG_X86_MPPARSE)      += mpparse.o
 obj-y                          += apic/
 obj-$(CONFIG_X86_REBOOTFIXUPS) += reboot_fixups_32.o
index 7f138b3c3c52cf2d6790d8308fdc9a6dae8b41b7..d6d6bb36193126885f4de35964fd6d07c7e3325b 100644 (file)
@@ -34,15 +34,6 @@ void __init i386_start_kernel(void)
 {
        memblock_init();
 
-#ifdef CONFIG_X86_TRAMPOLINE
-       /*
-        * But first pinch a few for the stack/trampoline stuff
-        * FIXME: Don't need the extra page at 4K, but need to fix
-        * trampoline before removing it. (see the GDT stuff)
-        */
-       memblock_x86_reserve_range(PAGE_SIZE, PAGE_SIZE + PAGE_SIZE, "EX TRAMPOLINE");
-#endif
-
        memblock_x86_reserve_range(__pa_symbol(&_text), __pa_symbol(&__bss_stop), "TEXT DATA BSS");
 
 #ifdef CONFIG_BLK_DEV_INITRD
index 239046bd447f9320d92fb0832cac6eeac4335685..e11e39478a4978643559e650844cb9941c876523 100644 (file)
@@ -136,10 +136,9 @@ ident_complete:
        /* Fixup phys_base */
        addq    %rbp, phys_base(%rip)
 
-#ifdef CONFIG_X86_TRAMPOLINE
+       /* Fixup trampoline */
        addq    %rbp, trampoline_level4_pgt + 0(%rip)
        addq    %rbp, trampoline_level4_pgt + (511*8)(%rip)
-#endif
 
        /* Due to ENTRY(), sometimes the empty space gets filled with
         * zeros. Better take a jmp than relying on empty space being
index d3cfe26c0252ab24aaae1481c8c297ac096e6c6d..994ea20e177cb84b80b1e4560327a9c16c97e934 100644 (file)
@@ -935,7 +935,7 @@ void __init setup_arch(char **cmdline_p)
        printk(KERN_DEBUG "initial memory mapped : 0 - %08lx\n",
                        max_pfn_mapped<<PAGE_SHIFT);
 
-       reserve_trampoline_memory();
+       setup_trampolines();
 
 #ifdef CONFIG_ACPI_SLEEP
        /*
index 08776a953487f826dad0acc84b5b6e35f6272387..545273369efa366b19e4087a8c09ac668c2cb536 100644 (file)
@@ -788,7 +788,7 @@ do_rest:
        stack_start  = c_idle.idle->thread.sp;
 
        /* start_ip had better be page-aligned! */
-       start_ip = setup_trampoline();
+       start_ip = trampoline_address();
 
        /* So we see what's up */
        announce_cpu(cpu, apicid);
@@ -798,6 +798,8 @@ do_rest:
         * the targeted processor.
         */
 
+       printk(KERN_DEBUG "smpboot cpu %d: start_ip = %lx\n", cpu, start_ip);
+
        atomic_set(&init_deasserted, 0);
 
        if (get_uv_system_type() != UV_NON_UNIQUE_APIC) {
@@ -851,8 +853,8 @@ do_rest:
                        pr_debug("CPU%d: has booted.\n", cpu);
                else {
                        boot_error = 1;
-                       if (*((volatile unsigned char *)trampoline_base)
-                                       == 0xA5)
+                       if (*(volatile u32 *)TRAMPOLINE_SYM(trampoline_status)
+                           == 0xA5A5A5A5)
                                /* trampoline started but...? */
                                pr_err("CPU%d: Stuck ??\n", cpu);
                        else
@@ -878,7 +880,7 @@ do_rest:
        }
 
        /* mark "stuck" area as not stuck */
-       *((volatile unsigned long *)trampoline_base) = 0;
+       *(volatile u32 *)TRAMPOLINE_SYM(trampoline_status) = 0;
 
        if (get_uv_system_type() != UV_NON_UNIQUE_APIC) {
                /*
index a375616d77f787f692e625b8a2ab70de05cfbb9a..a91ae7709b49ea17628aff1fc253899b4c9e4efa 100644 (file)
@@ -2,39 +2,41 @@
 #include <linux/memblock.h>
 
 #include <asm/trampoline.h>
+#include <asm/cacheflush.h>
 #include <asm/pgtable.h>
 
-#if defined(CONFIG_X86_64) && defined(CONFIG_ACPI_SLEEP)
-#define __trampinit
-#define __trampinitdata
-#else
-#define __trampinit __cpuinit
-#define __trampinitdata __cpuinitdata
-#endif
+unsigned char *x86_trampoline_base;
 
-/* ready for x86_64 and x86 */
-unsigned char *__trampinitdata trampoline_base;
-
-void __init reserve_trampoline_memory(void)
+void __init setup_trampolines(void)
 {
        phys_addr_t mem;
+       size_t size = PAGE_ALIGN(x86_trampoline_end - x86_trampoline_start);
 
        /* Has to be in very low memory so we can execute real-mode AP code. */
-       mem = memblock_find_in_range(0, 1<<20, TRAMPOLINE_SIZE, PAGE_SIZE);
+       mem = memblock_find_in_range(0, 1<<20, size, PAGE_SIZE);
        if (mem == MEMBLOCK_ERROR)
                panic("Cannot allocate trampoline\n");
 
-       trampoline_base = __va(mem);
-       memblock_x86_reserve_range(mem, mem + TRAMPOLINE_SIZE, "TRAMPOLINE");
+       x86_trampoline_base = __va(mem);
+       memblock_x86_reserve_range(mem, mem + size, "TRAMPOLINE");
+
+       printk(KERN_DEBUG "Base memory trampoline at [%p] %llx size %zu\n",
+              x86_trampoline_base, (unsigned long long)mem, size);
+
+       memcpy(x86_trampoline_base, x86_trampoline_start, size);
 }
 
 /*
- * Currently trivial. Write the real->protected mode
- * bootstrap into the page concerned. The caller
- * has made sure it's suitably aligned.
+ * setup_trampolines() gets called very early, to guarantee the
+ * availability of low memory.  This is before the proper kernel page
+ * tables are set up, so we cannot set page permissions in that
+ * function.  Thus, we use an arch_initcall instead.
  */
-unsigned long __trampinit setup_trampoline(void)
+static int __init configure_trampolines(void)
 {
-       memcpy(trampoline_base, trampoline_data, TRAMPOLINE_SIZE);
-       return virt_to_phys(trampoline_base);
+       size_t size = PAGE_ALIGN(x86_trampoline_end - x86_trampoline_start);
+
+       set_memory_x((unsigned long)x86_trampoline_base, size >> PAGE_SHIFT);
+       return 0;
 }
+arch_initcall(configure_trampolines);
index 8508237e8e43634c6ec70c41302922d301cef512..451c0a7ef7fd8c2faa4b0a5d78e30ce538317b17 100644 (file)
 #include <asm/segment.h>
 #include <asm/page_types.h>
 
-/* We can free up trampoline after bootup if cpu hotplug is not supported. */
-__CPUINITRODATA
-.code16
+#ifdef CONFIG_SMP
+
+       .section ".x86_trampoline","a"
+       .balign PAGE_SIZE
+       .code16
 
 ENTRY(trampoline_data)
 r_base = .
@@ -44,7 +46,7 @@ r_base = .
 
        cli                     # We should be safe anyway
 
-       movl    $0xA5A5A5A5, trampoline_data - r_base
+       movl    $0xA5A5A5A5, trampoline_status - r_base
                                # write marker for master knows we're running
 
        /* GDT tables in non default location kernel can be beyond 16MB and
@@ -72,5 +74,10 @@ boot_idt_descr:
        .word   0                               # idt limit = 0
        .long   0                               # idt base = 0L
 
+ENTRY(trampoline_status)
+       .long   0
+
 .globl trampoline_end
 trampoline_end:
+
+#endif /* CONFIG_SMP */
index 075d130efcf9019eb3f4745677e281533335fa11..49c77a682522b83fc4abe9d93115506d32e61ee6 100644 (file)
 #include <asm/segment.h>
 #include <asm/processor-flags.h>
 
-#ifdef CONFIG_ACPI_SLEEP
-.section .rodata, "a", @progbits
-#else
-/* We can free up the trampoline after bootup if cpu hotplug is not supported. */
-__CPUINITRODATA
-#endif
-.code16
+       .section ".x86_trampoline","a"
+       .balign PAGE_SIZE
+       .code16
 
 ENTRY(trampoline_data)
 r_base = .
@@ -50,7 +46,7 @@ r_base = .
        mov     %ax, %ss
 
 
-       movl    $0xA5A5A5A5, trampoline_data - r_base
+       movl    $0xA5A5A5A5, trampoline_status - r_base
                                # write marker for master knows we're running
 
                                        # Setup stack
@@ -64,10 +60,13 @@ r_base = .
        movzx   %ax, %esi               # Find the 32bit trampoline location
        shll    $4, %esi
 
-                                       # Fixup the vectors
-       addl    %esi, startup_32_vector - r_base
-       addl    %esi, startup_64_vector - r_base
-       addl    %esi, tgdt + 2 - r_base # Fixup the gdt pointer
+                                       # Fixup the absolute vectors
+       leal    (startup_32 - r_base)(%esi), %eax
+       movl    %eax, startup_32_vector - r_base
+       leal    (startup_64 - r_base)(%esi), %eax
+       movl    %eax, startup_64_vector - r_base
+       leal    (tgdt - r_base)(%esi), %eax
+       movl    %eax, (tgdt + 2 - r_base)
 
        /*
         * GDT tables in non default location kernel can be beyond 16MB and
@@ -129,6 +128,7 @@ no_longmode:
        jmp no_longmode
 #include "verify_cpu.S"
 
+       .balign 4
        # Careful these need to be in the same 64K segment as the above;
 tidt:
        .word   0                       # idt limit = 0
@@ -156,6 +156,12 @@ startup_64_vector:
        .long   startup_64 - r_base
        .word   __KERNEL_CS, 0
 
+       .balign 4
+fixup_base:
+       .long   0
+ENTRY(trampoline_status)
+       .long   0
+
 trampoline_stack:
        .org 0x1000
 trampoline_stack_end:
index bf4700755184e32d4b4e549bd19f4014caa46468..cb2c5069b016d4ff7db563899c5ed430a4c51f83 100644 (file)
@@ -240,6 +240,18 @@ SECTIONS
 
        INIT_DATA_SECTION(16)
 
+       /*
+        * Code and data for a variety of lowlevel trampolines, to be
+        * copied into base memory (< 1 MiB) during initialization.
+        * Since it is copied early, the main copy can be discarded
+        * afterwards.
+        */
+        .x86_trampoline : AT(ADDR(.x86_trampoline) - LOAD_OFFSET) {
+               x86_trampoline_start = .;
+               *(.x86_trampoline)
+               x86_trampoline_end = .;
+       }
+
        .x86_cpu_dev.init : AT(ADDR(.x86_cpu_dev.init) - LOAD_OFFSET) {
                __x86_cpu_dev_start = .;
                *(.x86_cpu_dev.init)
@@ -291,6 +303,7 @@ SECTIONS
                *(.iommu_table)
                __iommu_table_end = .;
        }
+
        . = ALIGN(8);
        /*
         * .exit.text is discard at runtime, not link time, to deal with