From patchwork Thu Aug 24 08:18:11 2017 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: AKASHI Takahiro X-Patchwork-Id: 110882 Delivered-To: patch@linaro.org Received: by 10.140.95.78 with SMTP id h72csp5315920qge; Thu, 24 Aug 2017 01:19:40 -0700 (PDT) X-Received: by 10.84.174.129 with SMTP id r1mr5908586plb.55.1503562780263; Thu, 24 Aug 2017 01:19:40 -0700 (PDT) ARC-Seal: i=1; a=rsa-sha256; t=1503562780; cv=none; d=google.com; s=arc-20160816; b=e9tBtgTb7P+lYIgW08BNW11ylnFi5PZSvQjMUuM9GzUveeERRxcqSi4MAkwxBJ46j2 pLIVmLkEWbPh2AXb/DT5Gx9jFIAMX4572IN7h23EQDfFNq6Fadvq+Sz9NTj2a+se2UNl cwi7HZHWLEKrU3fa90QfWxKkYxguNvx7OmO7ZpDqQvIu7ihudMVHtJDd/Mz6qAF0LXen a/pjkSFL8gSiGJJeEH+CUaX8QbRwIJDo+pNnNLl9oblRPUVtCb77a8BoOJTUO3XAaB/E /rhwl2kkFzrtOVDQET/Rw2DFmNJEU2GXbyeqMqD/KMKqmAJeKb6hgo2RnOVHqJfICR9V DSuQ== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=arc-20160816; h=list-id:precedence:sender:references:in-reply-to:message-id:date :subject:cc:to:from:dkim-signature:arc-authentication-results; bh=oWx+DDmp+fCylEpyN4dHca6RbYai8lu7RxLXeTLs1NA=; b=FRP1Zv37bCch96E4poZXu2NjEHk4/I8gqzXHrLUP9MX/R9u6ble1pGsehGPZ2fvkNa 6X6/OWrUCu+f5jZU9zXsXjw9T4CLy87FcnoEmhBs4sZMb9HZ3dfYkyesJ+q0ClTLW8HL R/8sacbQbKHUqn0gNDmemYfVe0emhMTgaAUHjiCMUBJX4W9EZKM0TIEokfRjejaniO0B cuB8Fomb57i7tDIS/dZLpb00acP+wcxwmcJB4qRAoBM5yMUJ8+xYbA9NUIjizOQVYEmE 63k6tc5FJRO8cyljhQve/nfzYijo3nncq5xqgPv3PMESrFKEQZmSjlYzZKr7vnmaBO0h OGfg== ARC-Authentication-Results: i=1; mx.google.com; dkim=pass header.i=@linaro.org header.s=google header.b=Mj2AIQh1; spf=pass (google.com: best guess record for domain of linux-kernel-owner@vger.kernel.org designates 209.132.180.67 as permitted sender) smtp.mailfrom=linux-kernel-owner@vger.kernel.org; dmarc=pass (p=NONE sp=NONE dis=NONE) header.from=linaro.org Return-Path: Received: from vger.kernel.org (vger.kernel.org. [209.132.180.67]) by mx.google.com with ESMTP id m2si2397789pgt.892.2017.08.24.01.19.39; Thu, 24 Aug 2017 01:19:40 -0700 (PDT) Received-SPF: pass (google.com: best guess record for domain of linux-kernel-owner@vger.kernel.org designates 209.132.180.67 as permitted sender) client-ip=209.132.180.67; Authentication-Results: mx.google.com; dkim=pass header.i=@linaro.org header.s=google header.b=Mj2AIQh1; spf=pass (google.com: best guess record for domain of linux-kernel-owner@vger.kernel.org designates 209.132.180.67 as permitted sender) smtp.mailfrom=linux-kernel-owner@vger.kernel.org; dmarc=pass (p=NONE sp=NONE dis=NONE) header.from=linaro.org Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1752421AbdHXITi (ORCPT + 26 others); Thu, 24 Aug 2017 04:19:38 -0400 Received: from mail-pg0-f42.google.com ([74.125.83.42]:36112 "EHLO mail-pg0-f42.google.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1751368AbdHXITU (ORCPT ); Thu, 24 Aug 2017 04:19:20 -0400 Received: by mail-pg0-f42.google.com with SMTP id 83so13459099pgb.3 for ; Thu, 24 Aug 2017 01:19:20 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=linaro.org; s=google; h=from:to:cc:subject:date:message-id:in-reply-to:references; bh=oWx+DDmp+fCylEpyN4dHca6RbYai8lu7RxLXeTLs1NA=; b=Mj2AIQh1ZXco4f66i2c439rKto6+M14tPZoqjS9v2fdiHw4lcy5YdN/xsxWXNrQVd2 Y9to3QELN2JtbKn0ZwS/0l9PW4GyMNzTUEnzy0oszZu3zrMi0WX3FELxotiGMb6+8Ond wailb7kPU0OR/Kt2zCkKaWg94ZADIFbnSkB2c= X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:from:to:cc:subject:date:message-id:in-reply-to :references; bh=oWx+DDmp+fCylEpyN4dHca6RbYai8lu7RxLXeTLs1NA=; b=a3S23sJTd+MKKxMmmkqdxBbtzBc96lIU+zjfnWQHyxhqGi4XGwEVVxqqQrrex5xZrv cTFdN5b9VvvDkNvq9HoUzuyEcD+iYaq+gdhzP0U6TiYCiFFMcORxYDxNiLCx8eHw8+v+ fLGNz2vv/C0PQYoMvtG6J6WgZ86z7U5Lbvlt1Y9UHun5F2GyT2aCNBFIe7/kWDlpyorG cD8ce+5vIvDw6/1Fi41u0pYiZ/JQMcjWYfCper5po2GKKFIk7IHRWkd84xgB5gNbq83d 7IQCYtSfRfb6Hi3IOT/oXtGL8yf5/yFY35Hop8fACnmtbsq7r11Pi1JQoKc12jHoTHt0 NwoA== X-Gm-Message-State: AHYfb5gaczjaoZ1FyCDRjUuwqrsimxR+jtEzEnNH2Jt9fJtkGyZi6i2x m2EOXHt4SlPR5UbV X-Received: by 10.98.80.131 with SMTP id g3mr5815720pfj.156.1503562759742; Thu, 24 Aug 2017 01:19:19 -0700 (PDT) Received: from linaro.org ([121.95.100.191]) by smtp.googlemail.com with ESMTPSA id b68sm6401230pfd.33.2017.08.24.01.19.18 (version=TLS1_2 cipher=ECDHE-RSA-AES128-GCM-SHA256 bits=128/128); Thu, 24 Aug 2017 01:19:19 -0700 (PDT) From: AKASHI Takahiro To: catalin.marinas@arm.com, will.deacon@arm.com, bauerman@linux.vnet.ibm.com, dhowells@redhat.com, vgoyal@redhat.com, herbert@gondor.apana.org.au, davem@davemloft.net, akpm@linux-foundation.org, mpe@ellerman.id.au, dyoung@redhat.com, bhe@redhat.com, arnd@arndb.de, ard.biesheuvel@linaro.org Cc: kexec@lists.infradead.org, linux-arm-kernel@lists.infradead.org, linux-kernel@vger.kernel.org, AKASHI Takahiro Subject: [PATCH 14/14] arm64: kexec_file: add vmlinux format support Date: Thu, 24 Aug 2017 17:18:11 +0900 Message-Id: <20170824081811.19299-15-takahiro.akashi@linaro.org> X-Mailer: git-send-email 2.14.1 In-Reply-To: <20170824081811.19299-1-takahiro.akashi@linaro.org> References: <20170824081811.19299-1-takahiro.akashi@linaro.org> Sender: linux-kernel-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-kernel@vger.kernel.org The first PT_LOAD segment, which is assumed to be "text" code, in vmlinux will be loaded at the offset of TEXT_OFFSET from the begining of system memory. The other PT_LOAD segments are placed relative to the first one. Regarding kernel verification, since there is no standard way to contain a signature within elf binary, we follow PowerPC's (not yet upstreamed) approach, that is, appending a signature right after the kernel binary itself like module signing. This way, the signature can be easily retrieved and verified with verify_pkcs7_signature(). We can sign the kernel with sign-file command. Unlike PowerPC, we don't support ima-based kexec for now since arm64 doesn't have any secure solution for system appraisal at this moment. Signed-off-by: AKASHI Takahiro Cc: Catalin Marinas Cc: Will Deacon --- arch/arm64/Kconfig | 8 ++ arch/arm64/include/asm/kexec_file.h | 1 + arch/arm64/kernel/Makefile | 1 + arch/arm64/kernel/kexec_elf.c | 216 +++++++++++++++++++++++++++++++++ arch/arm64/kernel/machine_kexec_file.c | 3 + 5 files changed, 229 insertions(+) create mode 100644 arch/arm64/kernel/kexec_elf.c -- 2.14.1 diff --git a/arch/arm64/Kconfig b/arch/arm64/Kconfig index c8f603700bdd..94021e66b826 100644 --- a/arch/arm64/Kconfig +++ b/arch/arm64/Kconfig @@ -772,11 +772,19 @@ config KEXEC_FILE_IMAGE_FMT ---help--- Select this option to enable 'Image' kernel loading. +config KEXEC_FILE_ELF_FMT + bool "Enable vmlinux/elf support" + depends on KEXEC_FILE + select KEXEC_FILE_ELF + ---help--- + Select this option to enable 'vmlinux' kernel loading. + config KEXEC_VERIFY_SIG bool "Verify kernel signature during kexec_file_load() syscall" depends on KEXEC_FILE select SYSTEM_DATA_VERIFICATION select SIGNED_PE_FILE_VERIFICATION if KEXEC_FILE_IMAGE_FMT + select MODULE_SIG_FORMAT if KEXEC_FILE_ELF_FMT ---help--- This option makes kernel signature verification mandatory for the kexec_file_load() syscall. diff --git a/arch/arm64/include/asm/kexec_file.h b/arch/arm64/include/asm/kexec_file.h index 5df899aa0d2e..eaf2adc1121c 100644 --- a/arch/arm64/include/asm/kexec_file.h +++ b/arch/arm64/include/asm/kexec_file.h @@ -2,6 +2,7 @@ #define _ASM_KEXEC_FILE_H extern struct kexec_file_ops kexec_image_ops; +extern struct kexec_file_ops kexec_elf64_ops; /** * struct arm64_image_header - arm64 kernel image header. diff --git a/arch/arm64/kernel/Makefile b/arch/arm64/kernel/Makefile index a1161bab6810..1463337160ea 100644 --- a/arch/arm64/kernel/Makefile +++ b/arch/arm64/kernel/Makefile @@ -52,6 +52,7 @@ arm64-obj-$(CONFIG_KEXEC_CORE) += machine_kexec.o relocate_kernel.o \ cpu-reset.o arm64-obj-$(CONFIG_KEXEC_FILE) += machine_kexec_file.o arm64-obj-$(CONFIG_KEXEC_FILE_IMAGE_FMT) += kexec_image.o +arm64-obj-$(CONFIG_KEXEC_FILE_ELF_FMT) += kexec_elf.o arm64-obj-$(CONFIG_ARM64_RELOC_TEST) += arm64-reloc-test.o arm64-reloc-test-y := reloc_test_core.o reloc_test_syms.o arm64-obj-$(CONFIG_CRASH_DUMP) += crash_dump.o diff --git a/arch/arm64/kernel/kexec_elf.c b/arch/arm64/kernel/kexec_elf.c new file mode 100644 index 000000000000..7bd3c1e1f65a --- /dev/null +++ b/arch/arm64/kernel/kexec_elf.c @@ -0,0 +1,216 @@ +/* + * Kexec vmlinux loader + + * Copyright (C) 2017 Linaro Limited + * Authors: AKASHI Takahiro + * + * 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. + */ + +#define pr_fmt(fmt) "kexec_file(elf): " fmt + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +static int elf64_probe(const char *buf, unsigned long len) +{ + struct elfhdr ehdr; + + /* Check for magic and architecture */ + memcpy(&ehdr, buf, sizeof(ehdr)); + if (memcmp(ehdr.e_ident, ELFMAG, SELFMAG) || + (elf16_to_cpu(&ehdr, ehdr.e_machine) != EM_AARCH64)) + return -ENOEXEC; + + return 0; +} + +static int elf_exec_load(struct kimage *image, struct elfhdr *ehdr, + struct elf_info *elf_info, + unsigned long *kernel_load_addr) +{ + struct kexec_buf kbuf; + const struct elf_phdr *phdr; + const struct arm64_image_header *h; + unsigned long text_offset, rand_offset; + unsigned long page_offset, phys_offset; + int first_segment, i, ret = -ENOEXEC; + + kbuf.image = image; + if (image->type == KEXEC_TYPE_CRASH) { + kbuf.buf_min = crashk_res.start; + kbuf.buf_max = crashk_res.end + 1; + } else { + kbuf.buf_min = 0; + kbuf.buf_max = ULONG_MAX; + } + kbuf.top_down = 0; + + /* Load PT_LOAD segments. */ + for (i = 0, first_segment = 1; i < ehdr->e_phnum; i++) { + phdr = &elf_info->proghdrs[i]; + if (phdr->p_type != PT_LOAD) + continue; + + kbuf.buffer = (void *) elf_info->buffer + phdr->p_offset; + kbuf.bufsz = min(phdr->p_filesz, phdr->p_memsz); + kbuf.memsz = phdr->p_memsz; + kbuf.buf_align = phdr->p_align; + + if (first_segment) { + /* + * Identify TEXT_OFFSET: + * When CONFIG_ARM64_RANDOMIZE_TEXT_OFFSET=y the image + * header could be offset in the elf segment. The linker + * script sets ehdr->e_entry to the start of text. + * + * NOTE: In v3.16 or older, h->text_offset is 0, + * so use the default, 0x80000 + */ + rand_offset = ehdr->e_entry - phdr->p_vaddr; + h = (struct arm64_image_header *) + (elf_info->buffer + phdr->p_offset + + rand_offset); + + if (!arm64_header_check_magic(h)) + goto out; + + if (h->image_size) + text_offset = le64_to_cpu(h->text_offset); + else + text_offset = 0x80000; + + /* Adjust kernel segment with TEXT_OFFSET */ + kbuf.memsz += text_offset - rand_offset; + + ret = kexec_add_buffer(&kbuf); + if (ret) + goto out; + + image->segment[image->nr_segments - 1].mem + += text_offset - rand_offset; + image->segment[image->nr_segments - 1].memsz + -= text_offset - rand_offset; + + *kernel_load_addr = kbuf.mem + text_offset; + + /* for succeeding segmemts */ + page_offset = ALIGN_DOWN(phdr->p_vaddr, SZ_2M); + phys_offset = kbuf.mem; + + first_segment = 0; + } else { + /* Calculate physical address */ + kbuf.mem = phdr->p_vaddr - page_offset + phys_offset; + + ret = kexec_add_segment(&kbuf); + if (ret) + goto out; + } + } + +out: + return ret; +} + +static void *elf64_load(struct kimage *image, char *kernel_buf, + unsigned long kernel_len, char *initrd, + unsigned long initrd_len, char *cmdline, + unsigned long cmdline_len) +{ + struct elfhdr ehdr; + struct elf_info elf_info; + unsigned long kernel_load_addr; + int ret; + + /* Create elf core header segment */ + ret = load_crashdump_segments(image); + if (ret) + goto out; + + /* Load the kernel */ + ret = build_elf_exec_info(kernel_buf, kernel_len, &ehdr, &elf_info); + if (ret) + goto out; + + ret = elf_exec_load(image, &ehdr, &elf_info, &kernel_load_addr); + if (ret) + goto out; + pr_debug("Loaded the kernel at 0x%lx\n", kernel_load_addr); + + /* Load additional data */ + ret = load_other_segments(image, kernel_load_addr, + initrd, initrd_len, cmdline, cmdline_len); + +out: + elf_free_info(&elf_info); + + return ERR_PTR(ret); +} + +#ifdef CONFIG_KEXEC_VERIFY_SIG +/* + * The file format is the exact same as module signing: + * := + + + * := + + */ +static int elf64_verify_sig(const char *kernel, unsigned long kernel_len) +{ + const size_t marker_len = sizeof(MODULE_SIG_STRING) - 1; + const struct module_signature *sig; + size_t file_len = kernel_len; + size_t sig_len; + const void *p; + int rc; + + if (kernel_len <= marker_len + sizeof(*sig)) + return -ENOENT; + + /* Check for marker */ + p = kernel + kernel_len - marker_len; + if (memcmp(p, MODULE_SIG_STRING, marker_len)) { + pr_err("probably the kernel is not signed.\n"); + return -ENOENT; + } + + /* Validate signature */ + sig = (const struct module_signature *) (p - sizeof(*sig)); + file_len -= marker_len; + + rc = validate_module_sig(sig, kernel_len - marker_len); + if (rc) { + pr_err("signature is not valid\n"); + return rc; + } + + /* Verify kernel with signature */ + sig_len = be32_to_cpu(sig->sig_len); + p -= sig_len + sizeof(*sig); + file_len -= sig_len + sizeof(*sig); + + rc = verify_pkcs7_signature(kernel, p - (void *)kernel, p, sig_len, + NULL, VERIFYING_MODULE_SIGNATURE, + NULL, NULL); + + return rc; +} +#endif + +struct kexec_file_ops kexec_elf64_ops = { + .probe = elf64_probe, + .load = elf64_load, +#ifdef CONFIG_KEXEC_VERIFY_SIG + .verify_sig = elf64_verify_sig, +#endif +}; diff --git a/arch/arm64/kernel/machine_kexec_file.c b/arch/arm64/kernel/machine_kexec_file.c index ab3b19d51727..cb1f24d98f87 100644 --- a/arch/arm64/kernel/machine_kexec_file.c +++ b/arch/arm64/kernel/machine_kexec_file.c @@ -31,6 +31,9 @@ static struct kexec_file_ops *kexec_file_loaders[] = { #ifdef CONFIG_KEXEC_FILE_IMAGE_FMT &kexec_image_ops, #endif +#ifdef CONFIG_KEXEC_FILE_ELF_FMT + &kexec_elf64_ops, +#endif }; int arch_kexec_kernel_image_probe(struct kimage *image, void *buf,