x86, boot: determine compressed code offset at compile time
authorH. Peter Anvin <hpa@zytor.com>
Sat, 9 May 2009 00:42:16 +0000 (17:42 -0700)
committerH. Peter Anvin <hpa@zytor.com>
Sat, 9 May 2009 00:46:34 +0000 (17:46 -0700)
Determine the compressed code offset (from the kernel runtime address)
at compile time.  This allows some minor optimizations in
arch/x86/boot/compressed/head_*.S, but more importantly it makes this
value available to the build process, which will enable a future patch
to export the necessary linear memory footprint into the bzImage
header.

[ Impact: cleanup, future patch enabling ]

Signed-off-by: H. Peter Anvin <hpa@zytor.com>

arch/x86/boot/compressed/Makefile
arch/x86/boot/compressed/head_32.S
arch/x86/boot/compressed/head_64.S
arch/x86/boot/compressed/mkpiggy.c [new file with mode: 0644]
arch/x86/boot/compressed/vmlinux.scr [deleted file]

index 7f24fdb..49c8a4c 100644 (file)
@@ -19,6 +19,8 @@ KBUILD_AFLAGS  := $(KBUILD_CFLAGS) -D__ASSEMBLY__
 LDFLAGS := -m elf_$(UTS_MACHINE)
 LDFLAGS_vmlinux := -T
 
+hostprogs-y    := mkpiggy
+
 $(obj)/vmlinux: $(obj)/vmlinux.lds $(obj)/head_$(BITS).o $(obj)/misc.o $(obj)/piggy.o FORCE
        $(call if_changed,ld)
        @:
@@ -50,6 +52,9 @@ suffix-$(CONFIG_KERNEL_GZIP)  := gz
 suffix-$(CONFIG_KERNEL_BZIP2)  := bz2
 suffix-$(CONFIG_KERNEL_LZMA)   := lzma
 
-LDFLAGS_piggy.o := -r --format binary --oformat $(CONFIG_OUTPUT_FORMAT) -T
-$(obj)/piggy.o: $(obj)/vmlinux.scr $(obj)/vmlinux.bin.$(suffix-y) FORCE
-       $(call if_changed,ld)
+quiet_cmd_mkpiggy = MKPIGGY $@
+      cmd_mkpiggy = $(obj)/mkpiggy $< > $@ || ( rm -f $@ ; false )
+
+targets += piggy.S
+$(obj)/piggy.S: $(obj)/vmlinux.bin.$(suffix-y) $(obj)/mkpiggy FORCE
+       $(call if_changed,mkpiggy)
index 6710dc7..470474b 100644 (file)
@@ -75,18 +75,8 @@ ENTRY(startup_32)
        movl    $LOAD_PHYSICAL_ADDR, %ebx
 #endif
 
-       /* Replace the compressed data size with the uncompressed size */
-       subl    input_len(%ebp), %ebx
-       movl    output_len(%ebp), %eax
-       addl    %eax, %ebx
-       /* Add 8 bytes for every 32K input block */
-       shrl    $12, %eax
-       addl    %eax, %ebx
-       /* Add 32K + 18 bytes of extra slack */
-       addl    $(32768 + 18), %ebx
-       /* Align on a 4K boundary */
-       addl    $4095, %ebx
-       andl    $~4095, %ebx
+       /* Target address to relocate to for decompression */
+       addl    $z_extract_offset, %ebx
 
        /* Set up the stack */
        leal    boot_stack_end(%ebx), %esp
@@ -142,12 +132,10 @@ relocated:
 /*
  * Do the decompression, and jump to the new kernel..
  */
-       movl    output_len(%ebx), %eax
-       pushl   %eax
+       leal    z_extract_offset_negative(%ebx), %ebp
                                /* push arguments for decompress_kernel: */
        pushl   %ebp            /* output address */
-       movl    input_len(%ebx), %eax
-       pushl   %eax            /* input_len */
+       pushl   $z_input_len    /* input_len */
        leal    input_data(%ebx), %eax
        pushl   %eax            /* input_data */
        leal    boot_heap(%ebx), %eax
@@ -155,14 +143,12 @@ relocated:
        pushl   %esi            /* real mode pointer */
        call    decompress_kernel
        addl    $20, %esp
-       popl    %ecx
 
 #if CONFIG_RELOCATABLE
 /*
  * Find the address of the relocations.
  */
-       movl    %ebp, %edi
-       addl    %ecx, %edi
+       leal    z_output_len(%ebp), %edi
 
 /*
  * Calculate the delta between where vmlinux was compiled to run
index 723c72d..2b9f251 100644 (file)
@@ -90,16 +90,8 @@ ENTRY(startup_32)
        movl    $CONFIG_PHYSICAL_START, %ebx
 #endif
 
-       /* Replace the compressed data size with the uncompressed size */
-       subl    input_len(%ebp), %ebx
-       movl    output_len(%ebp), %eax
-       addl    %eax, %ebx
-       /* Add 8 bytes for every 32K input block */
-       shrl    $12, %eax
-       addl    %eax, %ebx
-       /* Add 32K + 18 bytes of extra slack and align on a 4K boundary */
-       addl    $(32768 + 18 + 4095), %ebx
-       andl    $~4095, %ebx
+       /* Target address to relocate to for decompression */
+       addl    $z_extract_offset, %ebx
 
 /*
  * Prepare for entering 64 bit mode
@@ -224,6 +216,9 @@ ENTRY(startup_64)
         * If it is a relocatable kernel then decompress and run the kernel
         * from load address aligned to 2MB addr, otherwise decompress and
         * run the kernel from CONFIG_PHYSICAL_START
+        *
+        * We cannot rely on the calculation done in 32-bit mode, since we
+        * may have been invoked via the 64-bit entry point.
         */
 
        /* Start with the delta to where the kernel will run at. */
@@ -237,17 +232,8 @@ ENTRY(startup_64)
        movq    %rbp, %rbx
 #endif
 
-       /* Replace the compressed data size with the uncompressed size */
-       movl    input_len(%rip), %eax
-       subq    %rax, %rbx
-       movl    output_len(%rip), %eax
-       addq    %rax, %rbx
-       /* Add 8 bytes for every 32K input block */
-       shrq    $12, %rax
-       addq    %rax, %rbx
-       /* Add 32K + 18 bytes of extra slack and align on a 4K boundary */
-       addq    $(32768 + 18 + 4095), %rbx
-       andq    $~4095, %rbx
+       /* Target address to relocate to for decompression */
+       leaq    z_extract_offset(%rbp), %rbx
 
        /* Set up the stack */
        leaq    boot_stack_end(%rbx), %rsp
@@ -292,13 +278,12 @@ relocated:
 /*
  * Do the decompression, and jump to the new kernel..
  */
-       pushq   %rsi                    # Save the real mode argument
-       movq    %rsi, %rdi              # real mode address
-       leaq    boot_heap(%rip), %rsi   # malloc area for uncompression
-       leaq    input_data(%rip), %rdx  # input_data
-       movl    input_len(%rip), %eax
-       movq    %rax, %rcx              # input_len
-       movq    %rbp, %r8               # output
+       pushq   %rsi                    /* Save the real mode argument */
+       movq    %rsi, %rdi              /* real mode address */
+       leaq    boot_heap(%rip), %rsi   /* malloc area for uncompression */
+       leaq    input_data(%rip), %rdx  /* input_data */
+       movl    $z_input_len, %ecx      /* input_len */
+       movq    %rbp, %r8               /* output target address */
        call    decompress_kernel
        popq    %rsi
 
diff --git a/arch/x86/boot/compressed/mkpiggy.c b/arch/x86/boot/compressed/mkpiggy.c
new file mode 100644 (file)
index 0000000..bcbd36c
--- /dev/null
@@ -0,0 +1,97 @@
+/* ----------------------------------------------------------------------- *
+ *
+ *  Copyright (C) 2009 Intel Corporation. All rights reserved.
+ *
+ *  This program is free software; you can redistribute it and/or
+ *  modify it under the terms of the GNU General Public License version
+ *  2 as published by the Free Software Foundation.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ *  02110-1301, USA.
+ *
+ *  H. Peter Anvin <hpa@linux.intel.com>
+ *
+ * ----------------------------------------------------------------------- */
+
+/*
+ * Compute the desired load offset from a compressed program; outputs
+ * a small assembly wrapper with the appropriate symbols defined.
+ */
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <inttypes.h>
+
+static uint32_t getle32(const void *p)
+{
+       const uint8_t *cp = p;
+
+       return (uint32_t)cp[0] + ((uint32_t)cp[1] << 8) +
+               ((uint32_t)cp[2] << 16) + ((uint32_t)cp[3] << 24);
+}
+
+int main(int argc, char *argv[])
+{
+       uint32_t olen;
+       long ilen;
+       unsigned long offs;
+       FILE *f;
+
+       if (argc < 2) {
+               fprintf(stderr, "Usage: %s compressed_file\n", argv[0]);
+               return 1;
+       }
+
+       /* Get the information for the compressed kernel image first */
+
+       f = fopen(argv[1], "r");
+       if (!f) {
+               perror(argv[1]);
+               return 1;
+       }
+
+
+       if (fseek(f, -4L, SEEK_END)) {
+               perror(argv[1]);
+       }
+       fread(&olen, sizeof olen, 1, f);
+       ilen = ftell(f);
+       olen = getle32(&olen);
+       fclose(f);
+
+       /*
+        * Now we have the input (compressed) and output (uncompressed)
+        * sizes, compute the necessary decompression offset...
+        */
+
+       offs = (olen > ilen) ? olen - ilen : 0;
+       offs += olen >> 12;     /* Add 8 bytes for each 32K block */
+       offs += 32*1024 + 18;   /* Add 32K + 18 bytes slack */
+       offs = (offs+4095) & ~4095; /* Round to a 4K boundary */
+
+       printf(".section \".rodata.compressed\",\"a\",@progbits\n");
+       printf(".globl z_input_len\n");
+       printf("z_input_len = %lu\n", ilen);
+       printf(".globl z_output_len\n");
+       printf("z_output_len = %lu\n", (unsigned long)olen);
+       printf(".globl z_extract_offset\n");
+       printf("z_extract_offset = 0x%lx\n", offs);
+       /* z_extract_offset_negative allows simplification of head_32.S */
+       printf(".globl z_extract_offset_negative\n");
+       printf("z_extract_offset_negative = -0x%lx\n", offs);
+
+       printf(".globl input_data, input_data_end\n");
+       printf("input_data:\n");
+       printf(".incbin \"%s\"\n", argv[1]);
+       printf("input_data_end:\n");
+
+       return 0;
+}
diff --git a/arch/x86/boot/compressed/vmlinux.scr b/arch/x86/boot/compressed/vmlinux.scr
deleted file mode 100644 (file)
index f02382a..0000000
+++ /dev/null
@@ -1,10 +0,0 @@
-SECTIONS
-{
-  .rodata.compressed : {
-       input_len = .;
-       LONG(input_data_end - input_data) input_data = .;
-       *(.data)
-       output_len = . - 4;
-       input_data_end = .;
-       }
-}