xref: /qemu/tests/qtest/libqos/fw_cfg.c (revision 513823e7521a09ed7ad1e32e6454bac3b2cbf52d)
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