Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 8fc5b4d4 authored by Vivek Goyal's avatar Vivek Goyal Committed by Linus Torvalds
Browse files

purgatory: core purgatory functionality



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: default avatarVivek 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: default avatarStephen Rothwell <sfr@canb.auug.org.au>
Signed-off-by: default avatarAndrew Morton <akpm@linux-foundation.org>
Signed-off-by: default avatarLinus Torvalds <torvalds@linux-foundation.org>
parent daeba064
Loading
Loading
Loading
Loading
+4 −0
Original line number Diff line number Diff line
@@ -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
+8 −0
Original line number Diff line number Diff line
@@ -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

+30 −0
Original line number Diff line number Diff line
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
+101 −0
Original line number Diff line number Diff line
/*
 * 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:
+72 −0
Original line number Diff line number Diff line
/*
 * 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();
}
Loading