grub-devel
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

Re: [PATCH 6/7] add ARM Linux loader


From: Francesco Lavra
Subject: Re: [PATCH 6/7] add ARM Linux loader
Date: Mon, 01 Apr 2013 18:18:17 +0200
User-agent: Mozilla/5.0 (X11; Linux i686; rv:7.0.1) Gecko/20110929 Thunderbird/7.0.1

On 03/24/2013 06:01 PM, Leif Lindholm wrote:
> === added directory 'grub-core/loader/arm'
> === added file 'grub-core/loader/arm/linux.c'
> --- grub-core/loader/arm/linux.c      1970-01-01 00:00:00 +0000
> +++ grub-core/loader/arm/linux.c      2013-03-24 13:49:04 +0000
> @@ -0,0 +1,405 @@
> +/* linux.c - boot Linux */
> +/*
> + *  GRUB  --  GRand Unified Bootloader
> + *  Copyright (C) 2013  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 <http://www.gnu.org/licenses/>.
> + */
> +
> +#include <grub/dl.h>
> +#include <grub/file.h>
> +#include <grub/loader.h>
> +#include <grub/mm.h>
> +#include <grub/misc.h>
> +#include <grub/command.h>
> +#include <grub/cache.h>
> +#include <grub/cpu/linux.h>
> +#include <grub/lib/cmdline.h>
> +
> +#include <libfdt.h>
> +
> +GRUB_MOD_LICENSE ("GPLv3+");
> +
> +static grub_dl_t my_mod;
> +
> +static grub_addr_t initrd_start;
> +static grub_size_t initrd_end;

initrd_end should be the same type as initrd_start.

> +
> +static grub_addr_t linux_addr;
> +static grub_size_t linux_size;
> +
> +static char *linux_args;
> +
> +static grub_addr_t firmware_boot_data;
> +static grub_addr_t boot_data;
> +static grub_uint32_t machine_type;
> +
> +/*
> + * linux_prepare_fdt():
> + *   Prepares a loaded FDT for being passed to Linux.
> + *   Merges in command line parameters and sets up initrd addresses.
> + */
> +static grub_err_t
> +linux_prepare_fdt (void)
> +{
> +  int node;
> +  int retval;
> +  int tmp_size;
> +  void *tmp_fdt;
> +
> +  tmp_size = fdt_totalsize ((void *) boot_data) + 
> FDT_ADDITIONAL_ENTRIES_SIZE;

Having a fixed size limit (FDT_ADDITIONAL_ENTRIES_SIZE) to insert
variable-sized data (such as the linux command line) seems suboptimal,
as this may fail when a very long command line is passed. How about
removing the FDT_ADDITIONAL_ENTRIES_SIZE define from the header file
include/grub/arm/linux.h (after all, this is an implementation detail
and IMHO shouldn't go in a public header file) and defining it in this
function, say:

#define FDT_ADDITIONAL_ENTRIES_SIZE     (0x100 +        \
        grub_strlen (linux_args))

[...]
> +/*
> + * Only support zImage, so no relocations necessary
> + */
> +static grub_err_t
> +linux_load (const char *filename)
> +{
> +  grub_file_t file;
> +  int size;
> +
> +  file = grub_file_open (filename);
> +  if (!file)
> +    return GRUB_ERR_FILE_NOT_FOUND;
> +
> +  size = grub_file_size (file);
> +  if (size == 0)
> +    return GRUB_ERR_FILE_READ_ERROR;
> +
> +#ifdef GRUB_MACHINE_EFI
> +  linux_addr = (grub_addr_t) grub_efi_allocate_loader_memory 
> (LINUX_PHYS_OFFSET, size);

There should be a check against allocation failure.

> +#else
> +  linux_addr = LINUX_ADDRESS;
> +#endif
> +  grub_dprintf ("loader", "Loading Linux to 0x%08x\n",
> +             (grub_addr_t) linux_addr);
> +
> +  if (grub_file_read (file, (void *) linux_addr, size) != size)
> +    {
> +      grub_printf ("Kernel read failed!\n");
> +      return GRUB_ERR_FILE_READ_ERROR;

In the EFI case, the allocated memory should be freed before returning.

> +    }
> +
> +  if (*(grub_uint32_t *) (linux_addr + LINUX_ZIMAGE_OFFSET)
> +      != LINUX_ZIMAGE_MAGIC)
> +    {
> +      return grub_error (GRUB_ERR_BAD_FILE_TYPE, N_("invalid zImage"));

As above, in the EFI case the allocated memory should be freed before
returning.

> +    }
> +
> +  linux_size = size;
> +
> +  return GRUB_ERR_NONE;
> +}
> +static grub_err_t
> +linux_unload (void)
> +{
> +  grub_dl_unref (my_mod);
> +
> +  grub_free (linux_args);
> +  linux_args = NULL;

linux_args might already be NULL, if memory allocation for it failed in
grub_cmd_linux().
In the EFI case, the memory allocated for the kernel image should be
freed as well.

> +
> +  initrd_start = initrd_end = 0;

Why should the initrd data be discarded here?

> +
> +  return GRUB_ERR_NONE;
> +}
> +static grub_err_t
> +grub_cmd_linux (grub_command_t cmd __attribute__ ((unused)),
> +             int argc, char *argv[])
> +{
> +  int size, retval;

The return type of linux_load() is grub_err_t, so retval should be the
same type.

> +  grub_file_t file;
> +  grub_dl_ref (my_mod);
> +
> +  if (argc == 0)
> +    return grub_error (GRUB_ERR_BAD_ARGUMENT, N_("filename expected"));
> +
> +  file = grub_file_open (argv[0]);
> +  if (!file)
> +    goto fail;
> +
> +  retval = linux_load (argv[0]);

Here the file is already open: why not just pass the file handle to
linux_load()?

> +  grub_file_close (file);
> +  if (retval != GRUB_ERR_NONE)
> +    goto fail;

In order to correctly report all error types, grub_errno should be set
to retval before entering the error path.

> +
> +  grub_loader_set (linux_boot, linux_unload, 1);

Since linux_boot() may return control to its caller, the third argument
of grub_loader_set() should be 0, otherwise linux_boot() returning
control to its caller results in a dysfunctional state.

> +
> +  size = grub_loader_cmdline_size (argc, argv);
> +  linux_args = grub_malloc (size + sizeof (LINUX_IMAGE));
> +  if (!linux_args)
> +    goto fail;

grub_loader_unset() should be called in this error path, otherwise if
one tries to boot in this state, linux_prepare_fdt() will access a NULL
pointer.

> +
> +  /* Create kernel command line.  */
> +  grub_memcpy (linux_args, LINUX_IMAGE, sizeof (LINUX_IMAGE));
> +  grub_create_loader_cmdline (argc, argv,
> +                           linux_args + sizeof (LINUX_IMAGE) - 1, size);
> +
> +  return GRUB_ERR_NONE;
> +
> +fail:
> +  grub_dl_unref (my_mod);
> +  return grub_errno;
> +}
> +
> +static grub_err_t
> +grub_cmd_initrd (grub_command_t cmd __attribute__ ((unused)),
> +              int argc, char *argv[])
> +{
> +  grub_file_t file;
> +  int size;
> +
> +  if (argc == 0)
> +    return grub_error (GRUB_ERR_BAD_ARGUMENT, N_("filename expected"));
> +
> +  file = grub_file_open (argv[0]);
> +  if (!file)
> +    return grub_errno;
> +
> +  size = grub_file_size (file);
> +  if (size == 0)
> +    goto fail;
> +
> +#ifdef GRUB_MACHINE_EFI
> +  initrd_start = (grub_addr_t) grub_efi_allocate_loader_memory 
> (LINUX_INITRD_PHYS_OFFSET, size);

If the initrd memory was already allocated as a result of the initrd
command being already called previously, it should be freed before
re-allocating memory. Also there should be a check against memory
allocation failure.

> +#else
> +  initrd_start = LINUX_INITRD_ADDRESS;
> +#endif
> +  grub_dprintf ("loader", "Loading initrd to 0x%08x\n",
> +             (grub_addr_t) initrd_start);
> +
> +  if (grub_file_read (file, (void *) initrd_start, size) != size)
> +    goto fail;

In the EFI case, the memory allocated for the initrd should be freed
here. And in both EFI and U-Boot cases, initrd_start should be zeroed.

> +
> +  initrd_end = initrd_start + size;
> +
> +  return GRUB_ERR_NONE;
> +
> +fail:
> +  grub_file_close (file);
> +
> +  return grub_errno;
> +}
> +
> +static void *
> +load_dtb (grub_file_t dtb, int size)
> +{
> +  void *fdt;
> +
> +  fdt = grub_malloc (size);
> +  if (!fdt)
> +    return NULL;
> +
> +  if (grub_file_read (dtb, fdt, size) != size)
> +    {
> +      grub_free (fdt);
> +      return NULL;
> +    }
> +
> +  if (fdt_check_header (fdt) != 0)
> +    {
> +      grub_free (fdt);
> +      return NULL;
> +    }
> +
> +  return fdt;
> +}
> +
> +static grub_err_t
> +grub_cmd_devicetree (grub_command_t cmd __attribute__ ((unused)),
> +                  int argc, char *argv[])
> +{
> +  grub_file_t dtb;
> +  void *blob;
> +  int size;
> +
> +  if (argc != 1)
> +    return grub_error (GRUB_ERR_BAD_ARGUMENT, N_("filename expected"));
> +
> +  dtb = grub_file_open (argv[0]);
> +  if (!dtb)
> +    return grub_error (GRUB_ERR_FILE_NOT_FOUND, N_("failed to open file"));
> +
> +  size = grub_file_size (dtb);
> +  if (size == 0)
> +    goto out;
> +
> +  blob = load_dtb (dtb, size);
> +  if (!blob)
> +    return GRUB_ERR_FILE_NOT_FOUND;
> +
> +#ifdef GRUB_MACHINE_EFI
> +  boot_data = (grub_addr_t) grub_efi_allocate_loader_memory 
> (LINUX_FDT_PHYS_OFFSET, size);

As above, if the fdt memory was already allocated as a result of the
devicetree command being already called previously, it should be freed
before re-allocating memory. Also there should be a check against memory
allocation failure.

> +#else
> +  boot_data = LINUX_FDT_ADDRESS;
> +#endif
> +  grub_dprintf ("loader", "Loading device tree to 0x%08x\n",
> +             (grub_addr_t) boot_data);
> +  fdt_move (blob, (void *) boot_data, fdt_totalsize (blob));
> +  grub_free (blob);

I don't get the point of allocating memory for the FDT in load_dtb()
just to move the FDT data to boot_data and then freeing the temporary
memory. Isn't it easier if load_dtb() operates directly on boot_data?

> +
> +  /* 
> +   * We've successfully loaded an FDT, so any machine type passed
> +   * from firmware is now obsolete.
> +   */
> +  machine_type = ARM_FDT_MACHINE_TYPE;
> +
> +  return GRUB_ERR_NONE;

The file should be closed before returning.

> +
> + out:
> +  grub_file_close (dtb);
> +
> +  return grub_errno;
> +}

--
Francesco



reply via email to

[Prev in Thread] Current Thread [Next in Thread]