diff --git a/ChangeLog b/ChangeLog index 4e7abb564..83e90df94 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,10 @@ +2013-06-15 Vladimir Serbinenko + + Support for cbfs. Also factor out the part which is common + for all archives to a separate module. This splits tar from cpio + as they are very different but keeps cpio, cpio_be, odc and newc + together since they're very similar. + 2013-06-15 David Michael * configure.ac (FREETYPE): Change AC_CHECK_PROGS to AC_CHECK_TOOLS. diff --git a/Makefile.util.def b/Makefile.util.def index 220bf6ac4..97abd84c2 100644 --- a/Makefile.util.def +++ b/Makefile.util.def @@ -75,6 +75,8 @@ library = { common = grub-core/fs/afs.c; common = grub-core/fs/bfs.c; common = grub-core/fs/btrfs.c; + common = grub-core/fs/cbfs.c; + common = grub-core/fs/archelp.c; common = grub-core/fs/cpio.c; common = grub-core/fs/cpio_be.c; common = grub-core/fs/odc.c; diff --git a/grub-core/Makefile.core.def b/grub-core/Makefile.core.def index ed4b72724..9d177c674 100644 --- a/grub-core/Makefile.core.def +++ b/grub-core/Makefile.core.def @@ -1145,6 +1145,16 @@ module = { cppflags = '-I$(srcdir)/lib/posix_wrap -I$(srcdir)/lib/minilzo -DMINILZO_HAVE_CONFIG_H'; }; +module = { + name = archelp; + common = fs/archelp.c; +}; + +module = { + name = cbfs; + common = fs/cbfs.c; +}; + module = { name = cpio; common = fs/cpio.c; diff --git a/grub-core/commands/nativedisk.c b/grub-core/commands/nativedisk.c index 71d7bc410..af3191313 100644 --- a/grub-core/commands/nativedisk.c +++ b/grub-core/commands/nativedisk.c @@ -78,8 +78,12 @@ get_uuid (const char *name, char **uuid, int getnative) break; /* Virtual disks. */ + /* GRUB dynamically generated files. */ case GRUB_DISK_DEVICE_PROCFS_ID: + /* To access through host OS routines (grub-emu only). */ case GRUB_DISK_DEVICE_HOST_ID: + /* To access coreboot roms. */ + case GRUB_DISK_DEVICE_CBFSDISK_ID: /* GRUB-only memdisk. Can't match any of firmware devices. */ case GRUB_DISK_DEVICE_MEMDISK_ID: grub_dprintf ("nativedisk", "Skipping native disk %s\n", diff --git a/grub-core/fs/archelp.c b/grub-core/fs/archelp.c new file mode 100644 index 000000000..4a7ace117 --- /dev/null +++ b/grub-core/fs/archelp.c @@ -0,0 +1,298 @@ +/* + * GRUB -- GRand Unified Bootloader + * Copyright (C) 2007,2008,2009,2013 Free Software Foundation, Inc. + * + * This program 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. + * + * This program 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 this program. If not, see . + */ + +#include +#include +#include +#include +#include + +GRUB_MOD_LICENSE ("GPLv3+"); + +static inline void +canonicalize (char *name) +{ + char *iptr, *optr; + for (iptr = name, optr = name; *iptr; ) + { + while (*iptr == '/') + iptr++; + if (iptr[0] == '.' && (iptr[1] == '/' || iptr[1] == 0)) + { + iptr += 2; + continue; + } + if (iptr[0] == '.' && iptr[1] == '.' && (iptr[2] == '/' || iptr[2] == 0)) + { + iptr += 3; + if (optr == name) + continue; + for (optr -= 2; optr >= name && *optr != '/'; optr--); + optr++; + continue; + } + while (*iptr && *iptr != '/') + *optr++ = *iptr++; + if (*iptr) + *optr++ = *iptr++; + } + *optr = 0; +} + +static grub_err_t +handle_symlink (struct grub_archelp_data *data, + struct grub_archelp_ops *arcops, + const char *fn, char **name, + grub_uint32_t mode, int *restart) +{ + grub_size_t flen; + char *target; + char *ptr; + char *lastslash; + grub_size_t prefixlen; + char *rest; + char *linktarget; + + *restart = 0; + + if ((mode & GRUB_ARCHELP_ATTR_TYPE) != GRUB_ARCHELP_ATTR_LNK + || !arcops->get_link_target) + return GRUB_ERR_NONE; + flen = grub_strlen (fn); + if (grub_memcmp (*name, fn, flen) != 0 + || ((*name)[flen] != 0 && (*name)[flen] != '/')) + return GRUB_ERR_NONE; + rest = *name + flen; + lastslash = rest; + if (*rest) + rest++; + while (lastslash >= *name && *lastslash != '/') + lastslash--; + if (lastslash >= *name) + prefixlen = lastslash - *name; + else + prefixlen = 0; + + if (prefixlen) + prefixlen++; + + linktarget = arcops->get_link_target (data); + if (!linktarget) + return grub_errno; + if (linktarget[0] == '\0') + return GRUB_ERR_NONE; + target = grub_malloc (grub_strlen (linktarget) + grub_strlen (*name) + 2); + if (!target) + return grub_errno; + + grub_strcpy (target + prefixlen, linktarget); + grub_free (linktarget); + if (target[prefixlen] == '/') + { + ptr = grub_stpcpy (target, target + prefixlen); + ptr = grub_stpcpy (ptr, rest); + *ptr = 0; + grub_dprintf ("archelp", "symlink redirected %s to %s\n", + *name, target); + grub_free (*name); + + canonicalize (target); + *name = target; + *restart = 1; + return GRUB_ERR_NONE; + } + if (prefixlen) + { + grub_memcpy (target, *name, prefixlen); + target[prefixlen-1] = '/'; + } + grub_strcat (target, rest); + grub_dprintf ("archelp", "symlink redirected %s to %s\n", + *name, target); + grub_free (*name); + canonicalize (target); + *name = target; + *restart = 1; + return GRUB_ERR_NONE; +} + +grub_err_t +grub_archelp_dir (struct grub_archelp_data *data, + struct grub_archelp_ops *arcops, + const char *path_in, + grub_fs_dir_hook_t hook, void *hook_data) +{ + char *prev, *name, *path, *ptr; + grub_size_t len; + int symlinknest = 0; + + path = grub_strdup (path_in + 1); + if (!path) + return grub_errno; + canonicalize (path); + for (ptr = path + grub_strlen (path) - 1; ptr >= path && *ptr == '/'; ptr--) + *ptr = 0; + + prev = 0; + + len = grub_strlen (path); + while (1) + { + grub_int32_t mtime; + grub_uint32_t mode; + grub_err_t err; + + if (arcops->find_file (data, &name, &mtime, &mode)) + goto fail; + + if (mode == GRUB_ARCHELP_ATTR_END) + break; + + canonicalize (name); + + if (grub_memcmp (path, name, len) == 0 + && (name[len] == 0 || name[len] == '/' || len == 0)) + { + char *p, *n; + + n = name + len; + while (*n == '/') + n++; + + p = grub_strchr (n, '/'); + if (p) + *p = 0; + + if (((!prev) || (grub_strcmp (prev, name) != 0)) && *n != 0) + { + struct grub_dirhook_info info; + grub_memset (&info, 0, sizeof (info)); + info.dir = (p != NULL) || ((mode & GRUB_ARCHELP_ATTR_TYPE) + == GRUB_ARCHELP_ATTR_DIR); + if (!(mode & GRUB_ARCHELP_ATTR_NOTIME)) + { + info.mtime = mtime; + info.mtimeset = 1; + } + if (hook (n, &info, hook_data)) + { + grub_free (name); + goto fail; + } + grub_free (prev); + prev = name; + } + else + { + int restart = 0; + err = handle_symlink (data, arcops, name, + &path, mode, &restart); + grub_free (name); + if (err) + goto fail; + if (restart) + { + len = grub_strlen (path); + if (++symlinknest == 8) + { + grub_error (GRUB_ERR_SYMLINK_LOOP, + N_("too deep nesting of symlinks")); + goto fail; + } + arcops->rewind (data); + } + } + } + else + grub_free (name); + } + +fail: + + grub_free (path); + grub_free (prev); + + return grub_errno; +} + +grub_err_t +grub_archelp_open (struct grub_archelp_data *data, + struct grub_archelp_ops *arcops, + const char *name_in) +{ + char *fn; + char *name = grub_strdup (name_in + 1); + int symlinknest = 0; + + if (!name) + return grub_errno; + + canonicalize (name); + + while (1) + { + grub_uint32_t mode; + int restart; + + if (arcops->find_file (data, &fn, NULL, &mode)) + goto fail; + + if (mode == GRUB_ARCHELP_ATTR_END) + { + grub_error (GRUB_ERR_FILE_NOT_FOUND, N_("file `%s' not found"), name_in); + break; + } + + canonicalize (fn); + + if (handle_symlink (data, arcops, fn, &name, mode, &restart)) + { + grub_free (fn); + goto fail; + } + + if (restart) + { + arcops->rewind (data); + if (++symlinknest == 8) + { + grub_error (GRUB_ERR_SYMLINK_LOOP, + N_("too deep nesting of symlinks")); + goto fail; + } + goto no_match; + } + + if (grub_strcmp (name, fn) != 0) + goto no_match; + + grub_free (fn); + grub_free (name); + + return GRUB_ERR_NONE; + + no_match: + + grub_free (fn); + } + +fail: + grub_free (name); + + return grub_errno; +} diff --git a/grub-core/fs/cbfs.c b/grub-core/fs/cbfs.c new file mode 100644 index 000000000..d1631f152 --- /dev/null +++ b/grub-core/fs/cbfs.c @@ -0,0 +1,382 @@ +/* cbfs.c - cbfs and tar filesystem. */ +/* + * GRUB -- GRand Unified Bootloader + * Copyright (C) 2007,2008,2009,2013 Free Software Foundation, Inc. + * + * This program 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. + * + * This program 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 this program. If not, see . + */ + +#include +#include +#include + +#include +#include +#include +#include +#include + +GRUB_MOD_LICENSE ("GPLv3+"); + + +struct grub_archelp_data +{ + grub_disk_t disk; + grub_off_t hofs, next_hofs; + grub_off_t dofs; + grub_off_t size; + grub_off_t cbfs_start; + grub_off_t cbfs_end; + grub_off_t cbfs_align; +}; + +static grub_err_t +grub_cbfs_find_file (struct grub_archelp_data *data, char **name, + grub_int32_t *mtime, + grub_uint32_t *mode) +{ + grub_size_t offset; + for (;; + data->dofs = data->hofs + offset, + data->next_hofs = ALIGN_UP (data->dofs + data->size, data->cbfs_align)) + { + struct cbfs_file hd; + grub_size_t namesize; + + data->hofs = data->next_hofs; + + if (data->hofs >= data->cbfs_end) + { + *mode = GRUB_ARCHELP_ATTR_END; + return GRUB_ERR_NONE; + } + + if (grub_disk_read (data->disk, 0, data->hofs, sizeof (hd), &hd)) + return grub_errno; + + if (grub_memcmp (hd.magic, CBFS_FILE_MAGIC, sizeof (hd.magic)) != 0) + { + *mode = GRUB_ARCHELP_ATTR_END; + return GRUB_ERR_NONE; + } + data->size = grub_be_to_cpu32 (hd.len); + (void) mtime; + offset = grub_be_to_cpu32 (hd.offset); + + if (mode) + *mode = GRUB_ARCHELP_ATTR_FILE | GRUB_ARCHELP_ATTR_NOTIME; + + namesize = offset; + if (namesize >= sizeof (hd)) + namesize -= sizeof (hd); + if (namesize == 0) + continue; + *name = grub_malloc (namesize + 1); + if (*name == NULL) + return grub_errno; + + if (grub_disk_read (data->disk, 0, data->hofs + sizeof (hd), + namesize, *name)) + { + grub_free (*name); + return grub_errno; + } + + if ((*name)[0] == '\0') + { + grub_free (*name); + *name = NULL; + continue; + } + + (*name)[namesize] = 0; + + data->dofs = data->hofs + offset; + data->next_hofs = ALIGN_UP (data->dofs + data->size, data->cbfs_align); + return GRUB_ERR_NONE; + } +} + +static void +grub_cbfs_rewind (struct grub_archelp_data *data) +{ + data->next_hofs = data->cbfs_start; +} + +static struct grub_archelp_ops arcops = + { + .find_file = grub_cbfs_find_file, + .rewind = grub_cbfs_rewind + }; + +static int +validate_head (struct cbfs_header *head) +{ + return (head->magic == grub_cpu_to_be32_compile_time (CBFS_HEADER_MAGIC) + && (head->version + == grub_cpu_to_be32_compile_time (CBFS_HEADER_VERSION1) + || head->version + == grub_cpu_to_be32_compile_time (CBFS_HEADER_VERSION2)) + && (grub_be_to_cpu32 (head->bootblocksize) + < grub_be_to_cpu32 (head->romsize)) + && (grub_be_to_cpu32 (head->offset) + < grub_be_to_cpu32 (head->romsize)) + && (grub_be_to_cpu32 (head->offset) + + grub_be_to_cpu32 (head->bootblocksize) + < grub_be_to_cpu32 (head->romsize)) + && head->align != 0 + && (head->align & (head->align - 1)) == 0 + && head->romsize != 0); +} + +static struct grub_archelp_data * +grub_cbfs_mount (grub_disk_t disk) +{ + struct cbfs_file hd; + struct grub_archelp_data *data; + grub_uint32_t ptr; + grub_off_t header_off; + struct cbfs_header head; + + if (grub_disk_read (disk, grub_disk_get_size (disk) - 1, + GRUB_DISK_SECTOR_SIZE - sizeof (ptr), + sizeof (ptr), &ptr)) + goto fail; + + ptr = grub_cpu_to_le32 (ptr); + header_off = (grub_disk_get_size (disk) << GRUB_DISK_SECTOR_BITS) + + (grub_int32_t) ptr; + + if (grub_disk_read (disk, 0, header_off, + sizeof (head), &head)) + goto fail; + + if (!validate_head (&head)) + goto fail; + + data = (struct grub_archelp_data *) grub_zalloc (sizeof (*data)); + if (!data) + goto fail; + + data->cbfs_start = (grub_disk_get_size (disk) << GRUB_DISK_SECTOR_BITS) + - (grub_be_to_cpu32 (head.romsize) - grub_be_to_cpu32 (head.offset)); + data->cbfs_end = (grub_disk_get_size (disk) << GRUB_DISK_SECTOR_BITS) + - grub_be_to_cpu32 (head.bootblocksize); + data->cbfs_align = grub_be_to_cpu32 (head.align); + + if (data->cbfs_start >= (grub_disk_get_size (disk) << GRUB_DISK_SECTOR_BITS)) + goto fail; + if (data->cbfs_end > (grub_disk_get_size (disk) << GRUB_DISK_SECTOR_BITS)) + data->cbfs_end = (grub_disk_get_size (disk) << GRUB_DISK_SECTOR_BITS); + + data->next_hofs = data->cbfs_start; + + if (grub_disk_read (disk, 0, data->cbfs_start, sizeof (hd), &hd)) + goto fail; + + if (grub_memcmp (hd.magic, CBFS_FILE_MAGIC, sizeof (CBFS_FILE_MAGIC) - 1)) + goto fail; + + data->disk = disk; + + return data; + +fail: + grub_error (GRUB_ERR_BAD_FS, "not a cbfs filesystem"); + return 0; +} + +static grub_err_t +grub_cbfs_dir (grub_device_t device, const char *path_in, + grub_fs_dir_hook_t hook, void *hook_data) +{ + struct grub_archelp_data *data; + grub_err_t err; + + data = grub_cbfs_mount (device->disk); + if (!data) + return grub_errno; + + err = grub_archelp_dir (data, &arcops, + path_in, hook, hook_data); + + grub_free (data); + + return err; +} + +static grub_err_t +grub_cbfs_open (grub_file_t file, const char *name_in) +{ + struct grub_archelp_data *data; + grub_err_t err; + + data = grub_cbfs_mount (file->device->disk); + if (!data) + return grub_errno; + + err = grub_archelp_open (data, &arcops, name_in); + if (err) + { + grub_free (data); + } + else + { + file->data = data; + file->size = data->size; + } + return err; +} + +static grub_ssize_t +grub_cbfs_read (grub_file_t file, char *buf, grub_size_t len) +{ + struct grub_archelp_data *data; + + data = file->data; + return (grub_disk_read (data->disk, 0, data->dofs + file->offset, + len, buf)) ? -1 : (grub_ssize_t) len; +} + +static grub_err_t +grub_cbfs_close (grub_file_t file) +{ + struct grub_archelp_data *data; + + data = file->data; + grub_free (data); + + return grub_errno; +} + +#if (defined (__i386__) || defined (__x86_64__)) && !defined (GRUB_UTIL) + +static char *cbfsdisk_addr; +static grub_off_t cbfsdisk_size = 0; + +static int +grub_cbfsdisk_iterate (grub_disk_dev_iterate_hook_t hook, void *hook_data, + grub_disk_pull_t pull) +{ + if (pull != GRUB_DISK_PULL_NONE) + return 0; + + return hook ("cbfsdisk", hook_data); +} + +static grub_err_t +grub_cbfsdisk_open (const char *name, grub_disk_t disk) +{ + if (grub_strcmp (name, "cbfsdisk")) + return grub_error (GRUB_ERR_UNKNOWN_DEVICE, "not a cbfsdisk"); + + disk->total_sectors = cbfsdisk_size / GRUB_DISK_SECTOR_SIZE; + disk->id = (unsigned long) "cbfs"; + + return GRUB_ERR_NONE; +} + +static void +grub_cbfsdisk_close (grub_disk_t disk __attribute((unused))) +{ +} + +static grub_err_t +grub_cbfsdisk_read (grub_disk_t disk __attribute((unused)), + grub_disk_addr_t sector, + grub_size_t size, char *buf) +{ + grub_memcpy (buf, cbfsdisk_addr + (sector << GRUB_DISK_SECTOR_BITS), + size << GRUB_DISK_SECTOR_BITS); + return 0; +} + +static grub_err_t +grub_cbfsdisk_write (grub_disk_t disk __attribute__ ((unused)), + grub_disk_addr_t sector __attribute__ ((unused)), + grub_size_t size __attribute__ ((unused)), + const char *buf __attribute__ ((unused))) +{ + return grub_error (GRUB_ERR_NOT_IMPLEMENTED_YET, + "rom flashing isn't implemented yet"); +} + +static struct grub_disk_dev grub_cbfsdisk_dev = + { + .name = "cbfsdisk", + .id = GRUB_DISK_DEVICE_CBFSDISK_ID, + .iterate = grub_cbfsdisk_iterate, + .open = grub_cbfsdisk_open, + .close = grub_cbfsdisk_close, + .read = grub_cbfsdisk_read, + .write = grub_cbfsdisk_write, + .next = 0 + }; + +static void +init_cbfsdisk (void) +{ + grub_uint32_t ptr; + struct cbfs_header *head; + + ptr = *(grub_uint32_t *) 0xfffffffc; + head = (struct cbfs_header *) ptr; + + if (!validate_head (head)) + return; + + cbfsdisk_size = ALIGN_UP (grub_be_to_cpu32 (head->romsize), + GRUB_DISK_SECTOR_SIZE); + cbfsdisk_addr = (void *) (grub_addr_t) (0x100000000ULL - cbfsdisk_size); + + grub_disk_dev_register (&grub_cbfsdisk_dev); +} + +static void +fini_cbfsdisk (void) +{ + if (! cbfsdisk_size) + return; + grub_disk_dev_unregister (&grub_cbfsdisk_dev); +} + +#endif + +static struct grub_fs grub_cbfs_fs = { + .name = "cbfs", + .dir = grub_cbfs_dir, + .open = grub_cbfs_open, + .read = grub_cbfs_read, + .close = grub_cbfs_close, +#ifdef GRUB_UTIL + .reserved_first_sector = 0, + .blocklist_install = 0, +#endif +}; + +GRUB_MOD_INIT (cbfs) +{ +#if (defined (__i386__) || defined (__x86_64__)) && !defined (GRUB_UTIL) + init_cbfsdisk (); +#endif + grub_fs_register (&grub_cbfs_fs); +} + +GRUB_MOD_FINI (cbfs) +{ + grub_fs_unregister (&grub_cbfs_fs); +#if (defined (__i386__) || defined (__x86_64__)) && !defined (GRUB_UTIL) + fini_cbfsdisk (); +#endif +} diff --git a/grub-core/fs/cpio.c b/grub-core/fs/cpio.c index 628680d23..b14e190f9 100644 --- a/grub-core/fs/cpio.c +++ b/grub-core/fs/cpio.c @@ -53,7 +53,6 @@ read_number (const grub_uint16_t *arr, grub_size_t size) GRUB_MOD_INIT (cpio) { grub_fs_register (&grub_cpio_fs); - my_mod = mod; } GRUB_MOD_FINI (cpio) diff --git a/grub-core/fs/cpio_be.c b/grub-core/fs/cpio_be.c index 40894b97e..83df8195b 100644 --- a/grub-core/fs/cpio_be.c +++ b/grub-core/fs/cpio_be.c @@ -53,7 +53,6 @@ read_number (const grub_uint16_t *arr, grub_size_t size) GRUB_MOD_INIT (cpio_be) { grub_fs_register (&grub_cpio_fs); - my_mod = mod; } GRUB_MOD_FINI (cpio_be) diff --git a/grub-core/fs/cpio_common.c b/grub-core/fs/cpio_common.c index ee1bc4e9c..370324ceb 100644 --- a/grub-core/fs/cpio_common.c +++ b/grub-core/fs/cpio_common.c @@ -23,68 +23,29 @@ #include #include #include +#include GRUB_MOD_LICENSE ("GPLv3+"); -#define ATTR_TYPE 0160000 -#define ATTR_FILE 0100000 -#define ATTR_DIR 0040000 -#define ATTR_LNK 0120000 - -struct grub_cpio_data +struct grub_archelp_data { grub_disk_t disk; grub_off_t hofs; + grub_off_t next_hofs; grub_off_t dofs; grub_off_t size; -#ifdef MODE_USTAR - char *linkname; - grub_size_t linkname_alloc; -#endif }; -static grub_dl_t my_mod; - -static inline void -canonicalize (char *name) -{ - char *iptr, *optr; - for (iptr = name, optr = name; *iptr; ) - { - while (*iptr == '/') - iptr++; - if (iptr[0] == '.' && (iptr[1] == '/' || iptr[1] == 0)) - { - iptr += 2; - continue; - } - if (iptr[0] == '.' && iptr[1] == '.' && (iptr[2] == '/' || iptr[2] == 0)) - { - iptr += 3; - if (optr == name) - continue; - for (optr -= 2; optr >= name && *optr != '/'; optr--); - optr++; - continue; - } - while (*iptr && *iptr != '/') - *optr++ = *iptr++; - if (*iptr) - *optr++ = *iptr++; - } - *optr = 0; -} - static grub_err_t -grub_cpio_find_file (struct grub_cpio_data *data, char **name, - grub_int32_t *mtime, grub_disk_addr_t *ofs, - grub_uint32_t *mode) +grub_cpio_find_file (struct grub_archelp_data *data, char **name, + grub_int32_t *mtime, grub_uint32_t *mode) { -#ifndef MODE_USTAR struct head hd; grub_size_t namesize; grub_uint32_t modeval; + data->hofs = data->next_hofs; + if (grub_disk_read (data->disk, 0, data->hofs, sizeof (hd), &hd)) return grub_errno; @@ -118,153 +79,57 @@ grub_cpio_find_file (struct grub_cpio_data *data, char **name, if (data->size == 0 && modeval == 0 && namesize == 11 && grub_memcmp(*name, "TRAILER!!!", 11) == 0) { - *ofs = 0; + *mode = GRUB_ARCHELP_ATTR_END; grub_free (*name); return GRUB_ERR_NONE; } - canonicalize (*name); - data->dofs = data->hofs + ALIGN_CPIO (sizeof (hd) + namesize); - *ofs = data->dofs + ALIGN_CPIO (data->size); -#else - struct head hd; - int reread = 0, have_longname = 0, have_longlink = 0; - - for (reread = 0; reread < 3; reread++) - { - if (grub_disk_read (data->disk, 0, data->hofs, sizeof (hd), &hd)) - return grub_errno; - - if (!hd.name[0] && !hd.prefix[0]) - { - *ofs = 0; - return GRUB_ERR_NONE; - } - - if (grub_memcmp (hd.magic, MAGIC, sizeof (MAGIC) - 1)) - return grub_error (GRUB_ERR_BAD_FS, "invalid tar archive"); - - if (hd.typeflag == 'L') - { - grub_err_t err; - grub_size_t namesize = read_number (hd.size, sizeof (hd.size)); - *name = grub_malloc (namesize + 1); - if (*name == NULL) - return grub_errno; - err = grub_disk_read (data->disk, 0, - data->hofs + GRUB_DISK_SECTOR_SIZE, namesize, - *name); - (*name)[namesize] = 0; - if (err) - return err; - data->hofs += GRUB_DISK_SECTOR_SIZE - + ((namesize + GRUB_DISK_SECTOR_SIZE - 1) & - ~(GRUB_DISK_SECTOR_SIZE - 1)); - have_longname = 1; - continue; - } - - if (hd.typeflag == 'K') - { - grub_err_t err; - grub_size_t linksize = read_number (hd.size, sizeof (hd.size)); - if (data->linkname_alloc < linksize + 1) - { - char *n; - n = grub_malloc (2 * (linksize + 1)); - if (!n) - return grub_errno; - grub_free (data->linkname); - data->linkname = n; - data->linkname_alloc = 2 * (linksize + 1); - } - - err = grub_disk_read (data->disk, 0, - data->hofs + GRUB_DISK_SECTOR_SIZE, linksize, - data->linkname); - if (err) - return err; - data->linkname[linksize] = 0; - data->hofs += GRUB_DISK_SECTOR_SIZE - + ((linksize + GRUB_DISK_SECTOR_SIZE - 1) & - ~(GRUB_DISK_SECTOR_SIZE - 1)); - have_longlink = 1; - continue; - } - - if (!have_longname) - { - grub_size_t extra_size = 0; - - while (extra_size < sizeof (hd.prefix) - && hd.prefix[extra_size]) - extra_size++; - *name = grub_malloc (sizeof (hd.name) + extra_size + 2); - if (*name == NULL) - return grub_errno; - if (hd.prefix[0]) - { - grub_memcpy (*name, hd.prefix, extra_size); - (*name)[extra_size++] = '/'; - } - grub_memcpy (*name + extra_size, hd.name, sizeof (hd.name)); - (*name)[extra_size + sizeof (hd.name)] = 0; - } - - data->size = read_number (hd.size, sizeof (hd.size)); - data->dofs = data->hofs + GRUB_DISK_SECTOR_SIZE; - *ofs = data->dofs + ((data->size + GRUB_DISK_SECTOR_SIZE - 1) & - ~(GRUB_DISK_SECTOR_SIZE - 1)); - if (mtime) - *mtime = read_number (hd.mtime, sizeof (hd.mtime)); - if (mode) - { - *mode = read_number (hd.mode, sizeof (hd.mode)); - switch (hd.typeflag) - { - /* Hardlink. */ - case '1': - /* Symlink. */ - case '2': - *mode |= ATTR_LNK; - break; - case '0': - *mode |= ATTR_FILE; - break; - case '5': - *mode |= ATTR_DIR; - break; - } - } - if (!have_longlink) - { - if (data->linkname_alloc < 101) - { - char *n; - n = grub_malloc (101); - if (!n) - return grub_errno; - grub_free (data->linkname); - data->linkname = n; - data->linkname_alloc = 101; - } - grub_memcpy (data->linkname, hd.linkname, sizeof (hd.linkname)); - data->linkname[100] = 0; - } - - canonicalize (*name); - return GRUB_ERR_NONE; - } -#endif + data->next_hofs = data->dofs + ALIGN_CPIO (data->size); return GRUB_ERR_NONE; } -static struct grub_cpio_data * +static char * +grub_cpio_get_link_target (struct grub_archelp_data *data) +{ + char *ret; + grub_err_t err; + + if (data->size == 0) + return grub_strdup (""); + ret = grub_malloc (data->size + 1); + if (!ret) + return NULL; + + err = grub_disk_read (data->disk, 0, data->dofs, data->size, + ret); + if (err) + { + grub_free (ret); + return NULL; + } + ret[data->size] = '\0'; + return ret; +} + +static void +grub_cpio_rewind (struct grub_archelp_data *data) +{ + data->next_hofs = 0; +} + +static struct grub_archelp_ops arcops = + { + .find_file = grub_cpio_find_file, + .get_link_target = grub_cpio_get_link_target, + .rewind = grub_cpio_rewind + }; + +static struct grub_archelp_data * grub_cpio_mount (grub_disk_t disk) { struct head hd; - struct grub_cpio_data *data; + struct grub_archelp_data *data; if (grub_disk_read (disk, 0, 0, sizeof (hd), &hd)) goto fail; @@ -276,7 +141,7 @@ grub_cpio_mount (grub_disk_t disk) ) goto fail; - data = (struct grub_cpio_data *) grub_zalloc (sizeof (*data)); + data = (struct grub_archelp_data *) grub_zalloc (sizeof (*data)); if (!data) goto fail; @@ -289,293 +154,52 @@ fail: return 0; } -static grub_err_t -handle_symlink (struct grub_cpio_data *data, - const char *fn, char **name, - grub_uint32_t mode, int *restart) -{ - grub_size_t flen; - char *target; -#ifndef MODE_USTAR - grub_err_t err; -#endif - char *ptr; - char *lastslash; - grub_size_t prefixlen; - char *rest; - grub_size_t size; - - *restart = 0; - - if ((mode & ATTR_TYPE) != ATTR_LNK) - return GRUB_ERR_NONE; - flen = grub_strlen (fn); - if (grub_memcmp (*name, fn, flen) != 0 - || ((*name)[flen] != 0 && (*name)[flen] != '/')) - return GRUB_ERR_NONE; - rest = *name + flen; - lastslash = rest; - if (*rest) - rest++; - while (lastslash >= *name && *lastslash != '/') - lastslash--; - if (lastslash >= *name) - prefixlen = lastslash - *name; - else - prefixlen = 0; - - if (prefixlen) - prefixlen++; - -#ifdef MODE_USTAR - size = grub_strlen (data->linkname); -#else - size = data->size; -#endif - if (size == 0) - return GRUB_ERR_NONE; - target = grub_malloc (size + grub_strlen (*name) + 2); - if (!target) - return grub_errno; - -#ifdef MODE_USTAR - grub_memcpy (target + prefixlen, data->linkname, size); -#else - err = grub_disk_read (data->disk, 0, data->dofs, data->size, - target + prefixlen); - if (err) - return err; -#endif - if (target[prefixlen] == '/') - { - grub_memmove (target, target + prefixlen, size); - ptr = target + size; - ptr = grub_stpcpy (ptr, rest); - *ptr = 0; - grub_dprintf ("cpio", "symlink redirected %s to %s\n", - *name, target); - grub_free (*name); - - canonicalize (target); - *name = target; - *restart = 1; - return GRUB_ERR_NONE; - } - if (prefixlen) - { - grub_memcpy (target, *name, prefixlen); - target[prefixlen-1] = '/'; - } - ptr = target + prefixlen + size; - ptr = grub_stpcpy (ptr, rest); - *ptr = 0; - grub_dprintf ("cpio", "symlink redirected %s to %s\n", - *name, target); - grub_free (*name); - canonicalize (target); - *name = target; - *restart = 1; - return GRUB_ERR_NONE; -} - static grub_err_t grub_cpio_dir (grub_device_t device, const char *path_in, grub_fs_dir_hook_t hook, void *hook_data) { - struct grub_cpio_data *data; - grub_disk_addr_t ofs; - char *prev, *name, *path, *ptr; - grub_size_t len; - int symlinknest = 0; - - path = grub_strdup (path_in + 1); - if (!path) - return grub_errno; - canonicalize (path); - for (ptr = path + grub_strlen (path) - 1; ptr >= path && *ptr == '/'; ptr--) - *ptr = 0; - - grub_dl_ref (my_mod); - - prev = 0; + struct grub_archelp_data *data; + grub_err_t err; data = grub_cpio_mount (device->disk); if (!data) - { - grub_free (path); - return grub_errno; - } + return grub_errno; - len = grub_strlen (path); - data->hofs = 0; - while (1) - { - grub_int32_t mtime; - grub_uint32_t mode; - grub_err_t err; + err = grub_archelp_dir (data, &arcops, + path_in, hook, hook_data); - if (grub_cpio_find_file (data, &name, &mtime, &ofs, &mode)) - goto fail; - - if (!ofs) - break; - - if (grub_memcmp (path, name, len) == 0 - && (name[len] == 0 || name[len] == '/' || len == 0)) - { - char *p, *n; - - n = name + len; - while (*n == '/') - n++; - - p = grub_strchr (n, '/'); - if (p) - *p = 0; - - if (((!prev) || (grub_strcmp (prev, name) != 0)) && *n != 0) - { - struct grub_dirhook_info info; - grub_memset (&info, 0, sizeof (info)); - info.dir = (p != NULL) || ((mode & ATTR_TYPE) == ATTR_DIR); - info.mtime = mtime; - info.mtimeset = 1; - - if (hook (n, &info, hook_data)) - { - grub_free (name); - goto fail; - } - grub_free (prev); - prev = name; - } - else - { - int restart = 0; - err = handle_symlink (data, name, &path, mode, &restart); - grub_free (name); - if (err) - goto fail; - if (restart) - { - len = grub_strlen (path); - if (++symlinknest == 8) - { - grub_error (GRUB_ERR_SYMLINK_LOOP, - N_("too deep nesting of symlinks")); - goto fail; - } - ofs = 0; - } - } - } - else - grub_free (name); - data->hofs = ofs; - } - -fail: - - grub_free (path); - grub_free (prev); -#ifdef MODE_USTAR - grub_free (data->linkname); -#endif grub_free (data); - grub_dl_unref (my_mod); - - return grub_errno; + return err; } static grub_err_t grub_cpio_open (grub_file_t file, const char *name_in) { - struct grub_cpio_data *data; - grub_disk_addr_t ofs; - char *fn; - char *name = grub_strdup (name_in + 1); - int symlinknest = 0; - - if (!name) - return grub_errno; - - canonicalize (name); - - grub_dl_ref (my_mod); + struct grub_archelp_data *data; + grub_err_t err; data = grub_cpio_mount (file->device->disk); if (!data) + return grub_errno; + + err = grub_archelp_open (data, &arcops, name_in); + if (err) { - grub_free (name); - return grub_errno; + grub_free (data); } - - data->hofs = 0; - while (1) + else { - grub_uint32_t mode; - int restart; - - if (grub_cpio_find_file (data, &fn, NULL, &ofs, &mode)) - goto fail; - - if (!ofs) - { - grub_error (GRUB_ERR_FILE_NOT_FOUND, N_("file `%s' not found"), name_in); - break; - } - - if (handle_symlink (data, fn, &name, mode, &restart)) - { - grub_free (fn); - goto fail; - } - - if (restart) - { - ofs = 0; - if (++symlinknest == 8) - { - grub_error (GRUB_ERR_SYMLINK_LOOP, - N_("too deep nesting of symlinks")); - goto fail; - } - goto no_match; - } - - if (grub_strcmp (name, fn) != 0) - goto no_match; - file->data = data; file->size = data->size; - grub_free (fn); - grub_free (name); - - return GRUB_ERR_NONE; - - no_match: - - grub_free (fn); - data->hofs = ofs; } - -fail: -#ifdef MODE_USTAR - grub_free (data->linkname); -#endif - grub_free (data); - grub_free (name); - - grub_dl_unref (my_mod); - - return grub_errno; + return err; } static grub_ssize_t grub_cpio_read (grub_file_t file, char *buf, grub_size_t len) { - struct grub_cpio_data *data; + struct grub_archelp_data *data; data = file->data; return (grub_disk_read (data->disk, 0, data->dofs + file->offset, @@ -585,16 +209,11 @@ grub_cpio_read (grub_file_t file, char *buf, grub_size_t len) static grub_err_t grub_cpio_close (grub_file_t file) { - struct grub_cpio_data *data; + struct grub_archelp_data *data; data = file->data; -#ifdef MODE_USTAR - grub_free (data->linkname); -#endif grub_free (data); - grub_dl_unref (my_mod); - return grub_errno; } diff --git a/grub-core/fs/newc.c b/grub-core/fs/newc.c index 0a911e543..c7767ed3b 100644 --- a/grub-core/fs/newc.c +++ b/grub-core/fs/newc.c @@ -65,7 +65,6 @@ read_number (const char *str, grub_size_t size) GRUB_MOD_INIT (newc) { grub_fs_register (&grub_cpio_fs); - my_mod = mod; } GRUB_MOD_FINI (newc) diff --git a/grub-core/fs/odc.c b/grub-core/fs/odc.c index 99e9cd629..7b8a7220e 100644 --- a/grub-core/fs/odc.c +++ b/grub-core/fs/odc.c @@ -20,6 +20,7 @@ #include #define ALIGN_CPIO(x) x + #define MAGIC "070707" struct head { @@ -52,7 +53,6 @@ read_number (const char *str, grub_size_t size) GRUB_MOD_INIT (odc) { grub_fs_register (&grub_cpio_fs); - my_mod = mod; } GRUB_MOD_FINI (odc) diff --git a/grub-core/fs/tar.c b/grub-core/fs/tar.c index bf3ce8c3f..85382974c 100644 --- a/grub-core/fs/tar.c +++ b/grub-core/fs/tar.c @@ -18,8 +18,15 @@ */ #include +#include +#include -#define MODE_USTAR 1 +#include +#include +#include +#include + +GRUB_MOD_LICENSE ("GPLv3+"); /* tar support */ #define MAGIC "ustar" @@ -52,14 +59,277 @@ read_number (const char *str, grub_size_t size) return ret; } -#define FSNAME "tarfs" +struct grub_archelp_data +{ + grub_disk_t disk; + grub_off_t hofs, next_hofs; + grub_off_t dofs; + grub_off_t size; + char *linkname; + grub_size_t linkname_alloc; +}; -#include "cpio_common.c" +static grub_err_t +grub_cpio_find_file (struct grub_archelp_data *data, char **name, + grub_int32_t *mtime, + grub_uint32_t *mode) +{ + struct head hd; + int reread = 0, have_longname = 0, have_longlink = 0; + + data->hofs = data->next_hofs; + + for (reread = 0; reread < 3; reread++) + { + if (grub_disk_read (data->disk, 0, data->hofs, sizeof (hd), &hd)) + return grub_errno; + + if (!hd.name[0] && !hd.prefix[0]) + { + *mode = GRUB_ARCHELP_ATTR_END; + return GRUB_ERR_NONE; + } + + if (grub_memcmp (hd.magic, MAGIC, sizeof (MAGIC) - 1)) + return grub_error (GRUB_ERR_BAD_FS, "invalid tar archive"); + + if (hd.typeflag == 'L') + { + grub_err_t err; + grub_size_t namesize = read_number (hd.size, sizeof (hd.size)); + *name = grub_malloc (namesize + 1); + if (*name == NULL) + return grub_errno; + err = grub_disk_read (data->disk, 0, + data->hofs + GRUB_DISK_SECTOR_SIZE, namesize, + *name); + (*name)[namesize] = 0; + if (err) + return err; + data->hofs += GRUB_DISK_SECTOR_SIZE + + ((namesize + GRUB_DISK_SECTOR_SIZE - 1) & + ~(GRUB_DISK_SECTOR_SIZE - 1)); + have_longname = 1; + continue; + } + + if (hd.typeflag == 'K') + { + grub_err_t err; + grub_size_t linksize = read_number (hd.size, sizeof (hd.size)); + if (data->linkname_alloc < linksize + 1) + { + char *n; + n = grub_malloc (2 * (linksize + 1)); + if (!n) + return grub_errno; + grub_free (data->linkname); + data->linkname = n; + data->linkname_alloc = 2 * (linksize + 1); + } + + err = grub_disk_read (data->disk, 0, + data->hofs + GRUB_DISK_SECTOR_SIZE, linksize, + data->linkname); + if (err) + return err; + data->linkname[linksize] = 0; + data->hofs += GRUB_DISK_SECTOR_SIZE + + ((linksize + GRUB_DISK_SECTOR_SIZE - 1) & + ~(GRUB_DISK_SECTOR_SIZE - 1)); + have_longlink = 1; + continue; + } + + if (!have_longname) + { + grub_size_t extra_size = 0; + + while (extra_size < sizeof (hd.prefix) + && hd.prefix[extra_size]) + extra_size++; + *name = grub_malloc (sizeof (hd.name) + extra_size + 2); + if (*name == NULL) + return grub_errno; + if (hd.prefix[0]) + { + grub_memcpy (*name, hd.prefix, extra_size); + (*name)[extra_size++] = '/'; + } + grub_memcpy (*name + extra_size, hd.name, sizeof (hd.name)); + (*name)[extra_size + sizeof (hd.name)] = 0; + } + + data->size = read_number (hd.size, sizeof (hd.size)); + data->dofs = data->hofs + GRUB_DISK_SECTOR_SIZE; + data->next_hofs = data->dofs + ((data->size + GRUB_DISK_SECTOR_SIZE - 1) & + ~(GRUB_DISK_SECTOR_SIZE - 1)); + if (mtime) + *mtime = read_number (hd.mtime, sizeof (hd.mtime)); + if (mode) + { + *mode = read_number (hd.mode, sizeof (hd.mode)); + switch (hd.typeflag) + { + /* Hardlink. */ + case '1': + /* Symlink. */ + case '2': + *mode |= GRUB_ARCHELP_ATTR_LNK; + break; + case '0': + *mode |= GRUB_ARCHELP_ATTR_FILE; + break; + case '5': + *mode |= GRUB_ARCHELP_ATTR_DIR; + break; + } + } + if (!have_longlink) + { + if (data->linkname_alloc < 101) + { + char *n; + n = grub_malloc (101); + if (!n) + return grub_errno; + grub_free (data->linkname); + data->linkname = n; + data->linkname_alloc = 101; + } + grub_memcpy (data->linkname, hd.linkname, sizeof (hd.linkname)); + data->linkname[100] = 0; + } + return GRUB_ERR_NONE; + } + return GRUB_ERR_NONE; +} + +static char * +grub_cpio_get_link_target (struct grub_archelp_data *data) +{ + return grub_strdup (data->linkname); +} + +static void +grub_cpio_rewind (struct grub_archelp_data *data) +{ + data->next_hofs = 0; +} + +static struct grub_archelp_ops arcops = + { + .find_file = grub_cpio_find_file, + .get_link_target = grub_cpio_get_link_target, + .rewind = grub_cpio_rewind + }; + +static struct grub_archelp_data * +grub_cpio_mount (grub_disk_t disk) +{ + struct head hd; + struct grub_archelp_data *data; + + if (grub_disk_read (disk, 0, 0, sizeof (hd), &hd)) + goto fail; + + if (grub_memcmp (hd.magic, MAGIC, sizeof (MAGIC) - 1)) + goto fail; + + data = (struct grub_archelp_data *) grub_zalloc (sizeof (*data)); + if (!data) + goto fail; + + data->disk = disk; + + return data; + +fail: + grub_error (GRUB_ERR_BAD_FS, "not a tarfs filesystem"); + return 0; +} + +static grub_err_t +grub_cpio_dir (grub_device_t device, const char *path_in, + grub_fs_dir_hook_t hook, void *hook_data) +{ + struct grub_archelp_data *data; + grub_err_t err; + + data = grub_cpio_mount (device->disk); + if (!data) + return grub_errno; + + err = grub_archelp_dir (data, &arcops, + path_in, hook, hook_data); + + grub_free (data->linkname); + grub_free (data); + + return err; +} + +static grub_err_t +grub_cpio_open (grub_file_t file, const char *name_in) +{ + struct grub_archelp_data *data; + grub_err_t err; + + data = grub_cpio_mount (file->device->disk); + if (!data) + return grub_errno; + + err = grub_archelp_open (data, &arcops, name_in); + if (err) + { + grub_free (data->linkname); + grub_free (data); + } + else + { + file->data = data; + file->size = data->size; + } + return err; +} + +static grub_ssize_t +grub_cpio_read (grub_file_t file, char *buf, grub_size_t len) +{ + struct grub_archelp_data *data; + + data = file->data; + return (grub_disk_read (data->disk, 0, data->dofs + file->offset, + len, buf)) ? -1 : (grub_ssize_t) len; +} + +static grub_err_t +grub_cpio_close (grub_file_t file) +{ + struct grub_archelp_data *data; + + data = file->data; + grub_free (data->linkname); + grub_free (data); + + return grub_errno; +} + +static struct grub_fs grub_cpio_fs = { + .name = "tarfs", + .dir = grub_cpio_dir, + .open = grub_cpio_open, + .read = grub_cpio_read, + .close = grub_cpio_close, +#ifdef GRUB_UTIL + .reserved_first_sector = 0, + .blocklist_install = 0, +#endif +}; GRUB_MOD_INIT (tar) { grub_fs_register (&grub_cpio_fs); - my_mod = mod; } GRUB_MOD_FINI (tar) diff --git a/include/grub/archelp.h b/include/grub/archelp.h new file mode 100644 index 000000000..2611d7f45 --- /dev/null +++ b/include/grub/archelp.h @@ -0,0 +1,63 @@ +/* archelp.h -- Archive helper functions */ +/* + * 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 . + */ + +#ifndef GRUB_ARCHELP_HEADER +#define GRUB_ARCHELP_HEADER 1 + +#include +#include + +typedef enum + { + GRUB_ARCHELP_ATTR_TYPE = 0160000, + GRUB_ARCHELP_ATTR_FILE = 0100000, + GRUB_ARCHELP_ATTR_DIR = 0040000, + GRUB_ARCHELP_ATTR_LNK = 0120000, + GRUB_ARCHELP_ATTR_NOTIME = 0x80000000, + GRUB_ARCHELP_ATTR_END = 0xffffffff + } grub_archelp_mode_t; + +struct grub_archelp_data; + +struct grub_archelp_ops +{ + grub_err_t + (*find_file) (struct grub_archelp_data *data, char **name, + grub_int32_t *mtime, + grub_archelp_mode_t *mode); + + char * + (*get_link_target) (struct grub_archelp_data *data); + + void + (*rewind) (struct grub_archelp_data *data); +}; + +grub_err_t +grub_archelp_dir (struct grub_archelp_data *data, + struct grub_archelp_ops *ops, + const char *path_in, + grub_fs_dir_hook_t hook, void *hook_data); + +grub_err_t +grub_archelp_open (struct grub_archelp_data *data, + struct grub_archelp_ops *ops, + const char *name_in); + +#endif diff --git a/include/grub/cbfs_core.h b/include/grub/cbfs_core.h new file mode 100644 index 000000000..5067b6ef7 --- /dev/null +++ b/include/grub/cbfs_core.h @@ -0,0 +1,178 @@ +/* + * This file is part of the coreboot project. + * + * Copyright (C) 2008 Jordan Crouse + * Copyright (C) 2012 Google, Inc. + * Copyright (C) 2013 The Chromium OS Authors. All rights reserved. + * + * This file is dual-licensed. You can choose between: + * - The GNU GPL, version 2, as published by the Free Software Foundation + * - The revised BSD license (without advertising clause) + * + * --------------------------------------------------------------------------- + * This program 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; version 2 of the License. + * + * This program 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 this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA, 02110-1301 USA + * --------------------------------------------------------------------------- + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * --------------------------------------------------------------------------- + */ + +#ifndef _CBFS_CORE_H_ +#define _CBFS_CORE_H_ + +#include + +/** These are standard values for the known compression + alogrithms that coreboot knows about for stages and + payloads. Of course, other CBFS users can use whatever + values they want, as long as they understand them. */ + +#define CBFS_COMPRESS_NONE 0 +#define CBFS_COMPRESS_LZMA 1 + +/** These are standard component types for well known + components (i.e - those that coreboot needs to consume. + Users are welcome to use any other value for their + components */ + +#define CBFS_TYPE_STAGE 0x10 +#define CBFS_TYPE_PAYLOAD 0x20 +#define CBFS_TYPE_OPTIONROM 0x30 +#define CBFS_TYPE_BOOTSPLASH 0x40 +#define CBFS_TYPE_RAW 0x50 +#define CBFS_TYPE_VSA 0x51 +#define CBFS_TYPE_MBI 0x52 +#define CBFS_TYPE_MICROCODE 0x53 +#define CBFS_COMPONENT_CMOS_DEFAULT 0xaa +#define CBFS_COMPONENT_CMOS_LAYOUT 0x01aa + +#define CBFS_HEADER_MAGIC 0x4F524243 +#define CBFS_HEADER_VERSION1 0x31313131 +#define CBFS_HEADER_VERSION2 0x31313132 +#define CBFS_HEADER_VERSION CBFS_HEADER_VERSION2 + +#define CBFS_HEADER_INVALID_ADDRESS ((void*)(0xffffffff)) + +/** this is the master cbfs header - it need to be located somewhere available + to bootblock (to load romstage). Where it actually lives is up to coreboot. + On x86, a pointer to this header will live at 0xFFFFFFFC. + For other platforms, you need to define CONFIG_CBFS_HEADER_ROM_OFFSET */ + +struct cbfs_header { + grub_uint32_t magic; + grub_uint32_t version; + grub_uint32_t romsize; + grub_uint32_t bootblocksize; + grub_uint32_t align; + grub_uint32_t offset; + grub_uint32_t architecture; + grub_uint32_t pad[1]; +} __attribute__((packed)); + +/* "Unknown" refers to CBFS headers version 1, + * before the architecture was defined (i.e., x86 only). + */ +#define CBFS_ARCHITECTURE_UNKNOWN 0xFFFFFFFF +#define CBFS_ARCHITECTURE_X86 0x00000001 +#define CBFS_ARCHITECTURE_ARMV7 0x00000010 + +/** This is a component header - every entry in the CBFS + will have this header. + + This is how the component is arranged in the ROM: + + -------------- <- 0 + component header + -------------- <- sizeof(struct component) + component name + -------------- <- offset + data + ... + -------------- <- offset + len +*/ + +#define CBFS_FILE_MAGIC "LARCHIVE" + +struct cbfs_file { + char magic[8]; + grub_uint32_t len; + grub_uint32_t type; + grub_uint32_t checksum; + grub_uint32_t offset; +} __attribute__((packed)); + +/*** Component sub-headers ***/ + +/* Following are component sub-headers for the "standard" + component types */ + +/** This is the sub-header for stage components. Stages are + loaded by coreboot during the normal boot process */ + +struct cbfs_stage { + grub_uint32_t compression; /** Compression type */ + grub_uint64_t entry; /** entry point */ + grub_uint64_t load; /** Where to load in memory */ + grub_uint32_t len; /** length of data to load */ + grub_uint32_t memlen; /** total length of object in memory */ +} __attribute__((packed)); + +/** this is the sub-header for payload components. Payloads + are loaded by coreboot at the end of the boot process */ + +struct cbfs_payload_segment { + grub_uint32_t type; + grub_uint32_t compression; + grub_uint32_t offset; + grub_uint64_t load_addr; + grub_uint32_t len; + grub_uint32_t mem_len; +} __attribute__((packed)); + +struct cbfs_payload { + struct cbfs_payload_segment segments; +}; + +#define PAYLOAD_SEGMENT_CODE 0x45444F43 +#define PAYLOAD_SEGMENT_DATA 0x41544144 +#define PAYLOAD_SEGMENT_BSS 0x20535342 +#define PAYLOAD_SEGMENT_PARAMS 0x41524150 +#define PAYLOAD_SEGMENT_ENTRY 0x52544E45 + +struct cbfs_optionrom { + grub_uint32_t compression; + grub_uint32_t len; +} __attribute__((packed)); + +#endif diff --git a/include/grub/disk.h b/include/grub/disk.h index 8fa09a6df..c91583dfd 100644 --- a/include/grub/disk.h +++ b/include/grub/disk.h @@ -46,6 +46,7 @@ enum grub_disk_dev_id GRUB_DISK_DEVICE_ARCDISK_ID, GRUB_DISK_DEVICE_HOSTDISK_ID, GRUB_DISK_DEVICE_PROCFS_ID, + GRUB_DISK_DEVICE_CBFSDISK_ID }; struct grub_disk;