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
qfw_cfg_select(QFWCFG * fw_cfg,uint16_t key)23 void qfw_cfg_select(QFWCFG *fw_cfg, uint16_t key)
24 {
25 fw_cfg->select(fw_cfg, key);
26 }
27
qfw_cfg_read_data(QFWCFG * fw_cfg,void * data,size_t len)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
qfw_cfg_get(QFWCFG * fw_cfg,uint16_t key,void * data,size_t len)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
qfw_cfg_get_u16(QFWCFG * fw_cfg,uint16_t key)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
qfw_cfg_get_u32(QFWCFG * fw_cfg,uint16_t key)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
qfw_cfg_get_u64(QFWCFG * fw_cfg,uint16_t key)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
mm_fw_cfg_select(QFWCFG * fw_cfg,uint16_t key)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
qfw_cfg_dma_transfer(QFWCFG * fw_cfg,QOSState * qs,void * address,uint32_t length,uint32_t control)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
qfw_cfg_write_entry(QFWCFG * fw_cfg,QOSState * qs,uint16_t key,void * buf,uint32_t len)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
qfw_cfg_read_entry(QFWCFG * fw_cfg,QOSState * qs,uint16_t key,void * buf,uint32_t len)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
find_pdir_entry(QFWCFG * fw_cfg,const char * filename,uint16_t * sel,uint32_t * size)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 */
qfw_cfg_get_file(QFWCFG * fw_cfg,const char * filename,void * data,size_t buflen)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 */
qfw_cfg_read_file(QFWCFG * fw_cfg,QOSState * qs,const char * filename,void * data,size_t buflen)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 */
qfw_cfg_write_file(QFWCFG * fw_cfg,QOSState * qs,const char * filename,void * data,size_t buflen)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
mm_fw_cfg_read(QFWCFG * fw_cfg,void * data,size_t len)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
mm_fw_cfg_init(QTestState * qts,uint64_t base)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
mm_fw_cfg_uninit(QFWCFG * fw_cfg)285 void mm_fw_cfg_uninit(QFWCFG *fw_cfg)
286 {
287 g_free(fw_cfg);
288 }
289
io_fw_cfg_select(QFWCFG * fw_cfg,uint16_t key)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
io_fw_cfg_read(QFWCFG * fw_cfg,void * data,size_t len)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
io_fw_cfg_init(QTestState * qts,uint16_t base)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
io_fw_cfg_uninit(QFWCFG * fw_cfg)317 void io_fw_cfg_uninit(QFWCFG *fw_cfg)
318 {
319 g_free(fw_cfg);
320 }
321