1// SPDX-License-Identifier: GPL-2.0+ 2/* 3 * (C) Copyright 2015 Miao Yan <yanmiaobest@gmail.com> 4 * (C) Copyright 2021 Asherah Connor <ashe@kivikakk.ee> 5 */ 6 7#include <dm.h> 8#include <env.h> 9#include <mapmem.h> 10#include <qfw.h> 11#include <stdlib.h> 12#include <dm/uclass.h> 13 14int qfw_get_dev(struct udevice **devp) 15{ 16 return uclass_first_device_err(UCLASS_QFW, devp); 17} 18 19int qfw_online_cpus(struct udevice *dev) 20{ 21 u16 nb_cpus; 22 23 qfw_read_entry(dev, FW_CFG_NB_CPUS, 2, &nb_cpus); 24 25 return le16_to_cpu(nb_cpus); 26} 27 28int qfw_read_firmware_list(struct udevice *dev) 29{ 30 int i; 31 u32 count; 32 struct fw_file *file; 33 struct list_head *entry; 34 35 struct qfw_dev *qdev = dev_get_uclass_priv(dev); 36 37 /* don't read it twice */ 38 if (!list_empty(&qdev->fw_list)) 39 return 0; 40 41 qfw_read_entry(dev, FW_CFG_FILE_DIR, 4, &count); 42 if (!count) 43 return 0; 44 45 count = be32_to_cpu(count); 46 for (i = 0; i < count; i++) { 47 file = malloc(sizeof(*file)); 48 if (!file) { 49 printf("error: allocating resource\n"); 50 goto err; 51 } 52 qfw_read_entry(dev, FW_CFG_INVALID, 53 sizeof(struct fw_cfg_file), &file->cfg); 54 file->addr = 0; 55 list_add_tail(&file->list, &qdev->fw_list); 56 } 57 58 return 0; 59 60err: 61 list_for_each(entry, &qdev->fw_list) { 62 file = list_entry(entry, struct fw_file, list); 63 free(file); 64 } 65 66 return -ENOMEM; 67} 68 69struct fw_file *qfw_find_file(struct udevice *dev, const char *name) 70{ 71 struct list_head *entry; 72 struct fw_file *file; 73 74 struct qfw_dev *qdev = dev_get_uclass_priv(dev); 75 76 list_for_each(entry, &qdev->fw_list) { 77 file = list_entry(entry, struct fw_file, list); 78 if (!strcmp(file->cfg.name, name)) 79 return file; 80 } 81 82 return NULL; 83} 84 85struct fw_file *qfw_file_iter_init(struct udevice *dev, 86 struct fw_cfg_file_iter *iter) 87{ 88 struct qfw_dev *qdev = dev_get_uclass_priv(dev); 89 90 iter->entry = qdev->fw_list.next; 91 iter->end = &qdev->fw_list; 92 return list_entry((struct list_head *)iter->entry, 93 struct fw_file, list); 94} 95 96struct fw_file *qfw_file_iter_next(struct fw_cfg_file_iter *iter) 97{ 98 iter->entry = ((struct list_head *)iter->entry)->next; 99 return list_entry((struct list_head *)iter->entry, 100 struct fw_file, list); 101} 102 103bool qfw_file_iter_end(struct fw_cfg_file_iter *iter) 104{ 105 return iter->entry == iter->end; 106} 107 108int qemu_fwcfg_setup_kernel(struct udevice *qfw_dev, ulong load_addr, 109 ulong initrd_addr) 110{ 111 char *data_addr; 112 u32 setup_size, kernel_size, cmdline_size, initrd_size; 113 114 qfw_read_entry(qfw_dev, FW_CFG_SETUP_SIZE, 4, &setup_size); 115 qfw_read_entry(qfw_dev, FW_CFG_KERNEL_SIZE, 4, &kernel_size); 116 117 if (!kernel_size) { 118 printf("fatal: no kernel available\n"); 119 return -ENOENT; 120 } 121 122 data_addr = map_sysmem(load_addr, 0); 123 if (setup_size) { 124 qfw_read_entry(qfw_dev, FW_CFG_SETUP_DATA, 125 le32_to_cpu(setup_size), data_addr); 126 data_addr += le32_to_cpu(setup_size); 127 } 128 129 qfw_read_entry(qfw_dev, FW_CFG_KERNEL_DATA, 130 le32_to_cpu(kernel_size), data_addr); 131 data_addr += le32_to_cpu(kernel_size); 132 env_set_hex("filesize", le32_to_cpu(kernel_size)); 133 134 data_addr = map_sysmem(initrd_addr, 0); 135 qfw_read_entry(qfw_dev, FW_CFG_INITRD_SIZE, 4, &initrd_size); 136 if (!initrd_size) { 137 printf("warning: no initrd available\n"); 138 } else { 139 qfw_read_entry(qfw_dev, FW_CFG_INITRD_DATA, 140 le32_to_cpu(initrd_size), data_addr); 141 data_addr += le32_to_cpu(initrd_size); 142 env_set_hex("filesize", le32_to_cpu(initrd_size)); 143 } 144 145 qfw_read_entry(qfw_dev, FW_CFG_CMDLINE_SIZE, 4, &cmdline_size); 146 if (cmdline_size) { 147 qfw_read_entry(qfw_dev, FW_CFG_CMDLINE_DATA, 148 le32_to_cpu(cmdline_size), data_addr); 149 /* 150 * if kernel cmdline only contains '\0', (e.g. no -append 151 * when invoking qemu), do not update bootargs 152 */ 153 if (*data_addr) { 154 if (env_set("bootargs", data_addr) < 0) 155 printf("warning: unable to change bootargs\n"); 156 } 157 } 158 159 printf("loading kernel to address %lx size %x", load_addr, 160 le32_to_cpu(kernel_size)); 161 if (initrd_size) 162 printf(" initrd %lx size %x\n", initrd_addr, 163 le32_to_cpu(initrd_size)); 164 else 165 printf("\n"); 166 167 return 0; 168} 169