From patchwork Mon Jan 28 12:44:42 2019 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Alexander Graf X-Patchwork-Id: 156731 Delivered-To: patch@linaro.org Received: by 2002:a02:48:0:0:0:0:0 with SMTP id 69csp3388037jaa; Mon, 28 Jan 2019 04:47:42 -0800 (PST) X-Google-Smtp-Source: ALg8bN4tCMP3ZdqVDFOyIMdxDcanLfg/9oFeWD7AXpX1C4aOF5O/Gl/S4WylipZqTX3nRiqVBMiW X-Received: by 2002:a7b:cb86:: with SMTP id m6mr16937701wmi.61.1548679662862; Mon, 28 Jan 2019 04:47:42 -0800 (PST) ARC-Seal: i=1; a=rsa-sha256; t=1548679662; cv=none; d=google.com; s=arc-20160816; b=wpaNyDTysHV+SuUNm46tmCH8kGFjY+FvCVGcf3BnQfB2pFTQJktnTrXoapkJmKwllg OC6s7VGj1hgU/OBviuvngVYZwHWmAN6E4mUSpbkF28Z801fZsFIdA+RC6z2VK/BYct2q TZ1ryS+OozUCscPTjENS9B7Ts7VCCD3/BzfbGCukl1nNOVZQXfKE7xAafx1l1jsn+MDj uy+/6dMdLLpo6TTmpESzmCgBi/LVG0MciKZX7LRtIBDke90Y52EqiufwL+DSsDACv8kQ lfDKWJ0neoAqZl6PWB+ZYgy3YGagddvMEP8ZvjVDlLb9ZFvXASjCYQbGVaojJ0ZgpahI GUAQ== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=arc-20160816; h=sender:errors-to:content-transfer-encoding:mime-version:cc:reply-to :list-subscribe:list-help:list-post:list-archive:list-unsubscribe :list-id:precedence:references:in-reply-to:message-id:date:subject :to:from; bh=yuJGxVv316V1tKYu5myVyoqhA6zrZQfd83B6INkf9oY=; b=wsMbtzfea5b8gsVF8RosJ49mQzYOPia8RpmzGA24kubwMznh7pSsYtLlC93tzj/uY0 bd+l8IPCNvJuw6CiGkBeBIg/Qy4Xpz8d+YvztebTNQAKIxX6Qag/SNmxfjO3oCLPAXYc cdp4s+5GW8MTczdw4xymzx2vKisZ05CsZTjrtMTjiFGNfxIRsqndsSk5slmM68Agg82n vMUt65Cf7oPMSeIF1wDMWiaAoGvA/0Osla9TLg5ZQndXHNHh2/bn+SEuboIaqu/VsWxc aBWzjN4RUFs6ua0arwDkde9H1irE5BGijpRyTRP2A0eF4On9QipCDxZxWt1QDIVWHFN5 6HBA== ARC-Authentication-Results: i=1; mx.google.com; spf=pass (google.com: domain of grub-devel-bounces+patch=linaro.org@gnu.org designates 209.51.188.17 as permitted sender) smtp.mailfrom="grub-devel-bounces+patch=linaro.org@gnu.org" Return-Path: Received: from lists.gnu.org (lists.gnu.org. [209.51.188.17]) by mx.google.com with ESMTPS id j11si79890270wrx.187.2019.01.28.04.47.42 for (version=TLS1 cipher=AES128-SHA bits=128/128); Mon, 28 Jan 2019 04:47:42 -0800 (PST) Received-SPF: pass (google.com: domain of grub-devel-bounces+patch=linaro.org@gnu.org designates 209.51.188.17 as permitted sender) client-ip=209.51.188.17; Authentication-Results: mx.google.com; spf=pass (google.com: domain of grub-devel-bounces+patch=linaro.org@gnu.org designates 209.51.188.17 as permitted sender) smtp.mailfrom="grub-devel-bounces+patch=linaro.org@gnu.org" Received: from localhost ([127.0.0.1]:59252 helo=lists.gnu.org) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1go6KD-00011F-Ox for patch@linaro.org; Mon, 28 Jan 2019 07:47:41 -0500 Received: from eggs.gnu.org ([209.51.188.92]:49089) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1go6Hn-0007ds-EF for grub-devel@gnu.org; Mon, 28 Jan 2019 07:45:14 -0500 Received: from Debian-exim by eggs.gnu.org with spam-scanned (Exim 4.71) (envelope-from ) id 1go6Hk-0003t4-Uc for grub-devel@gnu.org; Mon, 28 Jan 2019 07:45:11 -0500 Received: from mx2.suse.de ([195.135.220.15]:60076 helo=mx1.suse.de) by eggs.gnu.org with esmtps (TLS1.0:DHE_RSA_AES_256_CBC_SHA1:32) (Exim 4.71) (envelope-from ) id 1go6Hk-0003U1-Cv for grub-devel@gnu.org; Mon, 28 Jan 2019 07:45:08 -0500 X-Virus-Scanned: by amavisd-new at test-mx.suse.de Received: from relay2.suse.de (unknown [195.135.220.254]) by mx1.suse.de (Postfix) with ESMTP id 9EDB4AE80; Mon, 28 Jan 2019 12:44:48 +0000 (UTC) From: Alexander Graf To: grub-devel@gnu.org Subject: [PATCH v5 06/11] RISC-V: Add Linux load logic Date: Mon, 28 Jan 2019 13:44:42 +0100 Message-Id: <20190128124447.81028-7-agraf@suse.de> X-Mailer: git-send-email 2.12.3 In-Reply-To: <20190128124447.81028-1-agraf@suse.de> References: <20190128124447.81028-1-agraf@suse.de> X-detected-operating-system: by eggs.gnu.org: GNU/Linux 2.2.x-3.x (no timestamps) [generic] X-Received-From: 195.135.220.15 X-BeenThere: grub-devel@gnu.org X-Mailman-Version: 2.1.21 Precedence: list List-Id: The development of GNU GRUB List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Reply-To: The development of GNU GRUB Cc: rickchen36@gmail.com, greentime , Andreas Schwab , David Abdurachmanov , leif.lindholm@linaro.org, atish.patra@wdc.com, Michael Chang , Alistair Francis , Lukas Auer , Paul Walmsley , Bin Meng , Daniel Kiper MIME-Version: 1.0 Errors-To: grub-devel-bounces+patch=linaro.org@gnu.org Sender: "Grub-devel" We currently only support to run grub on RISC-V as UEFI payload. Ideally, we also only want to support running Linux underneath as UEFI payload. Prepare that with a Linux boot case that is not enabled in Linux yet. At least it will give people something to test against when they enable the Linux UEFI port. This implementation will eventually have to get merged with the ARM one, as they really do almost the same thing. But for that, let's wait for the ARM one to get cleaned up. We can then merge the two by generalizing the clean ARM one. Signed-off-by: Alexander Graf Reviewed-by: Alistair Francis --- v1 -> v2: - adapt to new grub_open_file() API - adapt to new grub_create_loader_cmdline() API v3 -> v4: - Change copyright from 2013 to 2018 - Coding style fixes v4 -> v5: - Use GRUB_EFI_LINUX_FDT_EXTRA_SPACE - Fix whitespace - s/failure/fail --- grub-core/loader/riscv/linux.c | 350 +++++++++++++++++++++++++++++++++++++++++ include/grub/riscv32/linux.h | 41 +++++ include/grub/riscv64/linux.h | 43 +++++ 3 files changed, 434 insertions(+) create mode 100644 grub-core/loader/riscv/linux.c create mode 100644 include/grub/riscv32/linux.h create mode 100644 include/grub/riscv64/linux.h -- 2.12.3 _______________________________________________ Grub-devel mailing list Grub-devel@gnu.org https://lists.gnu.org/mailman/listinfo/grub-devel diff --git a/grub-core/loader/riscv/linux.c b/grub-core/loader/riscv/linux.c new file mode 100644 index 000000000..8879a0612 --- /dev/null +++ b/grub-core/loader/riscv/linux.c @@ -0,0 +1,350 @@ +/* + * GRUB -- GRand Unified Bootloader + * Copyright (C) 2018 Free Software Foundation, Inc. + * + * GRUB is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * GRUB 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 GRUB. If not, see . + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +GRUB_MOD_LICENSE ("GPLv3+"); + +static grub_dl_t my_mod; +static int loaded; + +static void *kernel_addr; +static grub_uint64_t kernel_size; + +static char *linux_args; +static grub_uint32_t cmdline_size; + +static grub_addr_t initrd_start; +static grub_addr_t initrd_end; + +grub_err_t +grub_arch_efi_linux_check_image (struct linux_riscv_kernel_header * lh) +{ + if (lh->magic != GRUB_LINUX_RISCV_MAGIC_SIGNATURE) + return grub_error(GRUB_ERR_BAD_OS, "invalid magic number"); + + if ((lh->code0 & 0xffff) != GRUB_PE32_MAGIC) + return grub_error (GRUB_ERR_NOT_IMPLEMENTED_YET, + N_("plain image kernel not supported - rebuild with CONFIG_(U)EFI_STUB enabled")); + + grub_dprintf ("linux", "UEFI stub kernel:\n"); + grub_dprintf ("linux", "PE/COFF header @ %08x\n", lh->hdr_offset); + + return GRUB_ERR_NONE; +} + +static grub_err_t +finalize_params_linux (void) +{ + int node, retval; + void *fdt; + + fdt = grub_fdt_load (GRUB_EFI_LINUX_FDT_EXTRA_SPACE); + + if (!fdt) + goto fail; + + node = grub_fdt_find_subnode (fdt, 0, "chosen"); + if (node < 0) + node = grub_fdt_add_subnode (fdt, 0, "chosen"); + + if (node < 1) + goto fail; + + /* Set initrd info */ + if (initrd_start && initrd_end > initrd_start) + { + grub_dprintf ("linux", "Initrd @ %p-%p\n", + (void *) initrd_start, (void *) initrd_end); + + retval = grub_fdt_set_prop64 (fdt, node, "linux,initrd-start", + initrd_start); + if (retval) + goto fail; + retval = grub_fdt_set_prop64 (fdt, node, "linux,initrd-end", + initrd_end); + if (retval) + goto fail; + } + + if (grub_fdt_install () != GRUB_ERR_NONE) + goto fail; + + return GRUB_ERR_NONE; + + fail: + grub_fdt_unload (); + return grub_error(GRUB_ERR_BAD_OS, "failed to install/update FDT"); +} + +grub_err_t +grub_arch_efi_linux_boot_image (grub_addr_t addr, grub_size_t size, char *args) +{ + grub_efi_memory_mapped_device_path_t *mempath; + grub_efi_handle_t image_handle; + grub_efi_boot_services_t *b; + grub_efi_status_t status; + grub_efi_loaded_image_t *loaded_image; + int len; + + mempath = grub_malloc (2 * sizeof (grub_efi_memory_mapped_device_path_t)); + if (!mempath) + return grub_errno; + + mempath[0].header.type = GRUB_EFI_HARDWARE_DEVICE_PATH_TYPE; + mempath[0].header.subtype = GRUB_EFI_MEMORY_MAPPED_DEVICE_PATH_SUBTYPE; + mempath[0].header.length = grub_cpu_to_le16_compile_time (sizeof (*mempath)); + mempath[0].memory_type = GRUB_EFI_LOADER_DATA; + mempath[0].start_address = addr; + mempath[0].end_address = addr + size; + + mempath[1].header.type = GRUB_EFI_END_DEVICE_PATH_TYPE; + mempath[1].header.subtype = GRUB_EFI_END_ENTIRE_DEVICE_PATH_SUBTYPE; + mempath[1].header.length = sizeof (grub_efi_device_path_t); + + b = grub_efi_system_table->boot_services; + status = b->load_image (0, grub_efi_image_handle, + (grub_efi_device_path_t *) mempath, + (void *) addr, size, &image_handle); + if (status != GRUB_EFI_SUCCESS) + return grub_error (GRUB_ERR_BAD_OS, "cannot load image"); + + grub_dprintf ("linux", "linux command line: '%s'\n", args); + + /* Convert command line to UCS-2 */ + loaded_image = grub_efi_get_loaded_image (image_handle); + loaded_image->load_options_size = len = + (grub_strlen (args) + 1) * sizeof (grub_efi_char16_t); + loaded_image->load_options = + grub_efi_allocate_any_pages (GRUB_EFI_BYTES_TO_PAGES (loaded_image->load_options_size)); + if (!loaded_image->load_options) + return grub_errno; + + loaded_image->load_options_size = + 2 * grub_utf8_to_utf16 (loaded_image->load_options, len, + (grub_uint8_t *) args, len, NULL); + + grub_dprintf ("linux", "starting image %p\n", image_handle); + status = b->start_image (image_handle, 0, NULL); + + /* When successful, not reached */ + b->unload_image (image_handle); + grub_efi_free_pages ((grub_addr_t) loaded_image->load_options, + GRUB_EFI_BYTES_TO_PAGES (loaded_image->load_options_size)); + + return grub_errno; +} + +static grub_err_t +grub_linux_boot (void) +{ + if (finalize_params_linux () != GRUB_ERR_NONE) + return grub_errno; + + return (grub_arch_efi_linux_boot_image((grub_addr_t)kernel_addr, + kernel_size, linux_args)); +} + +static grub_err_t +grub_linux_unload (void) +{ + grub_dl_unref (my_mod); + loaded = 0; + if (initrd_start) + grub_efi_free_pages ((grub_efi_physical_address_t) initrd_start, + GRUB_EFI_BYTES_TO_PAGES (initrd_end - initrd_start)); + initrd_start = initrd_end = 0; + grub_free (linux_args); + if (kernel_addr) + grub_efi_free_pages ((grub_addr_t) kernel_addr, + GRUB_EFI_BYTES_TO_PAGES (kernel_size)); + grub_fdt_unload (); + return GRUB_ERR_NONE; +} + +static grub_err_t +grub_cmd_initrd (grub_command_t cmd __attribute__ ((unused)), + int argc, char *argv[]) +{ + struct grub_linux_initrd_context initrd_ctx = { 0, 0, 0 }; + int initrd_size, initrd_pages; + void *initrd_mem = NULL; + + if (argc == 0) + { + grub_error (GRUB_ERR_BAD_ARGUMENT, N_("filename expected")); + goto fail; + } + + if (!loaded) + { + grub_error (GRUB_ERR_BAD_ARGUMENT, + N_("you need to load the kernel first")); + goto fail; + } + + if (grub_initrd_init (argc, argv, &initrd_ctx)) + goto fail; + + initrd_size = grub_get_initrd_size (&initrd_ctx); + grub_dprintf ("linux", "Loading initrd\n"); + + initrd_pages = (GRUB_EFI_BYTES_TO_PAGES (initrd_size)); + initrd_mem = grub_efi_allocate_any_pages (initrd_pages); + if (!initrd_mem) + { + grub_error (GRUB_ERR_OUT_OF_MEMORY, N_("out of memory")); + goto fail; + } + + if (grub_initrd_load (&initrd_ctx, argv, initrd_mem)) + goto fail; + + initrd_start = (grub_addr_t) initrd_mem; + initrd_end = initrd_start + initrd_size; + grub_dprintf ("linux", "[addr=%p, size=0x%x]\n", + (void *) initrd_start, initrd_size); + + fail: + grub_initrd_close (&initrd_ctx); + if (initrd_mem && !initrd_start) + grub_efi_free_pages ((grub_addr_t) initrd_mem, initrd_pages); + + return grub_errno; +} + +static grub_err_t +grub_cmd_linux (grub_command_t cmd __attribute__ ((unused)), + int argc, char *argv[]) +{ + grub_file_t file = 0; + struct linux_riscv_kernel_header lh; + + grub_dl_ref (my_mod); + + if (argc == 0) + { + grub_error (GRUB_ERR_BAD_ARGUMENT, N_("filename expected")); + goto fail; + } + + file = grub_file_open (argv[0], GRUB_FILE_TYPE_LINUX_KERNEL); + if (!file) + goto fail; + + kernel_size = grub_file_size (file); + + if (grub_file_read (file, &lh, sizeof (lh)) < (long) sizeof (lh)) + return grub_errno; + + if (grub_arch_efi_linux_check_image (&lh) != GRUB_ERR_NONE) + goto fail; + + grub_loader_unset(); + + grub_dprintf ("linux", "kernel file size: %lld\n", (long long) kernel_size); + kernel_addr = grub_efi_allocate_any_pages (GRUB_EFI_BYTES_TO_PAGES (kernel_size)); + grub_dprintf ("linux", "kernel numpages: %lld\n", + (long long) GRUB_EFI_BYTES_TO_PAGES (kernel_size)); + if (!kernel_addr) + { + grub_error (GRUB_ERR_OUT_OF_MEMORY, N_("out of memory")); + goto fail; + } + + grub_file_seek (file, 0); + if (grub_file_read (file, kernel_addr, kernel_size) + < (grub_int64_t) kernel_size) + { + if (!grub_errno) + grub_error (GRUB_ERR_BAD_OS, N_("premature end of file %s"), argv[0]); + goto fail; + } + + grub_dprintf ("linux", "kernel @ %p\n", kernel_addr); + + cmdline_size = grub_loader_cmdline_size (argc, argv) + sizeof (LINUX_IMAGE); + linux_args = grub_malloc (cmdline_size); + if (!linux_args) + { + grub_error (GRUB_ERR_OUT_OF_MEMORY, N_("out of memory")); + goto fail; + } + grub_memcpy (linux_args, LINUX_IMAGE, sizeof (LINUX_IMAGE)); + grub_create_loader_cmdline (argc, argv, + linux_args + sizeof (LINUX_IMAGE) - 1, + cmdline_size, + GRUB_VERIFY_KERNEL_CMDLINE); + + if (grub_errno == GRUB_ERR_NONE) + { + grub_loader_set (grub_linux_boot, grub_linux_unload, 0); + loaded = 1; + } + + fail: + if (file) + grub_file_close (file); + + if (grub_errno != GRUB_ERR_NONE) + { + grub_dl_unref (my_mod); + loaded = 0; + } + + if (linux_args && !loaded) + grub_free (linux_args); + + if (kernel_addr && !loaded) + grub_efi_free_pages ((grub_addr_t) kernel_addr, + GRUB_EFI_BYTES_TO_PAGES (kernel_size)); + + return grub_errno; +} + +static grub_command_t cmd_linux, cmd_initrd; + +GRUB_MOD_INIT (linux) +{ + cmd_linux = grub_register_command ("linux", grub_cmd_linux, 0, + N_("Load Linux.")); + cmd_initrd = grub_register_command ("initrd", grub_cmd_initrd, 0, + N_("Load initrd.")); + my_mod = mod; +} + +GRUB_MOD_FINI (linux) +{ + grub_unregister_command (cmd_linux); + grub_unregister_command (cmd_initrd); +} diff --git a/include/grub/riscv32/linux.h b/include/grub/riscv32/linux.h new file mode 100644 index 000000000..b8ed39407 --- /dev/null +++ b/include/grub/riscv32/linux.h @@ -0,0 +1,41 @@ +/* + * GRUB -- GRand Unified Bootloader + * Copyright (C) 2018 Free Software Foundation, Inc. + * + * GRUB is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * GRUB 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 GRUB. If not, see . + */ + +#ifndef GRUB_RISCV32_LINUX_HEADER +#define GRUB_RISCV32_LINUX_HEADER 1 + +#define GRUB_LINUX_RISCV_MAGIC_SIGNATURE 0x52534356 /* 'RSCV' */ + +/* From linux/Documentation/riscv/booting.txt */ +struct linux_riscv_kernel_header +{ + grub_uint32_t code0; /* Executable code */ + grub_uint32_t code1; /* Executable code */ + grub_uint64_t text_offset; /* Image load offset */ + grub_uint64_t res0; /* reserved */ + grub_uint64_t res1; /* reserved */ + grub_uint64_t res2; /* reserved */ + grub_uint64_t res3; /* reserved */ + grub_uint64_t res4; /* reserved */ + grub_uint32_t magic; /* Magic number, little endian, "RSCV" */ + grub_uint32_t hdr_offset; /* Offset of PE/COFF header */ +}; + +# define linux_arch_kernel_header linux_riscv_kernel_header + +#endif /* ! GRUB_RISCV32_LINUX_HEADER */ diff --git a/include/grub/riscv64/linux.h b/include/grub/riscv64/linux.h new file mode 100644 index 000000000..29140e45e --- /dev/null +++ b/include/grub/riscv64/linux.h @@ -0,0 +1,43 @@ +/* + * GRUB -- GRand Unified Bootloader + * Copyright (C) 2018 Free Software Foundation, Inc. + * + * GRUB is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * GRUB 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 GRUB. If not, see . + */ + +#ifndef GRUB_RISCV64_LINUX_HEADER +#define GRUB_RISCV64_LINUX_HEADER 1 + +#define GRUB_LINUX_RISCV_MAGIC_SIGNATURE 0x52534356 /* 'RSCV' */ + +#define GRUB_EFI_PE_MAGIC 0x5A4D + +/* From linux/Documentation/riscv/booting.txt */ +struct linux_riscv_kernel_header +{ + grub_uint32_t code0; /* Executable code */ + grub_uint32_t code1; /* Executable code */ + grub_uint64_t text_offset; /* Image load offset */ + grub_uint64_t res0; /* reserved */ + grub_uint64_t res1; /* reserved */ + grub_uint64_t res2; /* reserved */ + grub_uint64_t res3; /* reserved */ + grub_uint64_t res4; /* reserved */ + grub_uint32_t magic; /* Magic number, little endian, "RSCV" */ + grub_uint32_t hdr_offset; /* Offset of PE/COFF header */ +}; + +# define linux_arch_kernel_header linux_riscv_kernel_header + +#endif /* ! GRUB_RISCV64_LINUX_HEADER */