All of lore.kernel.org
 help / color / mirror / Atom feed
From: Chester Lin <clin@suse.com>
To: "grub-devel@gnu.org" <grub-devel@gnu.org>
Cc: "agraf@csgraf.de" <agraf@csgraf.de>,
	"daniel.kiper@oracle.com" <daniel.kiper@oracle.com>,
	"alistair.francis@wdc.com" <alistair.francis@wdc.com>,
	Chester Lin <clin@suse.com>
Subject: [PATCH 2/2] RISC-V: Implement linux image loader
Date: Thu, 16 Jan 2020 10:21:47 +0000	[thread overview]
Message-ID: <20200116102106.25136-3-clin@suse.com> (raw)
In-Reply-To: <20200116102106.25136-1-clin@suse.com>

An image loader for booting RISC-V linux. The major idea is based on
loader/arm64/linux.c but still follows the the current boot procedure of
RISC-V Linux. [boot-entry = dram-base + text-offset]

Signed-off-by: Chester Lin <clin@suse.com>
---
 grub-core/loader/riscv/linux.c | 311 ++++++++++++++++++++++++++++++++-
 1 file changed, 307 insertions(+), 4 deletions(-)

diff --git a/grub-core/loader/riscv/linux.c b/grub-core/loader/riscv/linux.c
index d17c488e1..e1960356e 100644
--- a/grub-core/loader/riscv/linux.c
+++ b/grub-core/loader/riscv/linux.c
@@ -15,21 +15,240 @@
  *  You should have received a copy of the GNU General Public License
  *  along with GRUB.  If not, see <http://www.gnu.org/licenses/>.
  */
-
+#include <grub/charset.h>
 #include <grub/command.h>
-#include <grub/dl.h>
+#include <grub/err.h>
+#include <grub/file.h>
+#include <grub/fdt.h>
+#include <grub/linux.h>
+#include <grub/loader.h>
+#include <grub/mm.h>
+#include <grub/types.h>
+#include <grub/cpu/linux.h>
+#include <grub/efi/efi.h>
+#include <grub/efi/fdtload.h>
+#include <grub/efi/memory.h>
+#include <grub/efi/pe32.h>
+#include <grub/i18n.h>
 #include <grub/lib/cmdline.h>
+#include <grub/verify.h>
 
 GRUB_MOD_LICENSE ("GPLv3+");
 
+static grub_dl_t my_mod;
+static int loaded;
+
+static void *kernel_addr;
+static grub_size_t kernel_size;
+
+static char *linux_args;
+static grub_size_t cmdline_size;
+
+static grub_addr_t initrd_start;
+static grub_addr_t initrd_end;
+
+#if __riscv_xlen == 64
+#define INITRD_MAX_ADDRESS_OFFSET (32ULL * 1024 * 1024 * 1024)
+#else
+#define INITRD_MAX_ADDRESS_OFFSET (512U * 1024 * 1024)
+#endif
+
+#define get_hartid(__v)			\
+{					\
+  __asm__ __volatile__ ("mv %0, tp"	\
+			: "=r" (__v) :	\
+			: "memory");	\
+}
+
+typedef void (*kernel_entry_t) (int, void *);
+
+grub_err_t
+grub_arch_efi_linux_check_image (struct linux_arch_kernel_header * lh)
+{
+  if (lh->magic != GRUB_LINUX_RISCV_MAGIC_SIGNATURE)
+    return grub_error(GRUB_ERR_BAD_OS, "invalid magic number");
+
+  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
+prepare_fdt (void **fdt_addr)
+{
+  int node, retval;
+  void *fdt;
+
+  fdt = grub_fdt_load (GRUB_EFI_LINUX_FDT_EXTRA_SPACE);
+
+  if (!fdt)
+    goto failure;
+
+  node = grub_fdt_find_subnode (fdt, 0, "chosen");
+  if (node < 0)
+    node = grub_fdt_add_subnode (fdt, 0, "chosen");
+
+  if (node < 1)
+    goto failure;
+
+  /* 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 failure;
+
+      retval = grub_fdt_set_prop64 (fdt, node, "linux,initrd-end",
+				    initrd_end);
+      if (retval)
+	goto failure;
+    }
+
+  if (grub_fdt_install() != GRUB_ERR_NONE)
+    goto failure;
+
+  *fdt_addr = fdt;
+
+  return GRUB_ERR_NONE;
+
+failure:
+  grub_fdt_unload();
+  return grub_error(GRUB_ERR_BAD_OS, "failed to install/update FDT");
+}
+
+static grub_err_t
+grub_linux_boot (void)
+{
+  register unsigned long hartid;
+  kernel_entry_t boot_kernel;
+  void *fdt_addr = NULL;
+
+  if(!kernel_addr)
+    return grub_error(GRUB_ERR_BAD_OS, "kernel image is not loaded");
+
+  boot_kernel = (kernel_entry_t) kernel_addr;
+
+  if(prepare_fdt(&fdt_addr) != GRUB_ERR_NONE)
+	goto failure;
+
+  get_hartid(hartid);
+
+  grub_dprintf ("linux", "Boot Hart: %lu\n", hartid);
+
+  boot_kernel(hartid, fdt_addr);
+
+failure:
+  return grub_error(GRUB_ERR_BAD_OS, "failed to boot kernel");
+}
+
+
+
+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;
+}
+
+/*
+ * This function returns a pointer to a legally allocated initrd buffer,
+ * or NULL if unsuccessful
+ */
+static void *
+allocate_kernel_mem (int kernel_pages, grub_addr_t offset)
+{
+  grub_addr_t max_addr;
+
+  if (grub_efi_get_ram_base (&max_addr) != GRUB_ERR_NONE)
+    return NULL;
+
+  max_addr += offset;
+
+  return grub_efi_allocate_pages_real (max_addr, kernel_pages,
+				       GRUB_EFI_ALLOCATE_ADDRESS,
+				       GRUB_EFI_LOADER_DATA);
+}
+
+static void *
+allocate_initrd_mem (int initrd_pages)
+{
+  grub_addr_t max_addr;
+
+  if (grub_efi_get_ram_base (&max_addr) != GRUB_ERR_NONE)
+    return NULL;
+
+  max_addr += INITRD_MAX_ADDRESS_OFFSET - 1;
+
+  return grub_efi_allocate_pages_real (max_addr, initrd_pages,
+				       GRUB_EFI_ALLOCATE_MAX_ADDRESS,
+				       GRUB_EFI_LOADER_DATA);
+}
+
 static grub_err_t
 grub_cmd_initrd (grub_command_t cmd __attribute__ ((unused)),
 		 int argc __attribute__ ((unused)),
 		 char *argv[] __attribute__ ((unused)))
 {
-  grub_error (GRUB_ERR_NOT_IMPLEMENTED_YET, N_("Linux not supported yet"));
+  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 = allocate_initrd_mem (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;
+
+ 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
@@ -37,9 +256,92 @@ grub_cmd_linux (grub_command_t cmd __attribute__ ((unused)),
 		int argc __attribute__ ((unused)),
 		char *argv[] __attribute__ ((unused)))
 {
-  grub_error (GRUB_ERR_NOT_IMPLEMENTED_YET, N_("Linux not supported yet"));
+  grub_file_t file = 0;
+  struct linux_arch_kernel_header lh;
+  grub_err_t err;
+
+  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();
+
+  kernel_addr = allocate_kernel_mem (
+		GRUB_EFI_BYTES_TO_PAGES (kernel_size), lh.text_offset);
+
+  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_ssize_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));
+  err = grub_create_loader_cmdline (argc, argv,
+				    linux_args + sizeof (LINUX_IMAGE) - 1,
+				    cmdline_size,
+				    GRUB_VERIFY_KERNEL_CMDLINE);
+  if (err)
+    goto fail;
+
+  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;
@@ -50,6 +352,7 @@ GRUB_MOD_INIT (linux)
 				     N_("Load Linux."));
   cmd_initrd = grub_register_command ("initrd", grub_cmd_initrd, 0,
 				      N_("Load initrd."));
+  my_mod = mod;
 }
 
 GRUB_MOD_FINI (linux)
-- 
2.24.0



  parent reply	other threads:[~2020-01-16 14:37 UTC|newest]

Thread overview: 9+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2020-01-16 10:21 [PATCH 0/2] Implement a grub loader for RISC-V LINUX Chester Lin
2020-01-16 10:21 ` [PATCH 1/2] RISC-V: Correct linux headers' definitions Chester Lin
2020-01-16 10:21 ` Chester Lin [this message]
2020-01-16 12:06 ` [PATCH 0/2] Implement a grub loader for RISC-V LINUX Alexander Graf
2020-01-16 13:14   ` Atish Patra
2020-01-16 13:27     ` Alexander Graf
2020-01-17  3:20       ` Chester Lin
2020-01-17  9:24         ` Atish Patra
2020-01-17 12:51     ` [EXTERNAL] " Leif Lindholm

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20200116102106.25136-3-clin@suse.com \
    --to=clin@suse.com \
    --cc=agraf@csgraf.de \
    --cc=alistair.francis@wdc.com \
    --cc=daniel.kiper@oracle.com \
    --cc=grub-devel@gnu.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.