purgatory: core purgatory functionality
authorVivek Goyal <vgoyal@redhat.com>
Fri, 8 Aug 2014 21:26:02 +0000 (14:26 -0700)
committerLinus Torvalds <torvalds@linux-foundation.org>
Fri, 8 Aug 2014 22:57:32 +0000 (15:57 -0700)
Create a stand alone relocatable object purgatory which runs between two
kernels.  This name, concept and some code has been taken from
kexec-tools.  Idea is that this code runs after a crash and it runs in
minimal environment.  So keep it separate from rest of the kernel and in
long term we will have to practically do no maintenance of this code.

This code also has the logic to do verify sha256 hashes of various
segments which have been loaded into memory.  So first we verify that the
kernel we are jumping to is fine and has not been corrupted and make
progress only if checsums are verified.

This code also takes care of copying some memory contents to backup region.

[sfr@canb.auug.org.au: run host built programs from objtree]
Signed-off-by: Vivek Goyal <vgoyal@redhat.com>
Cc: Borislav Petkov <bp@suse.de>
Cc: Michael Kerrisk <mtk.manpages@gmail.com>
Cc: Yinghai Lu <yinghai@kernel.org>
Cc: Eric Biederman <ebiederm@xmission.com>
Cc: H. Peter Anvin <hpa@zytor.com>
Cc: Matthew Garrett <mjg59@srcf.ucam.org>
Cc: Greg Kroah-Hartman <greg@kroah.com>
Cc: Dave Young <dyoung@redhat.com>
Cc: WANG Chao <chaowang@redhat.com>
Cc: Baoquan He <bhe@redhat.com>
Cc: Andy Lutomirski <luto@amacapital.net>
Signed-off-by: Stephen Rothwell <sfr@canb.auug.org.au>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
arch/x86/Kbuild
arch/x86/Makefile
arch/x86/purgatory/Makefile [new file with mode: 0644]
arch/x86/purgatory/entry64.S [new file with mode: 0644]
arch/x86/purgatory/purgatory.c [new file with mode: 0644]
arch/x86/purgatory/setup-x86_64.S [new file with mode: 0644]
arch/x86/purgatory/stack.S [new file with mode: 0644]
arch/x86/purgatory/string.c [new file with mode: 0644]

index e5287d8..61b6d51 100644 (file)
@@ -16,3 +16,7 @@ obj-$(CONFIG_IA32_EMULATION) += ia32/
 
 obj-y += platform/
 obj-y += net/
+
+ifeq ($(CONFIG_X86_64),y)
+obj-$(CONFIG_KEXEC) += purgatory/
+endif
index c65fd96..c1aa368 100644 (file)
@@ -183,6 +183,14 @@ archscripts: scripts_basic
 archheaders:
        $(Q)$(MAKE) $(build)=arch/x86/syscalls all
 
+archprepare:
+ifeq ($(CONFIG_KEXEC),y)
+# Build only for 64bit. No loaders for 32bit yet.
+ ifeq ($(CONFIG_X86_64),y)
+       $(Q)$(MAKE) $(build)=arch/x86/purgatory arch/x86/purgatory/kexec-purgatory.c
+ endif
+endif
+
 ###
 # Kernel objects
 
diff --git a/arch/x86/purgatory/Makefile b/arch/x86/purgatory/Makefile
new file mode 100644 (file)
index 0000000..7fde9ee
--- /dev/null
@@ -0,0 +1,30 @@
+purgatory-y := purgatory.o stack.o setup-x86_$(BITS).o sha256.o entry64.o string.o
+
+targets += $(purgatory-y)
+PURGATORY_OBJS = $(addprefix $(obj)/,$(purgatory-y))
+
+LDFLAGS_purgatory.ro := -e purgatory_start -r --no-undefined -nostdlib -z nodefaultlib
+targets += purgatory.ro
+
+# Default KBUILD_CFLAGS can have -pg option set when FTRACE is enabled. That
+# in turn leaves some undefined symbols like __fentry__ in purgatory and not
+# sure how to relocate those. Like kexec-tools, use custom flags.
+
+KBUILD_CFLAGS := -fno-strict-aliasing -Wall -Wstrict-prototypes -fno-zero-initialized-in-bss -fno-builtin -ffreestanding -c -MD -Os -mcmodel=large
+
+$(obj)/purgatory.ro: $(PURGATORY_OBJS) FORCE
+               $(call if_changed,ld)
+
+targets += kexec-purgatory.c
+
+quiet_cmd_bin2c = BIN2C   $@
+      cmd_bin2c = cat $(obj)/purgatory.ro | $(objtree)/scripts/basic/bin2c kexec_purgatory > $(obj)/kexec-purgatory.c
+
+$(obj)/kexec-purgatory.c: $(obj)/purgatory.ro FORCE
+       $(call if_changed,bin2c)
+
+
+# No loaders for 32bits yet.
+ifeq ($(CONFIG_X86_64),y)
+ obj-$(CONFIG_KEXEC)           += kexec-purgatory.o
+endif
diff --git a/arch/x86/purgatory/entry64.S b/arch/x86/purgatory/entry64.S
new file mode 100644 (file)
index 0000000..be3249d
--- /dev/null
@@ -0,0 +1,101 @@
+/*
+ * Copyright (C) 2003,2004  Eric Biederman (ebiederm@xmission.com)
+ * Copyright (C) 2014  Red Hat Inc.
+
+ * Author(s): Vivek Goyal <vgoyal@redhat.com>
+ *
+ * This code has been taken from kexec-tools.
+ *
+ * This source code is licensed under the GNU General Public License,
+ * Version 2.  See the file COPYING for more details.
+ */
+
+       .text
+       .balign 16
+       .code64
+       .globl entry64, entry64_regs
+
+
+entry64:
+       /* Setup a gdt that should be preserved */
+       lgdt gdt(%rip)
+
+       /* load the data segments */
+       movl    $0x18, %eax     /* data segment */
+       movl    %eax, %ds
+       movl    %eax, %es
+       movl    %eax, %ss
+       movl    %eax, %fs
+       movl    %eax, %gs
+
+       /* Setup new stack */
+       leaq    stack_init(%rip), %rsp
+       pushq   $0x10 /* CS */
+       leaq    new_cs_exit(%rip), %rax
+       pushq   %rax
+       lretq
+new_cs_exit:
+
+       /* Load the registers */
+       movq    rax(%rip), %rax
+       movq    rbx(%rip), %rbx
+       movq    rcx(%rip), %rcx
+       movq    rdx(%rip), %rdx
+       movq    rsi(%rip), %rsi
+       movq    rdi(%rip), %rdi
+       movq    rsp(%rip), %rsp
+       movq    rbp(%rip), %rbp
+       movq    r8(%rip), %r8
+       movq    r9(%rip), %r9
+       movq    r10(%rip), %r10
+       movq    r11(%rip), %r11
+       movq    r12(%rip), %r12
+       movq    r13(%rip), %r13
+       movq    r14(%rip), %r14
+       movq    r15(%rip), %r15
+
+       /* Jump to the new code... */
+       jmpq    *rip(%rip)
+
+       .section ".rodata"
+       .balign 4
+entry64_regs:
+rax:   .quad 0x0
+rbx:   .quad 0x0
+rcx:   .quad 0x0
+rdx:   .quad 0x0
+rsi:   .quad 0x0
+rdi:   .quad 0x0
+rsp:   .quad 0x0
+rbp:   .quad 0x0
+r8:    .quad 0x0
+r9:    .quad 0x0
+r10:   .quad 0x0
+r11:   .quad 0x0
+r12:   .quad 0x0
+r13:   .quad 0x0
+r14:   .quad 0x0
+r15:   .quad 0x0
+rip:   .quad 0x0
+       .size entry64_regs, . - entry64_regs
+
+       /* GDT */
+       .section ".rodata"
+       .balign 16
+gdt:
+       /* 0x00 unusable segment
+        * 0x08 unused
+        * so use them as gdt ptr
+        */
+       .word gdt_end - gdt - 1
+       .quad gdt
+       .word 0, 0, 0
+
+       /* 0x10 4GB flat code segment */
+       .word 0xFFFF, 0x0000, 0x9A00, 0x00AF
+
+       /* 0x18 4GB flat data segment */
+       .word 0xFFFF, 0x0000, 0x9200, 0x00CF
+gdt_end:
+stack: .quad   0, 0
+stack_init:
diff --git a/arch/x86/purgatory/purgatory.c b/arch/x86/purgatory/purgatory.c
new file mode 100644 (file)
index 0000000..25e068b
--- /dev/null
@@ -0,0 +1,72 @@
+/*
+ * purgatory: Runs between two kernels
+ *
+ * Copyright (C) 2014 Red Hat Inc.
+ *
+ * Author:
+ *       Vivek Goyal <vgoyal@redhat.com>
+ *
+ * This source code is licensed under the GNU General Public License,
+ * Version 2.  See the file COPYING for more details.
+ */
+
+#include "sha256.h"
+#include "../boot/string.h"
+
+struct sha_region {
+       unsigned long start;
+       unsigned long len;
+};
+
+unsigned long backup_dest = 0;
+unsigned long backup_src = 0;
+unsigned long backup_sz = 0;
+
+u8 sha256_digest[SHA256_DIGEST_SIZE] = { 0 };
+
+struct sha_region sha_regions[16] = {};
+
+/*
+ * On x86, second kernel requries first 640K of memory to boot. Copy
+ * first 640K to a backup region in reserved memory range so that second
+ * kernel can use first 640K.
+ */
+static int copy_backup_region(void)
+{
+       if (backup_dest)
+               memcpy((void *)backup_dest, (void *)backup_src, backup_sz);
+
+       return 0;
+}
+
+int verify_sha256_digest(void)
+{
+       struct sha_region *ptr, *end;
+       u8 digest[SHA256_DIGEST_SIZE];
+       struct sha256_state sctx;
+
+       sha256_init(&sctx);
+       end = &sha_regions[sizeof(sha_regions)/sizeof(sha_regions[0])];
+       for (ptr = sha_regions; ptr < end; ptr++)
+               sha256_update(&sctx, (uint8_t *)(ptr->start), ptr->len);
+
+       sha256_final(&sctx, digest);
+
+       if (memcmp(digest, sha256_digest, sizeof(digest)))
+               return 1;
+
+       return 0;
+}
+
+void purgatory(void)
+{
+       int ret;
+
+       ret = verify_sha256_digest();
+       if (ret) {
+               /* loop forever */
+               for (;;)
+                       ;
+       }
+       copy_backup_region();
+}
diff --git a/arch/x86/purgatory/setup-x86_64.S b/arch/x86/purgatory/setup-x86_64.S
new file mode 100644 (file)
index 0000000..fe3c91b
--- /dev/null
@@ -0,0 +1,58 @@
+/*
+ * purgatory:  setup code
+ *
+ * Copyright (C) 2003,2004  Eric Biederman (ebiederm@xmission.com)
+ * Copyright (C) 2014 Red Hat Inc.
+ *
+ * This code has been taken from kexec-tools.
+ *
+ * This source code is licensed under the GNU General Public License,
+ * Version 2.  See the file COPYING for more details.
+ */
+
+       .text
+       .globl purgatory_start
+       .balign 16
+purgatory_start:
+       .code64
+
+       /* Load a gdt so I know what the segment registers are */
+       lgdt    gdt(%rip)
+
+       /* load the data segments */
+       movl    $0x18, %eax     /* data segment */
+       movl    %eax, %ds
+       movl    %eax, %es
+       movl    %eax, %ss
+       movl    %eax, %fs
+       movl    %eax, %gs
+
+       /* Setup a stack */
+       leaq    lstack_end(%rip), %rsp
+
+       /* Call the C code */
+       call purgatory
+       jmp     entry64
+
+       .section ".rodata"
+       .balign 16
+gdt:   /* 0x00 unusable segment
+        * 0x08 unused
+        * so use them as the gdt ptr
+        */
+       .word   gdt_end - gdt - 1
+       .quad   gdt
+       .word   0, 0, 0
+
+       /* 0x10 4GB flat code segment */
+       .word   0xFFFF, 0x0000, 0x9A00, 0x00AF
+
+       /* 0x18 4GB flat data segment */
+       .word   0xFFFF, 0x0000, 0x9200, 0x00CF
+gdt_end:
+
+       .bss
+       .balign 4096
+lstack:
+       .skip 4096
+lstack_end:
diff --git a/arch/x86/purgatory/stack.S b/arch/x86/purgatory/stack.S
new file mode 100644 (file)
index 0000000..3cefba1
--- /dev/null
@@ -0,0 +1,19 @@
+/*
+ * purgatory:  stack
+ *
+ * Copyright (C) 2014 Red Hat Inc.
+ *
+ * This source code is licensed under the GNU General Public License,
+ * Version 2.  See the file COPYING for more details.
+ */
+
+       /* A stack for the loaded kernel.
+        * Seperate and in the data section so it can be prepopulated.
+        */
+       .data
+       .balign 4096
+       .globl stack, stack_end
+
+stack:
+       .skip 4096
+stack_end:
diff --git a/arch/x86/purgatory/string.c b/arch/x86/purgatory/string.c
new file mode 100644 (file)
index 0000000..d886b1f
--- /dev/null
@@ -0,0 +1,13 @@
+/*
+ * Simple string functions.
+ *
+ * Copyright (C) 2014 Red Hat Inc.
+ *
+ * Author:
+ *       Vivek Goyal <vgoyal@redhat.com>
+ *
+ * This source code is licensed under the GNU General Public License,
+ * Version 2.  See the file COPYING for more details.
+ */
+
+#include "../boot/string.c"