1 /* 2 * (C) Copyright 2015 Miao Yan <yanmiaobest@gmail.com> 3 * 4 * SPDX-License-Identifier: GPL-2.0+ 5 */ 6 7 #include <common.h> 8 #include <command.h> 9 #include <errno.h> 10 #include <malloc.h> 11 #include <qfw.h> 12 #include <asm/io.h> 13 #include <linux/list.h> 14 15 static bool fwcfg_present; 16 static bool fwcfg_dma_present; 17 static struct fw_cfg_arch_ops *fwcfg_arch_ops; 18 19 static LIST_HEAD(fw_list); 20 21 /* Read configuration item using fw_cfg PIO interface */ 22 static void qemu_fwcfg_read_entry_pio(uint16_t entry, 23 uint32_t size, void *address) 24 { 25 debug("qemu_fwcfg_read_entry_pio: entry 0x%x, size %u address %p\n", 26 entry, size, address); 27 28 return fwcfg_arch_ops->arch_read_pio(entry, size, address); 29 } 30 31 /* Read configuration item using fw_cfg DMA interface */ 32 static void qemu_fwcfg_read_entry_dma(uint16_t entry, 33 uint32_t size, void *address) 34 { 35 struct fw_cfg_dma_access dma; 36 37 dma.length = cpu_to_be32(size); 38 dma.address = cpu_to_be64((uintptr_t)address); 39 dma.control = cpu_to_be32(FW_CFG_DMA_READ); 40 41 /* 42 * writting FW_CFG_INVALID will cause read operation to resume at 43 * last offset, otherwise read will start at offset 0 44 */ 45 if (entry != FW_CFG_INVALID) 46 dma.control |= cpu_to_be32(FW_CFG_DMA_SELECT | (entry << 16)); 47 48 barrier(); 49 50 debug("qemu_fwcfg_read_entry_dma: entry 0x%x, size %u address %p, control 0x%x\n", 51 entry, size, address, be32_to_cpu(dma.control)); 52 53 fwcfg_arch_ops->arch_read_dma(&dma); 54 } 55 56 bool qemu_fwcfg_present(void) 57 { 58 return fwcfg_present; 59 } 60 61 bool qemu_fwcfg_dma_present(void) 62 { 63 return fwcfg_dma_present; 64 } 65 66 void qemu_fwcfg_read_entry(uint16_t entry, uint32_t length, void *address) 67 { 68 if (fwcfg_dma_present) 69 qemu_fwcfg_read_entry_dma(entry, length, address); 70 else 71 qemu_fwcfg_read_entry_pio(entry, length, address); 72 } 73 74 int qemu_fwcfg_online_cpus(void) 75 { 76 uint16_t nb_cpus; 77 78 if (!fwcfg_present) 79 return -ENODEV; 80 81 qemu_fwcfg_read_entry(FW_CFG_NB_CPUS, 2, &nb_cpus); 82 83 return le16_to_cpu(nb_cpus); 84 } 85 86 int qemu_fwcfg_read_firmware_list(void) 87 { 88 int i; 89 uint32_t count; 90 struct fw_file *file; 91 struct list_head *entry; 92 93 /* don't read it twice */ 94 if (!list_empty(&fw_list)) 95 return 0; 96 97 qemu_fwcfg_read_entry(FW_CFG_FILE_DIR, 4, &count); 98 if (!count) 99 return 0; 100 101 count = be32_to_cpu(count); 102 for (i = 0; i < count; i++) { 103 file = malloc(sizeof(*file)); 104 if (!file) { 105 printf("error: allocating resource\n"); 106 goto err; 107 } 108 qemu_fwcfg_read_entry(FW_CFG_INVALID, 109 sizeof(struct fw_cfg_file), &file->cfg); 110 file->addr = 0; 111 list_add_tail(&file->list, &fw_list); 112 } 113 114 return 0; 115 116 err: 117 list_for_each(entry, &fw_list) { 118 file = list_entry(entry, struct fw_file, list); 119 free(file); 120 } 121 122 return -ENOMEM; 123 } 124 125 struct fw_file *qemu_fwcfg_find_file(const char *name) 126 { 127 struct list_head *entry; 128 struct fw_file *file; 129 130 list_for_each(entry, &fw_list) { 131 file = list_entry(entry, struct fw_file, list); 132 if (!strcmp(file->cfg.name, name)) 133 return file; 134 } 135 136 return NULL; 137 } 138 139 struct fw_file *qemu_fwcfg_file_iter_init(struct fw_cfg_file_iter *iter) 140 { 141 iter->entry = fw_list.next; 142 return list_entry((struct list_head *)iter->entry, 143 struct fw_file, list); 144 } 145 146 struct fw_file *qemu_fwcfg_file_iter_next(struct fw_cfg_file_iter *iter) 147 { 148 iter->entry = ((struct list_head *)iter->entry)->next; 149 return list_entry((struct list_head *)iter->entry, 150 struct fw_file, list); 151 } 152 153 bool qemu_fwcfg_file_iter_end(struct fw_cfg_file_iter *iter) 154 { 155 return iter->entry == &fw_list; 156 } 157 158 void qemu_fwcfg_init(struct fw_cfg_arch_ops *ops) 159 { 160 uint32_t qemu; 161 uint32_t dma_enabled; 162 163 fwcfg_present = false; 164 fwcfg_dma_present = false; 165 fwcfg_arch_ops = NULL; 166 167 if (!ops || !ops->arch_read_pio || !ops->arch_read_dma) 168 return; 169 fwcfg_arch_ops = ops; 170 171 qemu_fwcfg_read_entry_pio(FW_CFG_SIGNATURE, 4, &qemu); 172 if (be32_to_cpu(qemu) == QEMU_FW_CFG_SIGNATURE) 173 fwcfg_present = true; 174 175 if (fwcfg_present) { 176 qemu_fwcfg_read_entry_pio(FW_CFG_ID, 1, &dma_enabled); 177 if (dma_enabled & FW_CFG_DMA_ENABLED) 178 fwcfg_dma_present = true; 179 } 180 } 181