Merge branch 'x86-kaslr-for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git...
authorLinus Torvalds <torvalds@linux-foundation.org>
Wed, 4 Sep 2013 16:38:10 +0000 (09:38 -0700)
committerLinus Torvalds <torvalds@linux-foundation.org>
Wed, 4 Sep 2013 16:38:10 +0000 (09:38 -0700)
Pull x86 relocation changes from Ingo Molnar:
 "This tree contains a single change, ELF relocation handling in C - one
  of the kernel randomization patches that makes sense even without
  randomization present upstream"

* 'x86-kaslr-for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/tip/tip:
  x86, relocs: Move ELF relocation handling to C

arch/x86/Kconfig
arch/x86/Makefile
arch/x86/boot/compressed/head_32.S
arch/x86/boot/compressed/head_64.S
arch/x86/boot/compressed/misc.c
arch/x86/include/asm/page_32_types.h
arch/x86/include/asm/page_64_types.h
arch/x86/include/asm/page_types.h

index ff47e230d3554ef3014e6cf6bbe6215b726441d9..b710efdec9a0afcd072660545cd5a4c4d33f8428 100644 (file)
@@ -1716,9 +1716,10 @@ config X86_NEED_RELOCS
        depends on X86_32 && RELOCATABLE
 
 config PHYSICAL_ALIGN
-       hex "Alignment value to which kernel should be aligned" if X86_32
+       hex "Alignment value to which kernel should be aligned"
        default "0x1000000"
-       range 0x2000 0x1000000
+       range 0x2000 0x1000000 if X86_32
+       range 0x200000 0x1000000 if X86_64
        ---help---
          This value puts the alignment restrictions on physical address
          where kernel is loaded and run from. Kernel is compiled for an
@@ -1736,6 +1737,9 @@ config PHYSICAL_ALIGN
          end result is that kernel runs from a physical address meeting
          above alignment restrictions.
 
+         On 32-bit this value must be a multiple of 0x2000. On 64-bit
+         this value must be a multiple of 0x200000.
+
          Don't change this unless you know what you are doing.
 
 config HOTPLUG_CPU
index 07639c656fcd89b1212e14b57bfc910549a44407..41250fb33985e179f63b399cfb6a2d10c93a4e29 100644 (file)
@@ -16,6 +16,10 @@ endif
 # e.g.: obj-y += foo_$(BITS).o
 export BITS
 
+ifdef CONFIG_X86_NEED_RELOCS
+        LDFLAGS_vmlinux := --emit-relocs
+endif
+
 ifeq ($(CONFIG_X86_32),y)
         BITS := 32
         UTS_MACHINE := i386
@@ -25,10 +29,6 @@ ifeq ($(CONFIG_X86_32),y)
         KBUILD_AFLAGS += $(biarch)
         KBUILD_CFLAGS += $(biarch)
 
-        ifdef CONFIG_RELOCATABLE
-                LDFLAGS_vmlinux := --emit-relocs
-        endif
-
         KBUILD_CFLAGS += -msoft-float -mregparm=3 -freg-struct-return
 
         # Never want PIC in a 32-bit kernel, prevent breakage with GCC built
index 1e3184f6072f913250b3b18371978a4fd1b3f63d..5d6f6891b1881ee812c060bc730915d73d4d7f65 100644 (file)
@@ -181,8 +181,9 @@ relocated:
 /*
  * Do the decompression, and jump to the new kernel..
  */
-       leal    z_extract_offset_negative(%ebx), %ebp
                                /* push arguments for decompress_kernel: */
+       pushl   $z_output_len   /* decompressed length */
+       leal    z_extract_offset_negative(%ebx), %ebp
        pushl   %ebp            /* output address */
        pushl   $z_input_len    /* input_len */
        leal    input_data(%ebx), %eax
@@ -191,33 +192,7 @@ relocated:
        pushl   %eax            /* heap area */
        pushl   %esi            /* real mode pointer */
        call    decompress_kernel
-       addl    $20, %esp
-
-#if CONFIG_RELOCATABLE
-/*
- * Find the address of the relocations.
- */
-       leal    z_output_len(%ebp), %edi
-
-/*
- * Calculate the delta between where vmlinux was compiled to run
- * and where it was actually loaded.
- */
-       movl    %ebp, %ebx
-       subl    $LOAD_PHYSICAL_ADDR, %ebx
-       jz      2f      /* Nothing to be done if loaded at compiled addr. */
-/*
- * Process relocations.
- */
-
-1:     subl    $4, %edi
-       movl    (%edi), %ecx
-       testl   %ecx, %ecx
-       jz      2f
-       addl    %ebx, -__PAGE_OFFSET(%ebx, %ecx)
-       jmp     1b
-2:
-#endif
+       addl    $24, %esp
 
 /*
  * Jump to the decompressed kernel.
index 06e71c2c16bf012c006fab90caa72bb9d25cb8db..c337422b575de190280334ef5cd61214441644f9 100644 (file)
@@ -338,6 +338,7 @@ relocated:
        leaq    input_data(%rip), %rdx  /* input_data */
        movl    $z_input_len, %ecx      /* input_len */
        movq    %rbp, %r8               /* output target address */
+       movq    $z_output_len, %r9      /* decompressed length */
        call    decompress_kernel
        popq    %rsi
 
index 0319c88290a52f6ea314dd03838333ef2d18b4b8..434f077d2c4d7b664216e6adaa22ac44ea51786a 100644 (file)
@@ -271,6 +271,79 @@ static void error(char *x)
                asm("hlt");
 }
 
+#if CONFIG_X86_NEED_RELOCS
+static void handle_relocations(void *output, unsigned long output_len)
+{
+       int *reloc;
+       unsigned long delta, map, ptr;
+       unsigned long min_addr = (unsigned long)output;
+       unsigned long max_addr = min_addr + output_len;
+
+       /*
+        * Calculate the delta between where vmlinux was linked to load
+        * and where it was actually loaded.
+        */
+       delta = min_addr - LOAD_PHYSICAL_ADDR;
+       if (!delta) {
+               debug_putstr("No relocation needed... ");
+               return;
+       }
+       debug_putstr("Performing relocations... ");
+
+       /*
+        * The kernel contains a table of relocation addresses. Those
+        * addresses have the final load address of the kernel in virtual
+        * memory. We are currently working in the self map. So we need to
+        * create an adjustment for kernel memory addresses to the self map.
+        * This will involve subtracting out the base address of the kernel.
+        */
+       map = delta - __START_KERNEL_map;
+
+       /*
+        * Process relocations: 32 bit relocations first then 64 bit after.
+        * Two sets of binary relocations are added to the end of the kernel
+        * before compression. Each relocation table entry is the kernel
+        * address of the location which needs to be updated stored as a
+        * 32-bit value which is sign extended to 64 bits.
+        *
+        * Format is:
+        *
+        * kernel bits...
+        * 0 - zero terminator for 64 bit relocations
+        * 64 bit relocation repeated
+        * 0 - zero terminator for 32 bit relocations
+        * 32 bit relocation repeated
+        *
+        * So we work backwards from the end of the decompressed image.
+        */
+       for (reloc = output + output_len - sizeof(*reloc); *reloc; reloc--) {
+               int extended = *reloc;
+               extended += map;
+
+               ptr = (unsigned long)extended;
+               if (ptr < min_addr || ptr > max_addr)
+                       error("32-bit relocation outside of kernel!\n");
+
+               *(uint32_t *)ptr += delta;
+       }
+#ifdef CONFIG_X86_64
+       for (reloc--; *reloc; reloc--) {
+               long extended = *reloc;
+               extended += map;
+
+               ptr = (unsigned long)extended;
+               if (ptr < min_addr || ptr > max_addr)
+                       error("64-bit relocation outside of kernel!\n");
+
+               *(uint64_t *)ptr += delta;
+       }
+#endif
+}
+#else
+static inline void handle_relocations(void *output, unsigned long output_len)
+{ }
+#endif
+
 static void parse_elf(void *output)
 {
 #ifdef CONFIG_X86_64
@@ -325,7 +398,8 @@ static void parse_elf(void *output)
 asmlinkage void decompress_kernel(void *rmode, memptr heap,
                                  unsigned char *input_data,
                                  unsigned long input_len,
-                                 unsigned char *output)
+                                 unsigned char *output,
+                                 unsigned long output_len)
 {
        real_mode = rmode;
 
@@ -365,6 +439,7 @@ asmlinkage void decompress_kernel(void *rmode, memptr heap,
        debug_putstr("\nDecompressing Linux... ");
        decompress(input_data, input_len, NULL, NULL, output, NULL, error);
        parse_elf(output);
+       handle_relocations(output, output_len);
        debug_putstr("done.\nBooting the kernel.\n");
        return;
 }
index ef17af0134750203f803f5a665a86178abfa2107..f48b17df42249e45cca9ef6de99bfd43083507cd 100644 (file)
@@ -15,6 +15,8 @@
  */
 #define __PAGE_OFFSET          _AC(CONFIG_PAGE_OFFSET, UL)
 
+#define __START_KERNEL_map     __PAGE_OFFSET
+
 #define THREAD_SIZE_ORDER      1
 #define THREAD_SIZE            (PAGE_SIZE << THREAD_SIZE_ORDER)
 
index 6c896fbe21db50fca525bb321aceab47172ee9d4..43dcd804ebd5732aea40415e6f4a419e363701c9 100644 (file)
  */
 #define __PAGE_OFFSET           _AC(0xffff880000000000, UL)
 
-#define __PHYSICAL_START       ((CONFIG_PHYSICAL_START +               \
-                                 (CONFIG_PHYSICAL_ALIGN - 1)) &        \
-                                ~(CONFIG_PHYSICAL_ALIGN - 1))
-
-#define __START_KERNEL         (__START_KERNEL_map + __PHYSICAL_START)
 #define __START_KERNEL_map     _AC(0xffffffff80000000, UL)
 
 /* See Documentation/x86/x86_64/mm.txt for a description of the memory map. */
index 54c97879195e7c5137d2a45fbda51c40a1c41be2..f97fbe3abb67f5059d4e6f0a37261d6113df19de 100644 (file)
        (((current->personality & READ_IMPLIES_EXEC) ? VM_EXEC : 0 ) | \
         VM_READ | VM_WRITE | VM_MAYREAD | VM_MAYWRITE | VM_MAYEXEC)
 
+#define __PHYSICAL_START       ALIGN(CONFIG_PHYSICAL_START, \
+                                     CONFIG_PHYSICAL_ALIGN)
+
+#define __START_KERNEL         (__START_KERNEL_map + __PHYSICAL_START)
+
 #ifdef CONFIG_X86_64
 #include <asm/page_64_types.h>
 #else