Currently qfw_locate_file() always prints error messages when it can't find a file. This causes unwanted error output in qemu_get_bootcmd() when the optional "opt/u-boot/bootcmd" file doesn't exist. Adjust qfw_locate_file() to be silent, with a new qfw_locate_file_msg() that shows messages. This allows callers to choose whether missing files should generate error messages. Co-developed-by: Claude <noreply@anthropic.com> Signed-off-by: Simon Glass <sjg@chromium.org>
300 lines
6.6 KiB
C
300 lines
6.6 KiB
C
// SPDX-License-Identifier: GPL-2.0+
|
|
/*
|
|
* (C) Copyright 2015 Miao Yan <yanmiaobest@gmail.com>
|
|
* (C) Copyright 2021 Asherah Connor <ashe@kivikakk.ee>
|
|
*/
|
|
|
|
#include <abuf.h>
|
|
#include <dm.h>
|
|
#include <env.h>
|
|
#include <mapmem.h>
|
|
#include <qfw.h>
|
|
#include <stdlib.h>
|
|
#include <dm/uclass.h>
|
|
|
|
int qfw_get_dev(struct udevice **devp)
|
|
{
|
|
return uclass_first_device_err(UCLASS_QFW, devp);
|
|
}
|
|
|
|
int qfw_online_cpus(struct udevice *dev)
|
|
{
|
|
u16 nb_cpus;
|
|
|
|
qfw_read_entry(dev, FW_CFG_NB_CPUS, 2, &nb_cpus);
|
|
|
|
return le16_to_cpu(nb_cpus);
|
|
}
|
|
|
|
int qfw_read_firmware_list(struct udevice *dev)
|
|
{
|
|
int i;
|
|
u32 count;
|
|
struct fw_file *file;
|
|
struct list_head *entry;
|
|
|
|
struct qfw_dev *qdev = dev_get_uclass_priv(dev);
|
|
|
|
/* don't read it twice */
|
|
if (!list_empty(&qdev->fw_list))
|
|
return 0;
|
|
|
|
qfw_read_entry(dev, FW_CFG_FILE_DIR, 4, &count);
|
|
if (!count)
|
|
return 0;
|
|
|
|
count = be32_to_cpu(count);
|
|
for (i = 0; i < count; i++) {
|
|
file = malloc(sizeof(*file));
|
|
if (!file) {
|
|
printf("error: allocating resource\n");
|
|
goto err;
|
|
}
|
|
qfw_read_entry(dev, FW_CFG_INVALID,
|
|
sizeof(struct fw_cfg_file), &file->cfg);
|
|
file->addr = 0;
|
|
list_add_tail(&file->list, &qdev->fw_list);
|
|
}
|
|
|
|
return 0;
|
|
|
|
err:
|
|
list_for_each(entry, &qdev->fw_list) {
|
|
file = list_entry(entry, struct fw_file, list);
|
|
free(file);
|
|
}
|
|
|
|
return -ENOMEM;
|
|
}
|
|
|
|
struct fw_file *qfw_find_file(struct udevice *dev, const char *name)
|
|
{
|
|
struct list_head *entry;
|
|
struct fw_file *file;
|
|
|
|
struct qfw_dev *qdev = dev_get_uclass_priv(dev);
|
|
|
|
list_for_each(entry, &qdev->fw_list) {
|
|
file = list_entry(entry, struct fw_file, list);
|
|
if (!strcmp(file->cfg.name, name))
|
|
return file;
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
struct fw_file *qfw_file_iter_init(struct udevice *dev,
|
|
struct fw_cfg_file_iter *iter)
|
|
{
|
|
struct qfw_dev *qdev = dev_get_uclass_priv(dev);
|
|
|
|
iter->entry = qdev->fw_list.next;
|
|
iter->end = &qdev->fw_list;
|
|
return list_entry((struct list_head *)iter->entry,
|
|
struct fw_file, list);
|
|
}
|
|
|
|
struct fw_file *qfw_file_iter_next(struct fw_cfg_file_iter *iter)
|
|
{
|
|
iter->entry = ((struct list_head *)iter->entry)->next;
|
|
return list_entry((struct list_head *)iter->entry,
|
|
struct fw_file, list);
|
|
}
|
|
|
|
bool qfw_file_iter_end(struct fw_cfg_file_iter *iter)
|
|
{
|
|
return iter->entry == iter->end;
|
|
}
|
|
|
|
/**
|
|
* qfw_read_size() - Read the size of an entry
|
|
*
|
|
* @sel: Selector value, e.g. FW_CFG_SETUP_SIZE, FW_CFG_CMDLINE_SIZE
|
|
* Return: Size of the entry
|
|
*/
|
|
static ulong qfw_read_size(struct udevice *qfw_dev, enum fw_cfg_selector sel)
|
|
{
|
|
u32 size = 0;
|
|
|
|
qfw_read_entry(qfw_dev, sel, 4, &size);
|
|
|
|
return le32_to_cpu(size);
|
|
}
|
|
|
|
int qemu_fwcfg_read_info(struct udevice *qfw_dev, ulong *setupp, ulong *kernp,
|
|
ulong *initrdp, struct abuf *cmdline,
|
|
ulong *setup_addrp)
|
|
{
|
|
uint cmdline_size;
|
|
|
|
*setupp = qfw_read_size(qfw_dev, FW_CFG_SETUP_SIZE);
|
|
*kernp = qfw_read_size(qfw_dev, FW_CFG_KERNEL_SIZE);
|
|
*initrdp = qfw_read_size(qfw_dev, FW_CFG_INITRD_SIZE);
|
|
cmdline_size = qfw_read_size(qfw_dev, FW_CFG_CMDLINE_SIZE);
|
|
if (!*kernp)
|
|
return -ENOENT;
|
|
|
|
*setup_addrp = qfw_read_size(qfw_dev, FW_CFG_SETUP_ADDR);
|
|
|
|
if (!abuf_init_size(cmdline, cmdline_size))
|
|
return log_msg_ret("qri", -ENOMEM);
|
|
qfw_read_entry(qfw_dev, FW_CFG_CMDLINE_DATA, cmdline_size,
|
|
cmdline->data);
|
|
|
|
return 0;
|
|
}
|
|
|
|
void qemu_fwcfg_read_files(struct udevice *qfw_dev, const struct abuf *setup,
|
|
const struct abuf *kern, const struct abuf *initrd)
|
|
{
|
|
if (setup->size) {
|
|
qfw_read_entry(qfw_dev, FW_CFG_SETUP_DATA, setup->size,
|
|
setup->data);
|
|
}
|
|
qfw_read_entry(qfw_dev, FW_CFG_KERNEL_DATA, kern->size, kern->data);
|
|
if (initrd->size) {
|
|
qfw_read_entry(qfw_dev, FW_CFG_INITRD_DATA, initrd->size,
|
|
initrd->data);
|
|
}
|
|
}
|
|
|
|
int qemu_fwcfg_setup_kernel(struct udevice *qfw_dev, ulong load_addr,
|
|
ulong initrd_addr)
|
|
{
|
|
ulong setup_size, kernel_size, initrd_size, setup_addr;
|
|
struct abuf cmdline, setup, kern, initrd;
|
|
int ret;
|
|
|
|
ret = qemu_fwcfg_read_info(qfw_dev, &setup_size, &initrd_size,
|
|
&kernel_size, &cmdline, &setup_addr);
|
|
if (ret) {
|
|
printf("fatal: no kernel available\n");
|
|
return log_msg_ret("qsk", ret);
|
|
}
|
|
|
|
/*
|
|
* always put the setup area where QEMU wants it, since it includes
|
|
* absolute pointers to itself
|
|
*/
|
|
abuf_init_const_addr(&setup, setup_addr, 0);
|
|
abuf_init_const_addr(&kern, load_addr, 0);
|
|
|
|
abuf_init_const_addr(&initrd, initrd_addr, 0);
|
|
qemu_fwcfg_read_files(qfw_dev, &setup, &kern, &initrd);
|
|
|
|
env_set_hex("filesize", kern.size);
|
|
|
|
if (!initrd_size)
|
|
printf("warning: no initrd available\n");
|
|
else
|
|
env_set_hex("filesize", initrd_size);
|
|
|
|
if (cmdline.data) {
|
|
/*
|
|
* if kernel cmdline only contains '\0', (e.g. no -append
|
|
* when invoking qemu), do not update bootargs
|
|
*/
|
|
if (*(char *)cmdline.data) {
|
|
if (env_set("bootargs", cmdline.data) < 0)
|
|
printf("warning: unable to change bootargs\n");
|
|
}
|
|
}
|
|
abuf_uninit(&cmdline);
|
|
|
|
printf("loading kernel to address %lx size %zx", abuf_addr(&kern),
|
|
kern.size);
|
|
if (initrd_size)
|
|
printf(" initrd %lx size %lx\n", abuf_addr(&initrd),
|
|
initrd_size);
|
|
else
|
|
printf("\n");
|
|
|
|
return 0;
|
|
}
|
|
|
|
int qfw_locate_file(struct udevice *dev, const char *fname,
|
|
enum fw_cfg_selector *selectp, ulong *sizep)
|
|
{
|
|
struct fw_file *file;
|
|
int ret;
|
|
|
|
/* make sure fw_list is loaded */
|
|
ret = qfw_read_firmware_list(dev);
|
|
if (ret)
|
|
return -EINVAL;
|
|
|
|
file = qfw_find_file(dev, fname);
|
|
if (!file)
|
|
return -ENOENT;
|
|
|
|
*selectp = be16_to_cpu(file->cfg.select);
|
|
*sizep = be32_to_cpu(file->cfg.size);
|
|
|
|
return 0;
|
|
}
|
|
|
|
int qfw_locate_file_msg(struct udevice *dev, const char *fname,
|
|
enum fw_cfg_selector *selectp, ulong *sizep)
|
|
{
|
|
int ret;
|
|
|
|
ret = qfw_locate_file(dev, fname, selectp, sizep);
|
|
if (ret == -EINVAL)
|
|
printf("error: can't read firmware file list\n");
|
|
else if (ret == -ENOENT)
|
|
printf("error: can't find %s\n", fname);
|
|
|
|
return ret;
|
|
}
|
|
|
|
int qfw_load_file(struct udevice *dev, const char *fname, ulong addr)
|
|
{
|
|
enum fw_cfg_selector select;
|
|
ulong size;
|
|
int ret;
|
|
|
|
ret = qfw_locate_file_msg(dev, fname, &select, &size);
|
|
if (ret)
|
|
return ret;
|
|
|
|
qfw_read_entry(dev, select, size, map_sysmem(addr, size));
|
|
|
|
return 0;
|
|
}
|
|
|
|
int qfw_get_file(struct udevice *dev, const char *fname, struct abuf *loader)
|
|
{
|
|
enum fw_cfg_selector select;
|
|
ulong size;
|
|
int ret;
|
|
|
|
ret = qfw_locate_file_msg(dev, fname, &select, &size);
|
|
if (ret)
|
|
return ret;
|
|
|
|
if (!abuf_init_size(loader, size)) {
|
|
printf("error: table-loader out of memory\n");
|
|
return -ENOMEM;
|
|
}
|
|
|
|
qfw_read_entry(dev, select, size, loader->data);
|
|
|
|
return 0;
|
|
}
|
|
|
|
int qfw_get_table_loader(struct udevice *dev, struct abuf *loader)
|
|
{
|
|
int ret;
|
|
|
|
ret = qfw_get_file(dev, "etc/table-loader", loader);
|
|
if (ret)
|
|
return ret;
|
|
if ((loader->size % sizeof(struct bios_linker_entry)) != 0) {
|
|
printf("error: table-loader maybe corrupted\n");
|
|
abuf_uninit(loader);
|
|
return -EINVAL;
|
|
}
|
|
|
|
return 0;
|
|
}
|