1 /* 2 * libqos fw_cfg support 3 * 4 * Copyright IBM, Corp. 2012-2013 5 * Copyright (C) 2013 Red Hat Inc. 6 * 7 * Authors: 8 * Anthony Liguori <aliguori@us.ibm.com> 9 * Markus Armbruster <armbru@redhat.com> 10 * 11 * This work is licensed under the terms of the GNU GPL, version 2 or later. 12 * See the COPYING file in the top-level directory. 13 */ 14 15 #include "qemu/osdep.h" 16 #include "fw_cfg.h" 17 #include "malloc-pc.h" 18 #include "libqos-malloc.h" 19 #include "../libqtest.h" 20 #include "qemu/bswap.h" 21 #include "hw/nvram/fw_cfg.h" 22 23 void qfw_cfg_select(QFWCFG *fw_cfg, uint16_t key) 24 { 25 fw_cfg->select(fw_cfg, key); 26 } 27 28 void qfw_cfg_read_data(QFWCFG *fw_cfg, void *data, size_t len) 29 { 30 fw_cfg->read(fw_cfg, data, len); 31 } 32 33 void qfw_cfg_get(QFWCFG *fw_cfg, uint16_t key, void *data, size_t len) 34 { 35 qfw_cfg_select(fw_cfg, key); 36 qfw_cfg_read_data(fw_cfg, data, len); 37 } 38 39 uint16_t qfw_cfg_get_u16(QFWCFG *fw_cfg, uint16_t key) 40 { 41 uint16_t value; 42 qfw_cfg_get(fw_cfg, key, &value, sizeof(value)); 43 return le16_to_cpu(value); 44 } 45 46 uint32_t qfw_cfg_get_u32(QFWCFG *fw_cfg, uint16_t key) 47 { 48 uint32_t value; 49 qfw_cfg_get(fw_cfg, key, &value, sizeof(value)); 50 return le32_to_cpu(value); 51 } 52 53 uint64_t qfw_cfg_get_u64(QFWCFG *fw_cfg, uint16_t key) 54 { 55 uint64_t value; 56 qfw_cfg_get(fw_cfg, key, &value, sizeof(value)); 57 return le64_to_cpu(value); 58 } 59 60 static void mm_fw_cfg_select(QFWCFG *fw_cfg, uint16_t key) 61 { 62 qtest_writew(fw_cfg->qts, fw_cfg->base, key); 63 } 64 65 static void qfw_cfg_dma_transfer(QFWCFG *fw_cfg, QOSState *qs, void *address, 66 uint32_t length, uint32_t control) 67 { 68 FWCfgDmaAccess access; 69 uint32_t addr; 70 uint64_t guest_access_addr; 71 uint64_t gaddr; 72 73 /* create a data buffer in guest memory */ 74 gaddr = guest_alloc(&qs->alloc, length); 75 76 if (control & FW_CFG_DMA_CTL_WRITE) { 77 qtest_bufwrite(fw_cfg->qts, gaddr, address, length); 78 } 79 access.address = cpu_to_be64(gaddr); 80 access.length = cpu_to_be32(length); 81 access.control = cpu_to_be32(control); 82 83 /* now create a separate buffer in guest memory for 'access' */ 84 guest_access_addr = guest_alloc(&qs->alloc, sizeof(access)); 85 qtest_bufwrite(fw_cfg->qts, guest_access_addr, &access, sizeof(access)); 86 87 /* write lower 32 bits of address */ 88 addr = cpu_to_be32((uint32_t)(uintptr_t)guest_access_addr); 89 qtest_outl(fw_cfg->qts, fw_cfg->base + 8, addr); 90 91 /* write upper 32 bits of address */ 92 addr = cpu_to_be32((uint32_t)(uintptr_t)(guest_access_addr >> 32)); 93 qtest_outl(fw_cfg->qts, fw_cfg->base + 4, addr); 94 95 g_assert(!(be32_to_cpu(access.control) & FW_CFG_DMA_CTL_ERROR)); 96 97 if (control & FW_CFG_DMA_CTL_READ) { 98 qtest_bufread(fw_cfg->qts, gaddr, address, length); 99 } 100 101 guest_free(&qs->alloc, guest_access_addr); 102 guest_free(&qs->alloc, gaddr); 103 } 104 105 static void qfw_cfg_write_entry(QFWCFG *fw_cfg, QOSState *qs, uint16_t key, 106 void *buf, uint32_t len) 107 { 108 qfw_cfg_select(fw_cfg, key); 109 qfw_cfg_dma_transfer(fw_cfg, qs, buf, len, FW_CFG_DMA_CTL_WRITE); 110 } 111 112 static void qfw_cfg_read_entry(QFWCFG *fw_cfg, QOSState *qs, uint16_t key, 113 void *buf, uint32_t len) 114 { 115 qfw_cfg_select(fw_cfg, key); 116 qfw_cfg_dma_transfer(fw_cfg, qs, buf, len, FW_CFG_DMA_CTL_READ); 117 } 118 119 static bool find_pdir_entry(QFWCFG *fw_cfg, const char *filename, 120 uint16_t *sel, uint32_t *size) 121 { 122 g_autofree unsigned char *filesbuf = NULL; 123 uint32_t count; 124 size_t dsize; 125 FWCfgFile *pdir_entry; 126 uint32_t i; 127 bool found = false; 128 129 *size = 0; 130 *sel = 0; 131 132 qfw_cfg_get(fw_cfg, FW_CFG_FILE_DIR, &count, sizeof(count)); 133 count = be32_to_cpu(count); 134 dsize = sizeof(uint32_t) + count * sizeof(struct fw_cfg_file); 135 filesbuf = g_malloc(dsize); 136 qfw_cfg_get(fw_cfg, FW_CFG_FILE_DIR, filesbuf, dsize); 137 pdir_entry = (FWCfgFile *)(filesbuf + sizeof(uint32_t)); 138 for (i = 0; i < count; ++i, ++pdir_entry) { 139 if (!strcmp(pdir_entry->name, filename)) { 140 *size = be32_to_cpu(pdir_entry->size); 141 *sel = be16_to_cpu(pdir_entry->select); 142 found = true; 143 break; 144 } 145 } 146 147 return found; 148 } 149 150 /* 151 * The caller need check the return value. When the return value is 152 * nonzero, it means that some bytes have been transferred. 153 * 154 * If the fw_cfg file in question is smaller than the allocated & passed-in 155 * buffer, then the buffer has been populated only in part. 156 * 157 * If the fw_cfg file in question is larger than the passed-in 158 * buffer, then the return value explains how much room would have been 159 * necessary in total. And, while the caller's buffer has been fully 160 * populated, it has received only a starting slice of the fw_cfg file. 161 */ 162 size_t qfw_cfg_get_file(QFWCFG *fw_cfg, const char *filename, 163 void *data, size_t buflen) 164 { 165 size_t filesize = 0; 166 uint32_t len; 167 uint16_t sel; 168 169 if (find_pdir_entry(fw_cfg, filename, &sel, &len)) { 170 filesize = len; 171 if (len > buflen) { 172 len = buflen; 173 } 174 qfw_cfg_get(fw_cfg, sel, data, len); 175 } 176 177 return filesize; 178 } 179 180 /* 181 * The caller need check the return value. When the return value is 182 * nonzero, it means that some bytes have been transferred. 183 * 184 * If the fw_cfg file in question is smaller than the allocated & passed-in 185 * buffer, then the first len bytes were read. 186 * 187 * If the fw_cfg file in question is larger than the passed-in 188 * buffer, then the return value explains how much was actually read. 189 * 190 * It is illegal to call this function if fw_cfg does not support DMA 191 * interface. The caller should ensure that DMA is supported before 192 * calling this function. 193 * 194 * Passed QOSState pointer qs must be initialized. qs->alloc must also be 195 * properly initialized. 196 */ 197 size_t qfw_cfg_read_file(QFWCFG *fw_cfg, QOSState *qs, const char *filename, 198 void *data, size_t buflen) 199 { 200 uint32_t len = 0; 201 uint16_t sel; 202 uint32_t id; 203 204 g_assert(qs); 205 g_assert(filename); 206 g_assert(data); 207 g_assert(buflen); 208 /* check if DMA is supported since we use DMA for read */ 209 id = qfw_cfg_get_u32(fw_cfg, FW_CFG_ID); 210 g_assert(id & FW_CFG_VERSION_DMA); 211 212 if (find_pdir_entry(fw_cfg, filename, &sel, &len)) { 213 if (len > buflen) { 214 len = buflen; 215 } 216 qfw_cfg_read_entry(fw_cfg, qs, sel, data, len); 217 } 218 219 return len; 220 } 221 222 /* 223 * The caller need check the return value. When the return value is 224 * nonzero, it means that some bytes have been transferred. 225 * 226 * If the fw_cfg file in question is smaller than the allocated & passed-in 227 * buffer, then the buffer has been partially written. 228 * 229 * If the fw_cfg file in question is larger than the passed-in 230 * buffer, then the return value explains how much was actually written. 231 * 232 * It is illegal to call this function if fw_cfg does not support DMA 233 * interface. The caller should ensure that DMA is supported before 234 * calling this function. 235 * 236 * Passed QOSState pointer qs must be initialized. qs->alloc must also be 237 * properly initialized. 238 */ 239 size_t qfw_cfg_write_file(QFWCFG *fw_cfg, QOSState *qs, const char *filename, 240 void *data, size_t buflen) 241 { 242 uint32_t len = 0; 243 uint16_t sel; 244 uint32_t id; 245 246 g_assert(qs); 247 g_assert(filename); 248 g_assert(data); 249 g_assert(buflen); 250 /* write operation is only valid if DMA is supported */ 251 id = qfw_cfg_get_u32(fw_cfg, FW_CFG_ID); 252 g_assert(id & FW_CFG_VERSION_DMA); 253 254 if (find_pdir_entry(fw_cfg, filename, &sel, &len)) { 255 if (len > buflen) { 256 len = buflen; 257 } 258 qfw_cfg_write_entry(fw_cfg, qs, sel, data, len); 259 } 260 return len; 261 } 262 263 static void mm_fw_cfg_read(QFWCFG *fw_cfg, void *data, size_t len) 264 { 265 uint8_t *ptr = data; 266 int i; 267 268 for (i = 0; i < len; i++) { 269 ptr[i] = qtest_readb(fw_cfg->qts, fw_cfg->base + 2); 270 } 271 } 272 273 QFWCFG *mm_fw_cfg_init(QTestState *qts, uint64_t base) 274 { 275 QFWCFG *fw_cfg = g_malloc0(sizeof(*fw_cfg)); 276 277 fw_cfg->base = base; 278 fw_cfg->qts = qts; 279 fw_cfg->select = mm_fw_cfg_select; 280 fw_cfg->read = mm_fw_cfg_read; 281 282 return fw_cfg; 283 } 284 285 void mm_fw_cfg_uninit(QFWCFG *fw_cfg) 286 { 287 g_free(fw_cfg); 288 } 289 290 static void io_fw_cfg_select(QFWCFG *fw_cfg, uint16_t key) 291 { 292 qtest_outw(fw_cfg->qts, fw_cfg->base, key); 293 } 294 295 static void io_fw_cfg_read(QFWCFG *fw_cfg, void *data, size_t len) 296 { 297 uint8_t *ptr = data; 298 int i; 299 300 for (i = 0; i < len; i++) { 301 ptr[i] = qtest_inb(fw_cfg->qts, fw_cfg->base + 1); 302 } 303 } 304 305 QFWCFG *io_fw_cfg_init(QTestState *qts, uint16_t base) 306 { 307 QFWCFG *fw_cfg = g_malloc0(sizeof(*fw_cfg)); 308 309 fw_cfg->base = base; 310 fw_cfg->qts = qts; 311 fw_cfg->select = io_fw_cfg_select; 312 fw_cfg->read = io_fw_cfg_read; 313 314 return fw_cfg; 315 } 316 317 void io_fw_cfg_uninit(QFWCFG *fw_cfg) 318 { 319 g_free(fw_cfg); 320 } 321