On 04/01/2013 04:16 AM, Vladimir 'φ-coder/phcoder' Serbinenko wrote: > Could you put the library itself into a separate tar rather than patch?
I recall you saying a few months ago that you'd prefer rather without libfdt. In fact, libfdt contains much more than what is needed by GRUB. I rewrote the FDT-related functions needed by GRUB (using GRUB's naming convention) and put them in the standalone files grub-core/lib/fdt.c and include/grub/fdt.h. The .c file would need to be included in the linux loader module. I also changed loader/arm/linux.c to use the new functions. Attached are the needed files; the loader code already contains the changes which I proposed in my review of Leif's patch, but supports EFI only, not U-Boot. Also, my code is not completely tested yet, I just wanted to send it out to get your feedback. Regards, Francesco
/* * 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/fdt.h> #include <grub/misc.h> #include <grub/mm.h> #define FDT_SUPPORTED_VERSION 17 #define FDT_BEGIN_NODE 0x00000001 #define FDT_END_NODE 0x00000002 #define FDT_PROP 0x00000003 #define FDT_NOP 0x00000004 #define FDT_END 0x00000009 #define struct_end(fdt) \ ((grub_addr_t) fdt + grub_fdt_get_off_dt_struct(fdt) \ + grub_fdt_get_size_dt_struct(fdt)) /* Size needed by a node entry: 2 tokens (FDT_BEGIN_NODE and FDT_END_NODE), plus the NULL-terminated string containing the name, plus padding if needed. */ #define node_entry_size(node_name) \ (2 * sizeof(grub_uint32_t) \ + ALIGN_UP (grub_strlen (name) + 1, sizeof(grub_uint32_t))) /* Size needed by a property entry: 1 token (FDT_PROPERTY), plus len and nameoff fields, plus the property value, plus padding if needed. */ #define prop_entry_size(prop_len) \ (3 * sizeof(grub_uint32_t) + ALIGN_UP(prop_len, sizeof(grub_uint32_t))) static grub_uint32_t *get_next_node (const void *fdt, char *node_name) { grub_uint32_t *end = (void *) struct_end (fdt); grub_uint32_t *token; if (node_name >= (char *) end) return NULL; while (*node_name) { if (++node_name >= (char *) end) return NULL; } token = (grub_uint32_t *) ALIGN_UP ((grub_addr_t) node_name, 4); while (token < end) { switch (grub_be_to_cpu32(*token)) { case FDT_BEGIN_NODE: token = get_next_node (fdt, (char *) (token + 1)); if (!token) return NULL; break; case FDT_END_NODE: token++; if (token >= end) return NULL; return token; case FDT_PROP: /* Skip property token and following data (len, nameoff and property value). */ token += 3 + grub_be_to_cpu32 (*(token + 1)); break; case FDT_NOP: token++; break; default: return NULL; } } return NULL; } static int get_mem_rsvmap_size (const void *fdt) { int size = 0; grub_uint64_t *ptr = (void *) ((grub_addr_t) fdt + grub_fdt_get_off_mem_rsvmap (fdt)); do { size += 2 * sizeof(*ptr); if (!*ptr && !*(ptr + 1)) return size; ptr += 2; } while ((grub_addr_t) ptr <= (grub_addr_t) fdt + grub_fdt_get_totalsize (fdt) - 2 * sizeof(grub_uint64_t)); return -1; } static grub_uint32_t get_free_space (void *fdt) { int mem_rsvmap_size = get_mem_rsvmap_size (fdt); if (mem_rsvmap_size < 0) /* invalid memory reservation block */ return 0; return (grub_fdt_get_totalsize (fdt) - sizeof(grub_fdt_header_t) - mem_rsvmap_size - grub_fdt_get_size_dt_strings (fdt) - grub_fdt_get_size_dt_struct (fdt)); } static int add_subnode (void *fdt, int parentoffset, const char *name) { grub_uint32_t *begin = (void *) ((grub_addr_t) fdt + grub_fdt_get_off_dt_struct(fdt) + parentoffset); grub_uint32_t *end = (void *) struct_end (fdt); unsigned int entry_size = node_entry_size (name); grub_uint32_t *token = begin; /* Insert the new subnode just after the properties of the parent node (if any).*/ while (1) { if (token >= end) return -1; switch (grub_be_to_cpu32(*token)) { case FDT_PROP: /* Skip len and nameoff. */ token += 2; break; case FDT_BEGIN_NODE: case FDT_END_NODE: goto insert; case FDT_NOP: break; default: /* invalid token */ return -1; } token++; } insert: grub_memmove (token + entry_size, token, (grub_addr_t) end - (grub_addr_t) token); *token = grub_cpu_to_be32(FDT_BEGIN_NODE); token[entry_size / sizeof(*token) - 2] = 0; /* padding bytes */ grub_strcpy((char *) (token + 1), name); token += entry_size / sizeof(*token) - 1; *token = grub_cpu_to_be32(FDT_END_NODE); return ((grub_addr_t) token - (grub_addr_t) fdt - grub_fdt_get_off_dt_struct(fdt)); } /* Rearrange FDT blocks in the canonical order: first the memory reservation block (just after the FDT header), then the structure block and finally the strings block. No free space is left between the first and the second block, while the space between the second and the third block is given by the clearance argument. */ static int rearrange_blocks (void *fdt, unsigned int clearance) { grub_uint32_t off_mem_rsvmap = ALIGN_UP(sizeof(grub_fdt_header_t), 8); grub_uint32_t off_dt_struct = off_mem_rsvmap + get_mem_rsvmap_size (fdt); grub_uint32_t off_dt_strings = off_dt_struct + grub_fdt_get_size_dt_struct (fdt) + clearance; grub_uint8_t *fdt_ptr = fdt; grub_uint8_t *tmp_fdt; if ((grub_fdt_get_off_mem_rsvmap (fdt) == off_mem_rsvmap) && (grub_fdt_get_off_dt_struct (fdt) == off_dt_struct)) { /* No need to allocate memory for a temporary FDT, just move the strings block if needed. */ if (grub_fdt_get_off_dt_strings (fdt) != off_dt_strings) grub_memmove(fdt_ptr + off_dt_strings, fdt_ptr + grub_fdt_get_off_dt_strings (fdt), grub_fdt_get_size_dt_strings (fdt)); return 0; } tmp_fdt = grub_malloc (grub_fdt_get_totalsize (fdt)); if (!tmp_fdt) return -1; grub_memcpy (tmp_fdt + off_mem_rsvmap, fdt_ptr + grub_fdt_get_off_mem_rsvmap (fdt), get_mem_rsvmap_size (fdt)); grub_fdt_set_off_mem_rsvmap (fdt, off_mem_rsvmap); grub_memcpy (tmp_fdt + off_dt_struct, fdt_ptr + grub_fdt_get_off_dt_struct (fdt), grub_fdt_get_size_dt_struct (fdt)); grub_fdt_set_off_dt_struct (fdt, off_dt_struct); grub_memcpy (tmp_fdt + off_dt_strings, fdt_ptr + grub_fdt_get_off_dt_strings (fdt), grub_fdt_get_size_dt_strings (fdt)); grub_fdt_set_off_dt_strings (fdt, off_dt_strings); /* Copy reordered blocks back to fdt. */ memcpy (fdt_ptr + off_mem_rsvmap, tmp_fdt + off_mem_rsvmap, grub_fdt_get_totalsize (fdt) - off_mem_rsvmap); grub_free(tmp_fdt); return 0; } static grub_uint32_t *find_prop (void *fdt, unsigned int nodeoffset, const char *name) { grub_uint32_t *prop = (void *) ((grub_addr_t) fdt + grub_fdt_get_off_dt_struct (fdt) + nodeoffset); grub_uint32_t nameoff; do { if (grub_be_to_cpu32(*prop) == FDT_PROP) { nameoff = grub_be_to_cpu32(*(prop + 2)); if ((nameoff + grub_strlen (name) < grub_fdt_get_size_dt_strings (fdt)) && !grub_strcmp (name, (char *) fdt + grub_fdt_get_off_dt_strings (fdt) + nameoff)) return prop; prop += prop_entry_size(grub_be_to_cpu32(*prop + 1)) / sizeof (*prop); } else if (grub_be_to_cpu32(*prop) != FDT_NOP) return NULL; prop++; } while ((grub_addr_t) prop < ((grub_addr_t) fdt + grub_fdt_get_off_dt_struct (fdt) + grub_fdt_get_size_dt_struct (fdt))); return NULL; } /* Check the FDT header for consistency and adjust the totalsize field to match the size allocated for the FDT; if this function is called before the other functions in this file and returns success, the other functions are guaranteed not to access memory locations outside the allocated memory. */ int grub_fdt_check_header (void *fdt, unsigned int size) { if (((grub_addr_t) fdt & 0x7) || (grub_fdt_get_magic (fdt) != FDT_MAGIC) || (grub_fdt_get_totalsize (fdt) > size) || (grub_fdt_get_version (fdt) < FDT_SUPPORTED_VERSION) || (grub_fdt_get_last_comp_version (fdt) > FDT_SUPPORTED_VERSION) || (grub_fdt_get_off_dt_struct (fdt) & 0x00000003) || (grub_fdt_get_size_dt_struct (fdt) & 0x00000003) || (grub_fdt_get_off_dt_struct (fdt) + grub_fdt_get_size_dt_struct (fdt) > grub_fdt_get_totalsize (fdt)) || (grub_fdt_get_off_dt_strings (fdt) + grub_fdt_get_size_dt_strings (fdt) > grub_fdt_get_totalsize (fdt)) || (grub_fdt_get_off_mem_rsvmap (fdt) & 0x00000007) || (grub_fdt_get_off_mem_rsvmap (fdt) > grub_fdt_get_totalsize (fdt) - 2 * sizeof(grub_uint64_t))) return -1; return 0; } /* Find a direct sub-node of a given parent node. */ int grub_fdt_find_subnode (const void *fdt, unsigned int parentoffset, const char *name) { grub_uint32_t *token, *end; char *node_name; if (parentoffset & 0x3) return -1; token = (void *) ((grub_addr_t) fdt + grub_fdt_get_off_dt_struct(fdt) + parentoffset); end = (void *) struct_end (fdt); while (token < end) { switch (grub_be_to_cpu32(*token)) { case FDT_BEGIN_NODE: node_name = (char *) (token + 1); if (node_name + grub_strlen (name) >= (char *) end) return -1; if (!grub_strcmp (node_name, name)) return (int) ((grub_addr_t) token + ALIGN_UP(grub_strlen (name) + 1, 4) - grub_fdt_get_off_dt_struct (fdt)); token = get_next_node (fdt, node_name); if (!token) return -1; break; case FDT_END_NODE: return -1; case FDT_PROP: /* Skip property token and following data (len, nameoff and property value). */ token += 3 + grub_be_to_cpu32 (*(token + 1)); break; case FDT_NOP: token++; break; default: return -1; } } return -1; } int grub_fdt_add_subnode (void *fdt, unsigned int parentoffset, const char *name) { unsigned int entry_size = node_entry_size(name); if ((parentoffset & 0x3) || (get_free_space (fdt) < entry_size)) return -1; /* The new node entry will increase the size of the structure block: rearrange blocks such that there is sufficient free space between the structure and the strings block, then add the new node entry. */ if (rearrange_blocks (fdt, entry_size) < 0) return -1; return add_subnode (fdt, parentoffset, name); } int grub_fdt_set_prop (void *fdt, unsigned int nodeoffset, const char *name, const void *val, grub_uint32_t len) { grub_uint32_t *prop; int prop_name_present = 0; grub_uint32_t nameoff = 0; if ((nodeoffset >= grub_fdt_get_size_dt_struct (fdt)) || (nodeoffset & 0x3)) return -1; prop = find_prop (fdt, nodeoffset, name); if (prop) { grub_uint32_t prop_len = ALIGN_UP(grub_be_to_cpu32 (*(prop + 1)), sizeof(grub_uint32_t)); grub_uint32_t i; prop_name_present = 1; for (i = 0; i < prop_len / sizeof(grub_uint32_t); i++) *(prop + 3 + i) = grub_cpu_to_be32 (FDT_NOP); if (len > prop_len) { /* Length of new property value is greater than the space allocated for the current value: a new entry needs to be created, so save the nameoff field of the current entry and replace the current entry with NOP tokens. */ nameoff = grub_be_to_cpu32 (*(prop + 2)); *prop = *(prop + 1) = *(prop + 2) = grub_cpu_to_be32 (FDT_NOP); prop = NULL; } } if (!prop || !prop_name_present) { unsigned int needed_space = 0; if (!prop) needed_space = prop_entry_size(len); if (!prop_name_present) needed_space += grub_strlen (name) + 1; if (needed_space > get_free_space (fdt)) return -1; if (rearrange_blocks (fdt, !prop ? prop_entry_size(len) : 0) < 0) return -1; } if (!prop_name_present) { /* Append the property name at the end of the strings block. */ nameoff = grub_fdt_get_size_dt_strings (fdt); grub_strcpy ((char *) fdt + grub_fdt_get_off_dt_strings (fdt) + nameoff, name); grub_fdt_set_size_dt_strings (fdt, grub_fdt_get_size_dt_strings (fdt) + grub_strlen (name) + 1); } if (!prop) { prop = (void *) ((grub_addr_t) fdt + grub_fdt_get_off_dt_struct (fdt) + nodeoffset); grub_memmove (prop + prop_entry_size(len), prop, grub_fdt_get_size_dt_struct (fdt) - nodeoffset); *prop = grub_cpu_to_be32 (FDT_PROP); *(prop + 1) = grub_cpu_to_be32 (len); *(prop + 2) = grub_cpu_to_be32 (nameoff); /* Insert padding bytes at the end of the value; if they are not needed, they will be overwritten by the follozing memcpy. */ *(prop + prop_entry_size(len) / sizeof(grub_uint32_t) - 1) = 0; grub_memcpy (prop + 3, val, len); } return 0; }
/* * 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/>. */ #ifndef GRUB_FDT_HEADER #define GRUB_FDT_HEADER 1 #include <grub/types.h> #define FDT_MAGIC 0xD00DFEED typedef struct { grub_uint32_t magic; grub_uint32_t totalsize; grub_uint32_t off_dt_struct; grub_uint32_t off_dt_strings; grub_uint32_t off_mem_rsvmap; grub_uint32_t version; grub_uint32_t last_comp_version; grub_uint32_t boot_cpuid_phys; grub_uint32_t size_dt_strings; grub_uint32_t size_dt_struct; } grub_fdt_header_t; #define grub_fdt_get_header(fdt, field) \ grub_be_to_cpu32(((const grub_fdt_header_t *)(fdt))->field) #define grub_fdt_set_header(fdt, field, value) \ ((grub_fdt_header_t *)(fdt))->field = grub_cpu_to_be32(value) #define grub_fdt_get_magic(fdt) \ grub_fdt_get_header(fdt, magic) #define grub_fdt_set_magic(fdt, value) \ grub_fdt_set_header(fdt, magic, value) #define grub_fdt_get_totalsize(fdt) \ grub_fdt_get_header(fdt, totalsize) #define grub_fdt_set_totalsize(fdt, value) \ grub_fdt_set_header(fdt, totalsize, value) #define grub_fdt_get_off_dt_struct(fdt) \ grub_fdt_get_header(fdt, off_dt_struct) #define grub_fdt_set_off_dt_struct(fdt, value) \ grub_fdt_set_header(fdt, off_dt_struct, value) #define grub_fdt_get_off_dt_strings(fdt) \ grub_fdt_get_header(fdt, off_dt_strings) #define grub_fdt_set_off_dt_strings(fdt, value) \ grub_fdt_set_header(fdt, off_dt_strings, value) #define grub_fdt_get_off_mem_rsvmap(fdt) \ grub_fdt_get_header(fdt, off_mem_rsvmap) #define grub_fdt_set_off_mem_rsvmap(fdt, value) \ grub_fdt_set_header(fdt, off_mem_rsvmap, value) #define grub_fdt_get_version(fdt) \ grub_fdt_get_header(fdt, version) #define grub_fdt_set_version(fdt, value) \ grub_fdt_set_header(fdt, version, value) #define grub_fdt_get_last_comp_version(fdt) \ grub_fdt_get_header(fdt, last_comp_version) #define grub_fdt_set_last_comp_version(fdt, value) \ grub_fdt_set_header(fdt, last_comp_version, value) #define grub_fdt_get_boot_cpuid_phys(fdt) \ grub_fdt_get_header(fdt, boot_cpuid_phys) #define grub_fdt_set_boot_cpuid_phys(fdt, value) \ grub_fdt_set_header(fdt, boot_cpuid_phys, value) #define grub_fdt_get_size_dt_strings(fdt) \ grub_fdt_get_header(fdt, size_dt_strings) #define grub_fdt_set_size_dt_strings(fdt, value) \ grub_fdt_set_header(fdt, size_dt_strings, value) #define grub_fdt_get_size_dt_struct(fdt) \ grub_fdt_get_header(fdt, size_dt_struct) #define grub_fdt_set_size_dt_struct(fdt, value) \ grub_fdt_set_header(fdt, size_dt_struct, value) int grub_fdt_check_header (void *fdt, unsigned int size); int grub_fdt_find_subnode (const void *fdt, unsigned int parentoffset, const char *name); int grub_fdt_add_subnode (void *fdt, unsigned int parentoffset, const char *name); int grub_fdt_set_prop (void *fdt, unsigned int nodeoffset, const char *name, const void *val, grub_uint32_t len); #define grub_fdt_set_prop32(fdt, nodeoffset, name, val) \ ({ \ grub_uint32_t _val = grub_cpu_to_be32(val); \ grub_fdt_set_prop ((fdt), (nodeoffset), (name), &_val, 4); \ }) #endif /* ! GRUB_FDT_HEADER */
/* 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/fdt.h> #include <grub/file.h> #include <grub/loader.h> #include <grub/machine/loader.h> #include <grub/mm.h> #include <grub/misc.h> #include <grub/command.h> #include <grub/cache.h> #include <grub/lib/cmdline.h> GRUB_MOD_LICENSE ("GPLv3+"); static grub_dl_t my_mod; static grub_addr_t initrd_start, initrd_end; static grub_addr_t linux_addr; static grub_size_t linux_size; static char *linux_args; static void *fdt_addr; #define LINUX_ZIMAGE_OFFSET 0x24 #define LINUX_ZIMAGE_MAGIC 0x016f2818 #define ARM_FDT_MACHINE_TYPE 0xFFFFFFFF #define LINUX_PHYS_OFFSET (0x00008000) #define LINUX_INITRD_PHYS_OFFSET (LINUX_PHYS_OFFSET + 0x02000000) #define LINUX_FDT_PHYS_OFFSET (LINUX_INITRD_PHYS_OFFSET - 0x10000) typedef void (*kernel_entry_t) (int, unsigned long, void *); /* * 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) { #define FDT_ADDITIONAL_ENTRIES_SIZE (0x100 + grub_strlen (linux_args)) int node; int retval; int tmp_size; void *tmp_fdt; tmp_size = grub_fdt_get_totalsize (fdt_addr) + FDT_ADDITIONAL_ENTRIES_SIZE; tmp_fdt = grub_malloc (tmp_size); if (!tmp_fdt) return GRUB_ERR_OUT_OF_MEMORY; grub_memcpy (tmp_fdt, fdt_addr, grub_fdt_get_totalsize (fdt_addr)); grub_fdt_set_totalsize (tmp_fdt, tmp_size); /* Find or create '/chosen' node */ node = grub_fdt_find_subnode (tmp_fdt, 0, "chosen"); if (node < 0) { grub_printf ("No 'chosen' node in FDT - creating.\n"); node = grub_fdt_add_subnode (tmp_fdt, 0, "chosen"); if (node < 0) goto failure; } grub_printf ("linux_args: '%s'\n", linux_args); /* Generate and set command line */ retval = grub_fdt_set_prop (tmp_fdt, node, "bootargs", linux_args, grub_strlen (linux_args) + 1); if (retval) goto failure; if (initrd_start && initrd_end) { /* * We're using physical addresses, so even if we have LPAE, we're * restricted to a 32-bit address space. */ grub_dprintf ("loader", "Initrd @ 0x%08x-0x%08x\n", initrd_start, initrd_end); retval = grub_fdt_set_prop32 (tmp_fdt, node, "linux,initrd-start", initrd_start); if (retval) goto failure; retval = grub_fdt_set_prop32 (tmp_fdt, node, "linux,initrd-end", initrd_end); if (retval) goto failure; } /* Copy updated FDT to its launch location */ grub_memcpy (fdt_addr, tmp_fdt, tmp_size); grub_free (tmp_fdt); grub_dprintf ("loader", "FDT updated for Linux boot\n"); return GRUB_ERR_NONE; failure: grub_free (tmp_fdt); return GRUB_ERR_BAD_ARGUMENT; } static grub_err_t linux_boot (void) { kernel_entry_t linuxmain; grub_err_t err; if (!fdt_addr) return grub_error (GRUB_ERR_FILE_NOT_FOUND, N_("device tree must be supplied")); grub_arch_sync_caches ((void *) linux_addr, linux_size); grub_dprintf ("loader", "Kernel at: 0x%x\n", linux_addr); err = linux_prepare_fdt (); if (err != GRUB_ERR_NONE) { return grub_error (err, "unable to prepare FDT"); } grub_dprintf ("loader", "FDT @ 0x%p\n", fdt_addr); grub_dprintf ("loader", "Jumping to Linux...\n"); /* Boot the kernel. * Arguments to kernel: * r0 - 0 * r1 - machine type * r2 - address of DTB */ linuxmain = (kernel_entry_t) linux_addr; err = grub_efi_prepare_platform(); if (err != GRUB_ERR_NONE) return err; linuxmain (0, ARM_FDT_MACHINE_TYPE, fdt_addr); return err; } /* * Only support zImage, so no relocations necessary */ static grub_err_t linux_load (grub_file_t file) { int size; grub_err_t err; size = grub_file_size (file); if (size == 0) return GRUB_ERR_FILE_READ_ERROR; linux_addr = (grub_addr_t) grub_efi_allocate_loader_memory (LINUX_PHYS_OFFSET, size); if (!linux_addr) return GRUB_ERR_OUT_OF_MEMORY; 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"); err = GRUB_ERR_FILE_READ_ERROR; goto error; } if (*(grub_uint32_t *) (linux_addr + LINUX_ZIMAGE_OFFSET) != LINUX_ZIMAGE_MAGIC) { err = grub_error (GRUB_ERR_BAD_FILE_TYPE, N_("invalid zImage")); goto error; } linux_size = size; return GRUB_ERR_NONE; error: grub_free ((void *) linux_addr); return err; } static grub_err_t linux_unload (void) { grub_dl_unref (my_mod); if (linux_args) { grub_free (linux_args); linux_args = NULL; } grub_free ((void *) linux_addr); return GRUB_ERR_NONE; } static grub_err_t grub_cmd_linux (grub_command_t cmd __attribute__ ((unused)), int argc, char *argv[]) { int size; grub_err_t retval; 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 (file); grub_file_close (file); if (retval != GRUB_ERR_NONE) { grub_errno = retval; goto fail; } grub_loader_set (linux_boot, linux_unload, 0); size = grub_loader_cmdline_size (argc, argv); linux_args = grub_malloc (size + sizeof (LINUX_IMAGE)); if (!linux_args) { grub_loader_unset(); goto fail; } /* 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; if (initrd_start) grub_free ((void *) initrd_start); initrd_start = (grub_addr_t) grub_efi_allocate_loader_memory ( LINUX_INITRD_PHYS_OFFSET, size); if (!initrd_start) { grub_error (GRUB_ERR_OUT_OF_MEMORY, N_("memory allocation failed")); goto fail; } grub_dprintf ("loader", "Loading initrd to 0x%08x\n", (grub_addr_t) initrd_start); if (grub_file_read (file, (void *) initrd_start, size) != size) { grub_free ((void *) initrd_start); initrd_start = 0; goto fail; } initrd_end = initrd_start + size; return GRUB_ERR_NONE; fail: grub_file_close (file); return grub_errno; } static grub_err_t load_dtb (grub_file_t dtb, int size) { if ((grub_file_read (dtb, fdt_addr, size) != size) || (grub_fdt_check_header (fdt_addr, size) != 0)) return grub_error (GRUB_ERR_BAD_FILE_TYPE, N_("invalid device tree")); grub_fdt_set_totalsize (fdt_addr, size); return GRUB_ERR_NONE; } static grub_err_t grub_cmd_devicetree (grub_command_t cmd __attribute__ ((unused)), int argc, char *argv[]) { grub_file_t dtb; 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; if (fdt_addr) grub_free (fdt_addr); fdt_addr = grub_efi_allocate_loader_memory (LINUX_FDT_PHYS_OFFSET, size); if (!fdt_addr) { grub_error (GRUB_ERR_OUT_OF_MEMORY, N_("memory allocation failed")); goto out; } grub_dprintf ("loader", "Loading device tree to 0x%08x\n", (grub_addr_t) fdt_addr); load_dtb (dtb, size); if (grub_errno != GRUB_ERR_NONE) { grub_free (fdt_addr); fdt_addr = NULL; } out: grub_file_close (dtb); return grub_errno; } static grub_command_t cmd_linux, cmd_initrd, cmd_devicetree; 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.")); cmd_devicetree = grub_register_command ("devicetree", grub_cmd_devicetree, 0, N_("Load DTB file.")); my_mod = mod; } GRUB_MOD_FINI (linux) { grub_unregister_command (cmd_linux); grub_unregister_command (cmd_initrd); grub_unregister_command (cmd_devicetree); }
_______________________________________________ Grub-devel mailing list Grub-devel@gnu.org https://lists.gnu.org/mailman/listinfo/grub-devel