The GRUB is failing to build with GCC-12 in many places like this:
In function 'init_cbfsdisk',
inlined from 'grub_mod_init' at ../../grub-core/fs/cbfs.c:391:3:
../../grub-core/fs/cbfs.c:345:7: error: array subscript 0 is outside array bounds of 'grub_uint32_t[0]' {aka 'unsigned int[]'} [-Werror=array-bounds]
345 | ptr = *(grub_uint32_t *) 0xfffffffc;
| ~~~~^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
This is caused by GCC regression in 11/12 [1]. In a nut shell, the
warning is about detected invalid accesses at non-zero offsets to NULL
pointers. Since hardwired constant address is treated as NULL plus an
offset in the same underlying code, the warning is therefore triggered.
Instead of inserting #pragma all over the places where literal pointers
are accessed to avoid diagnosing array-bounds, we can try to borrow the
idea from Linux kernel that the absolute_pointer() macro [2][3] is used
to disconnect a pointer using literal address from it's original object,
hence GCC won't be able to make assumptions on the boundary while doing
pointer arithmetic. With that we can greatly reduce the code we have to
cover up by making initial literal pointer assignment to use the new
wrapper but not having to track everywhere literal pointers are
accessed. This also makes code looks cleaner.
Please note the grub_absolute_pointer() macro requires to be invoked in
a function as long as it is compound expression. Some global variables
with literal pointers has been changed to local ones in order to use
grub_absolute_pointer() to initialize it. The shuffling is basically done
in a selective and careful way that the variable's scope doesn't matter
being local or global, for example, the global variable must not get
modified at run time throughout. For the record, here's the list of
global variables got shuffled in this patch:
grub-core/commands/i386/pc/drivemap.c:int13slot
grub-core/term/i386/pc/console.c:bios_data_area
grub-core/term/ns8250.c:serial_hw_io_addr
[1] https://gcc.gnu.org/bugzilla/show_bug.cgi?id=99578
[2] https://elixir.bootlin.com/linux/v5.16.14/source/include/linux/compiler.h#L180
[3] https://elixir.bootlin.com/linux/v5.16.14/source/include/linux/compiler-gcc.h#L31
Signed-off-by: Michael Chang <mchang@suse.com>
Reviewed-by: Daniel Kiper <daniel.kiper@oracle.com>
403 lines
10 KiB
C
403 lines
10 KiB
C
/* 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 <http://www.gnu.org/licenses/>.
|
|
*/
|
|
|
|
#include <grub/misc.h>
|
|
#include <grub/disk.h>
|
|
#include <grub/archelp.h>
|
|
|
|
#include <grub/file.h>
|
|
#include <grub/mm.h>
|
|
#include <grub/dl.h>
|
|
#include <grub/i18n.h>
|
|
#include <grub/cbfs_core.h>
|
|
|
|
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);
|
|
|
|
*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 = NULL;
|
|
grub_uint32_t ptr;
|
|
grub_off_t header_off;
|
|
struct cbfs_header head;
|
|
|
|
if (grub_disk_native_sectors (disk) == GRUB_DISK_SIZE_UNKNOWN)
|
|
goto fail;
|
|
|
|
if (grub_disk_read (disk, grub_disk_native_sectors (disk) - 1,
|
|
GRUB_DISK_SECTOR_SIZE - sizeof (ptr),
|
|
sizeof (ptr), &ptr))
|
|
goto fail;
|
|
|
|
ptr = grub_cpu_to_le32 (ptr);
|
|
header_off = (grub_disk_native_sectors (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_native_sectors (disk) << GRUB_DISK_SECTOR_BITS)
|
|
- (grub_be_to_cpu32 (head.romsize) - grub_be_to_cpu32 (head.offset));
|
|
data->cbfs_end = (grub_disk_native_sectors (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_native_sectors (disk) << GRUB_DISK_SECTOR_BITS))
|
|
goto fail;
|
|
if (data->cbfs_end > (grub_disk_native_sectors (disk) << GRUB_DISK_SECTOR_BITS))
|
|
data->cbfs_end = (grub_disk_native_sectors (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_free (data);
|
|
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;
|
|
grub_ssize_t ret;
|
|
|
|
data = file->data;
|
|
data->disk->read_hook = file->read_hook;
|
|
data->disk->read_hook_data = file->read_hook_data;
|
|
|
|
ret = (grub_disk_read (data->disk, 0, data->dofs + file->offset,
|
|
len, buf)) ? -1 : (grub_ssize_t) len;
|
|
data->disk->read_hook = 0;
|
|
|
|
return ret;
|
|
}
|
|
|
|
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) \
|
|
&& !defined (GRUB_MACHINE_EMU) && !defined (GRUB_MACHINE_XEN)
|
|
|
|
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->max_agglomerate = GRUB_DISK_MAX_MAX_AGGLOMERATE;
|
|
disk->id = 0;
|
|
|
|
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,
|
|
.disk_iterate = grub_cbfsdisk_iterate,
|
|
.disk_open = grub_cbfsdisk_open,
|
|
.disk_close = grub_cbfsdisk_close,
|
|
.disk_read = grub_cbfsdisk_read,
|
|
.disk_write = grub_cbfsdisk_write,
|
|
.next = 0
|
|
};
|
|
|
|
static void
|
|
init_cbfsdisk (void)
|
|
{
|
|
grub_uint32_t ptr;
|
|
struct cbfs_header *head;
|
|
|
|
ptr = *((grub_uint32_t *) grub_absolute_pointer (0xfffffffc));
|
|
head = (struct cbfs_header *) (grub_addr_t) ptr;
|
|
grub_dprintf ("cbfs", "head=%p\n", head);
|
|
|
|
/* coreboot current supports only ROMs <= 16 MiB. Bigger ROMs will
|
|
have problems as RCBA is 18 MiB below end of 32-bit typically,
|
|
so either memory map would have to be rearranged or we'd need to support
|
|
reading ROMs through controller directly.
|
|
*/
|
|
if (ptr < 0xff000000
|
|
|| 0xffffffff - ptr < (grub_uint32_t) sizeof (*head) + 0xf
|
|
|| !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",
|
|
.fs_dir = grub_cbfs_dir,
|
|
.fs_open = grub_cbfs_open,
|
|
.fs_read = grub_cbfs_read,
|
|
.fs_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) && !defined (GRUB_MACHINE_EMU) && !defined (GRUB_MACHINE_XEN)
|
|
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) && !defined (GRUB_MACHINE_EMU) && !defined (GRUB_MACHINE_XEN)
|
|
fini_cbfsdisk ();
|
|
#endif
|
|
}
|