Commit 3cce62435ccdab61336c6b0ec709252c79f553ba
1 parent
b03d0971
Key/value based qemu<->guest firmware communication mechanism (Gleb Natapov)
git-svn-id: svn://svn.savannah.nongnu.org/qemu/trunk@5256 c046a42c-6fe2-441c-8c8c-71466251a162
Showing
6 changed files
with
344 additions
and
0 deletions
Makefile.target
hw/fw_cfg.c
0 → 100644
1 | +/* | |
2 | + * QEMU Firmware configuration device emulation | |
3 | + * | |
4 | + * Copyright (c) 2008 Gleb Natapov | |
5 | + * | |
6 | + * Permission is hereby granted, free of charge, to any person obtaining a copy | |
7 | + * of this software and associated documentation files (the "Software"), to deal | |
8 | + * in the Software without restriction, including without limitation the rights | |
9 | + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | |
10 | + * copies of the Software, and to permit persons to whom the Software is | |
11 | + * furnished to do so, subject to the following conditions: | |
12 | + * | |
13 | + * The above copyright notice and this permission notice shall be included in | |
14 | + * all copies or substantial portions of the Software. | |
15 | + * | |
16 | + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | |
17 | + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | |
18 | + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL | |
19 | + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | |
20 | + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | |
21 | + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN | |
22 | + * THE SOFTWARE. | |
23 | + */ | |
24 | +#include "hw.h" | |
25 | +#include "isa.h" | |
26 | +#include "fw_cfg.h" | |
27 | + | |
28 | +/* debug firmware config */ | |
29 | +//#define DEBUG_FW_CFG | |
30 | + | |
31 | +#ifdef DEBUG_FW_CFG | |
32 | +#define FW_CFG_DPRINTF(fmt, args...) \ | |
33 | + do { printf("FW_CFG: " fmt , ##args); } while (0) | |
34 | +#else | |
35 | +#define FW_CFG_DPRINTF(fmt, args...) | |
36 | +#endif | |
37 | + | |
38 | +#define FW_CFG_SIZE 2 | |
39 | + | |
40 | +typedef struct _FWCfgEntry { | |
41 | + uint16_t len; | |
42 | + uint8_t *data; | |
43 | + void *callback_opaque; | |
44 | + FWCfgCallback callback; | |
45 | +} FWCfgEntry; | |
46 | + | |
47 | +typedef struct _FWCfgState { | |
48 | + FWCfgEntry entries[2][FW_CFG_MAX_ENTRY]; | |
49 | + uint16_t cur_entry; | |
50 | + uint16_t cur_offset; | |
51 | +} FWCfgState; | |
52 | + | |
53 | +static void fw_cfg_write(FWCfgState *s, uint8_t value) | |
54 | +{ | |
55 | + int arch = !!(s->cur_entry & FW_CFG_ARCH_LOCAL); | |
56 | + FWCfgEntry *e = &s->entries[arch][s->cur_entry & FW_CFG_ENTRY_MASK]; | |
57 | + | |
58 | + FW_CFG_DPRINTF("write %d\n", value); | |
59 | + | |
60 | + if (s->cur_entry & FW_CFG_WRITE_CHANNEL && s->cur_offset < e->len) { | |
61 | + e->data[s->cur_offset++] = value; | |
62 | + if (s->cur_offset == e->len) { | |
63 | + e->callback(e->callback_opaque, e->data); | |
64 | + s->cur_offset = 0; | |
65 | + } | |
66 | + } | |
67 | +} | |
68 | + | |
69 | +static int fw_cfg_select(FWCfgState *s, uint16_t key) | |
70 | +{ | |
71 | + int ret; | |
72 | + | |
73 | + s->cur_offset = 0; | |
74 | + if ((key & FW_CFG_ENTRY_MASK) >= FW_CFG_MAX_ENTRY) { | |
75 | + s->cur_entry = FW_CFG_INVALID; | |
76 | + ret = 0; | |
77 | + } else { | |
78 | + s->cur_entry = key; | |
79 | + ret = 1; | |
80 | + } | |
81 | + | |
82 | + FW_CFG_DPRINTF("select key %d (%sfound)\n", key, ret ? "" : "not "); | |
83 | + | |
84 | + return ret; | |
85 | +} | |
86 | + | |
87 | +static uint8_t fw_cfg_read(FWCfgState *s) | |
88 | +{ | |
89 | + int arch = !!(s->cur_entry & FW_CFG_ARCH_LOCAL); | |
90 | + FWCfgEntry *e = &s->entries[arch][s->cur_entry & FW_CFG_ENTRY_MASK]; | |
91 | + uint8_t ret; | |
92 | + | |
93 | + if (s->cur_entry == FW_CFG_INVALID || !e->data || s->cur_offset >= e->len) | |
94 | + ret = 0; | |
95 | + else | |
96 | + ret = e->data[s->cur_offset++]; | |
97 | + | |
98 | + FW_CFG_DPRINTF("read %d\n", ret); | |
99 | + | |
100 | + return ret; | |
101 | +} | |
102 | + | |
103 | +static uint32_t fw_cfg_io_readb(void *opaque, uint32_t addr) | |
104 | +{ | |
105 | + return fw_cfg_read(opaque); | |
106 | +} | |
107 | + | |
108 | +static void fw_cfg_io_writeb(void *opaque, uint32_t addr, uint32_t value) | |
109 | +{ | |
110 | + return fw_cfg_write(opaque, (uint8_t)value); | |
111 | +} | |
112 | + | |
113 | +static void fw_cfg_io_writew(void *opaque, uint32_t addr, uint32_t value) | |
114 | +{ | |
115 | + fw_cfg_select(opaque, (uint16_t)value); | |
116 | +} | |
117 | + | |
118 | +static uint32_t fw_cfg_mem_readb(void *opaque, target_phys_addr_t addr) | |
119 | +{ | |
120 | + return fw_cfg_read(opaque); | |
121 | +} | |
122 | + | |
123 | +static void fw_cfg_mem_writeb(void *opaque, target_phys_addr_t addr, | |
124 | + uint32_t value) | |
125 | +{ | |
126 | + return fw_cfg_write(opaque, (uint8_t)value); | |
127 | +} | |
128 | + | |
129 | +static void fw_cfg_mem_writew(void *opaque, target_phys_addr_t addr, | |
130 | + uint32_t value) | |
131 | +{ | |
132 | + fw_cfg_select(opaque, (uint16_t)value); | |
133 | +} | |
134 | + | |
135 | +static CPUReadMemoryFunc *fw_cfg_ctl_mem_read[3] = { | |
136 | + NULL, | |
137 | + NULL, | |
138 | + NULL, | |
139 | +}; | |
140 | + | |
141 | +static CPUWriteMemoryFunc *fw_cfg_ctl_mem_write[3] = { | |
142 | + NULL, | |
143 | + fw_cfg_mem_writew, | |
144 | + NULL, | |
145 | +}; | |
146 | + | |
147 | +static CPUReadMemoryFunc *fw_cfg_data_mem_read[3] = { | |
148 | + fw_cfg_mem_readb, | |
149 | + NULL, | |
150 | + NULL, | |
151 | +}; | |
152 | + | |
153 | +static CPUWriteMemoryFunc *fw_cfg_data_mem_write[3] = { | |
154 | + fw_cfg_mem_writeb, | |
155 | + NULL, | |
156 | + NULL, | |
157 | +}; | |
158 | + | |
159 | +static void fw_cfg_reset(void *opaque) | |
160 | +{ | |
161 | + FWCfgState *s = opaque; | |
162 | + | |
163 | + fw_cfg_select(s, 0); | |
164 | +} | |
165 | + | |
166 | +static void fw_cfg_save(QEMUFile *f, void *opaque) | |
167 | +{ | |
168 | + FWCfgState *s = opaque; | |
169 | + | |
170 | + qemu_put_be16s(f, &s->cur_entry); | |
171 | + qemu_put_be16s(f, &s->cur_offset); | |
172 | +} | |
173 | + | |
174 | +static int fw_cfg_load(QEMUFile *f, void *opaque, int version_id) | |
175 | +{ | |
176 | + FWCfgState *s = opaque; | |
177 | + | |
178 | + if (version_id > 1) | |
179 | + return -EINVAL; | |
180 | + | |
181 | + qemu_get_be16s(f, &s->cur_entry); | |
182 | + qemu_get_be16s(f, &s->cur_offset); | |
183 | + | |
184 | + return 0; | |
185 | +} | |
186 | + | |
187 | +int fw_cfg_add_bytes(void *opaque, uint16_t key, uint8_t *data, uint16_t len) | |
188 | +{ | |
189 | + FWCfgState *s = opaque; | |
190 | + int arch = !!(key & FW_CFG_ARCH_LOCAL); | |
191 | + | |
192 | + key &= FW_CFG_ENTRY_MASK; | |
193 | + | |
194 | + if (key >= FW_CFG_MAX_ENTRY) | |
195 | + return 0; | |
196 | + | |
197 | + s->entries[arch][key].data = data; | |
198 | + s->entries[arch][key].len = len; | |
199 | + | |
200 | + return 1; | |
201 | +} | |
202 | + | |
203 | +int fw_cfg_add_i16(void *opaque, uint16_t key, uint16_t value) | |
204 | +{ | |
205 | + uint16_t *copy; | |
206 | + | |
207 | + copy = qemu_malloc(sizeof(value)); | |
208 | + if (!copy) | |
209 | + return 0; | |
210 | + *copy = cpu_to_le16(value); | |
211 | + return fw_cfg_add_bytes(opaque, key, (uint8_t *)copy, sizeof(value)); | |
212 | +} | |
213 | + | |
214 | +int fw_cfg_add_i32(void *opaque, uint16_t key, uint32_t value) | |
215 | +{ | |
216 | + uint32_t *copy; | |
217 | + | |
218 | + copy = qemu_malloc(sizeof(value)); | |
219 | + if (!copy) | |
220 | + return 0; | |
221 | + *copy = cpu_to_le32(value); | |
222 | + return fw_cfg_add_bytes(opaque, key, (uint8_t *)copy, sizeof(value)); | |
223 | +} | |
224 | + | |
225 | +int fw_cfg_add_i64(void *opaque, uint16_t key, uint64_t value) | |
226 | +{ | |
227 | + uint64_t *copy; | |
228 | + | |
229 | + copy = qemu_malloc(sizeof(value)); | |
230 | + if (!copy) | |
231 | + return 0; | |
232 | + *copy = cpu_to_le64(value); | |
233 | + return fw_cfg_add_bytes(opaque, key, (uint8_t *)copy, sizeof(value)); | |
234 | +} | |
235 | + | |
236 | +int fw_cfg_add_callback(void *opaque, uint16_t key, FWCfgCallback callback, | |
237 | + void *callback_opaque, uint8_t *data, size_t len) | |
238 | +{ | |
239 | + FWCfgState *s = opaque; | |
240 | + int arch = !!(key & FW_CFG_ARCH_LOCAL); | |
241 | + | |
242 | + key &= FW_CFG_ENTRY_MASK; | |
243 | + | |
244 | + if (key >= FW_CFG_MAX_ENTRY || !(key & FW_CFG_WRITE_CHANNEL) | |
245 | + || len > 65535) | |
246 | + return 0; | |
247 | + | |
248 | + s->entries[arch][key].data = data; | |
249 | + s->entries[arch][key].len = len; | |
250 | + s->entries[arch][key].callback_opaque = callback_opaque; | |
251 | + s->entries[arch][key].callback = callback; | |
252 | + | |
253 | + return 1; | |
254 | +} | |
255 | + | |
256 | +void *fw_cfg_init(uint32_t ctl_port, uint32_t data_port, | |
257 | + target_phys_addr_t ctl_addr, target_phys_addr_t data_addr) | |
258 | +{ | |
259 | + FWCfgState *s; | |
260 | + int io_ctl_memory, io_data_memory; | |
261 | + | |
262 | + s = qemu_mallocz(sizeof(FWCfgState)); | |
263 | + if (!s) | |
264 | + return NULL; | |
265 | + | |
266 | + if (ctl_port) { | |
267 | + register_ioport_write(ctl_port, 2, 2, fw_cfg_io_writew, s); | |
268 | + } | |
269 | + if (data_port) { | |
270 | + register_ioport_read(data_port, 1, 1, fw_cfg_io_readb, s); | |
271 | + register_ioport_write(data_port, 1, 1, fw_cfg_io_writeb, s); | |
272 | + } | |
273 | + if (ctl_addr) { | |
274 | + io_ctl_memory = cpu_register_io_memory(0, fw_cfg_ctl_mem_read, | |
275 | + fw_cfg_ctl_mem_write, s); | |
276 | + cpu_register_physical_memory(ctl_addr, FW_CFG_SIZE, io_ctl_memory); | |
277 | + } | |
278 | + if (data_addr) { | |
279 | + io_data_memory = cpu_register_io_memory(0, fw_cfg_data_mem_read, | |
280 | + fw_cfg_data_mem_write, s); | |
281 | + cpu_register_physical_memory(data_addr, FW_CFG_SIZE, io_data_memory); | |
282 | + } | |
283 | + fw_cfg_add_bytes(s, FW_CFG_SIGNATURE, (uint8_t *)"QEMU", 4); | |
284 | + register_savevm("fw_cfg", -1, 1, fw_cfg_save, fw_cfg_load, s); | |
285 | + qemu_register_reset(fw_cfg_reset, s); | |
286 | + fw_cfg_reset(s); | |
287 | + | |
288 | + return s; | |
289 | +} | ... | ... |
hw/fw_cfg.h
0 → 100644
1 | +#ifndef FW_CFG_H | |
2 | +#define FW_CFG_H | |
3 | + | |
4 | +#define FW_CFG_SIGNATURE 0x00 | |
5 | +#define FW_CFG_ID 0x01 | |
6 | +#define FW_CFG_MAX_ENTRY 0x10 | |
7 | + | |
8 | +#define FW_CFG_WRITE_CHANNEL 0x4000 | |
9 | +#define FW_CFG_ARCH_LOCAL 0x8000 | |
10 | +#define FW_CFG_ENTRY_MASK ~(FW_CFG_WRITE_CHANNEL | FW_CFG_ARCH_LOCAL) | |
11 | + | |
12 | +#define FW_CFG_INVALID 0xffff | |
13 | + | |
14 | +#ifndef NO_QEMU_PROTOS | |
15 | +typedef void (*FWCfgCallback)(void *opaque, uint8_t *data); | |
16 | + | |
17 | +int fw_cfg_add_bytes(void *opaque, uint16_t key, uint8_t *data, uint16_t len); | |
18 | +int fw_cfg_add_i16(void *opaque, uint16_t key, uint16_t value); | |
19 | +int fw_cfg_add_i32(void *opaque, uint16_t key, uint32_t value); | |
20 | +int fw_cfg_add_i64(void *opaque, uint16_t key, uint64_t value); | |
21 | +int fw_cfg_add_callback(void *opaque, uint16_t key, FWCfgCallback callback, | |
22 | + void *callback_opaque, uint8_t *data, size_t len); | |
23 | +void *fw_cfg_init(uint32_t ctl_port, uint32_t data_port, | |
24 | + target_phys_addr_t crl_addr, target_phys_addr_t data_addr); | |
25 | + | |
26 | +#endif /* NO_QEMU_PROTOS */ | |
27 | + | |
28 | +#endif | ... | ... |
hw/pc.c
... | ... | @@ -32,6 +32,7 @@ |
32 | 32 | #include "smbus.h" |
33 | 33 | #include "boards.h" |
34 | 34 | #include "console.h" |
35 | +#include "fw_cfg.h" | |
35 | 36 | |
36 | 37 | /* output Bochs bios info messages */ |
37 | 38 | //#define DEBUG_BIOS |
... | ... | @@ -44,6 +45,7 @@ |
44 | 45 | |
45 | 46 | /* Leave a chunk of memory at the top of RAM for the BIOS ACPI tables. */ |
46 | 47 | #define ACPI_DATA_SIZE 0x10000 |
48 | +#define BIOS_CFG_IOPORT 0x510 | |
47 | 49 | |
48 | 50 | #define MAX_IDE_BUS 2 |
49 | 51 | |
... | ... | @@ -416,6 +418,8 @@ static void bochs_bios_write(void *opaque, uint32_t addr, uint32_t val) |
416 | 418 | |
417 | 419 | static void bochs_bios_init(void) |
418 | 420 | { |
421 | + void *fw_cfg; | |
422 | + | |
419 | 423 | register_ioport_write(0x400, 1, 2, bochs_bios_write, NULL); |
420 | 424 | register_ioport_write(0x401, 1, 2, bochs_bios_write, NULL); |
421 | 425 | register_ioport_write(0x402, 1, 1, bochs_bios_write, NULL); |
... | ... | @@ -426,6 +430,9 @@ static void bochs_bios_init(void) |
426 | 430 | register_ioport_write(0x502, 1, 2, bochs_bios_write, NULL); |
427 | 431 | register_ioport_write(0x500, 1, 1, bochs_bios_write, NULL); |
428 | 432 | register_ioport_write(0x503, 1, 1, bochs_bios_write, NULL); |
433 | + | |
434 | + fw_cfg = fw_cfg_init(BIOS_CFG_IOPORT, BIOS_CFG_IOPORT + 1, 0, 0); | |
435 | + fw_cfg_add_i32(fw_cfg, FW_CFG_ID, 1); | |
429 | 436 | } |
430 | 437 | |
431 | 438 | /* Generate an initial boot sector which sets state and jump to | ... | ... |
hw/sun4m.c
... | ... | @@ -34,6 +34,7 @@ |
34 | 34 | #include "scsi.h" |
35 | 35 | #include "pc.h" |
36 | 36 | #include "isa.h" |
37 | +#include "fw_cfg.h" | |
37 | 38 | |
38 | 39 | //#define DEBUG_IRQ |
39 | 40 | |
... | ... | @@ -78,6 +79,7 @@ |
78 | 79 | #define PROM_SIZE_MAX (512 * 1024) |
79 | 80 | #define PROM_VADDR 0xffd00000 |
80 | 81 | #define PROM_FILENAME "openbios-sparc32" |
82 | +#define CFG_ADDR 0xd00000510ULL | |
81 | 83 | |
82 | 84 | // Control plane, 8-bit and 24-bit planes |
83 | 85 | #define TCX_SIZE (9 * 1024 * 1024) |
... | ... | @@ -410,6 +412,7 @@ static void sun4m_hw_init(const struct hwdef *hwdef, ram_addr_t RAM_size, |
410 | 412 | char buf[1024]; |
411 | 413 | BlockDriverState *fd[MAX_FD]; |
412 | 414 | int drive_index; |
415 | + void *fw_cfg; | |
413 | 416 | |
414 | 417 | /* init CPUs */ |
415 | 418 | if (!cpu_model) |
... | ... | @@ -570,6 +573,9 @@ static void sun4m_hw_init(const struct hwdef *hwdef, ram_addr_t RAM_size, |
570 | 573 | if (hwdef->ecc_base != (target_phys_addr_t)-1) |
571 | 574 | ecc_init(hwdef->ecc_base, slavio_irq[hwdef->ecc_irq], |
572 | 575 | hwdef->ecc_version); |
576 | + | |
577 | + fw_cfg = fw_cfg_init(0, 0, CFG_ADDR, CFG_ADDR + 2); | |
578 | + fw_cfg_add_i32(fw_cfg, FW_CFG_ID, 1); | |
573 | 579 | } |
574 | 580 | |
575 | 581 | static void sun4c_hw_init(const struct hwdef *hwdef, ram_addr_t RAM_size, |
... | ... | @@ -589,6 +595,7 @@ static void sun4c_hw_init(const struct hwdef *hwdef, ram_addr_t RAM_size, |
589 | 595 | char buf[1024]; |
590 | 596 | BlockDriverState *fd[MAX_FD]; |
591 | 597 | int drive_index; |
598 | + void *fw_cfg; | |
592 | 599 | |
593 | 600 | /* init CPU */ |
594 | 601 | if (!cpu_model) |
... | ... | @@ -715,6 +722,9 @@ static void sun4c_hw_init(const struct hwdef *hwdef, ram_addr_t RAM_size, |
715 | 722 | nvram_init(nvram, (uint8_t *)&nd_table[0].macaddr, kernel_cmdline, |
716 | 723 | boot_device, RAM_size, kernel_size, graphic_width, |
717 | 724 | graphic_height, graphic_depth, hwdef->machine_id, "Sun4c"); |
725 | + | |
726 | + fw_cfg = fw_cfg_init(0, 0, CFG_ADDR, CFG_ADDR + 2); | |
727 | + fw_cfg_add_i32(fw_cfg, FW_CFG_ID, 1); | |
718 | 728 | } |
719 | 729 | |
720 | 730 | static const struct hwdef hwdefs[] = { |
... | ... | @@ -1405,6 +1415,7 @@ static void sun4d_hw_init(const struct sun4d_hwdef *hwdef, ram_addr_t RAM_size, |
1405 | 1415 | int ret; |
1406 | 1416 | char buf[1024]; |
1407 | 1417 | int drive_index; |
1418 | + void *fw_cfg; | |
1408 | 1419 | |
1409 | 1420 | /* init CPUs */ |
1410 | 1421 | if (!cpu_model) |
... | ... | @@ -1528,6 +1539,9 @@ static void sun4d_hw_init(const struct sun4d_hwdef *hwdef, ram_addr_t RAM_size, |
1528 | 1539 | nvram_init(nvram, (uint8_t *)&nd_table[0].macaddr, kernel_cmdline, |
1529 | 1540 | boot_device, RAM_size, kernel_size, graphic_width, |
1530 | 1541 | graphic_height, graphic_depth, hwdef->machine_id, "Sun4d"); |
1542 | + | |
1543 | + fw_cfg = fw_cfg_init(0, 0, CFG_ADDR, CFG_ADDR + 2); | |
1544 | + fw_cfg_add_i32(fw_cfg, FW_CFG_ID, 1); | |
1531 | 1545 | } |
1532 | 1546 | |
1533 | 1547 | /* SPARCserver 1000 hardware initialisation */ | ... | ... |
hw/sun4u.c
... | ... | @@ -31,6 +31,7 @@ |
31 | 31 | #include "sysemu.h" |
32 | 32 | #include "boards.h" |
33 | 33 | #include "firmware_abi.h" |
34 | +#include "fw_cfg.h" | |
34 | 35 | |
35 | 36 | #define KERNEL_LOAD_ADDR 0x00404000 |
36 | 37 | #define CMDLINE_ADDR 0x003ff000 |
... | ... | @@ -44,6 +45,7 @@ |
44 | 45 | #define PROM_FILENAME "openbios-sparc64" |
45 | 46 | #define NVRAM_SIZE 0x2000 |
46 | 47 | #define MAX_IDE_BUS 2 |
48 | +#define BIOS_CFG_IOPORT 0x510 | |
47 | 49 | |
48 | 50 | struct hwdef { |
49 | 51 | const char * const default_cpu_model; |
... | ... | @@ -270,6 +272,7 @@ static void sun4uv_init(ram_addr_t RAM_size, int vga_ram_size, |
270 | 272 | int drive_index; |
271 | 273 | BlockDriverState *hd[MAX_IDE_BUS * MAX_IDE_DEVS]; |
272 | 274 | BlockDriverState *fd[MAX_FD]; |
275 | + void *fw_cfg; | |
273 | 276 | |
274 | 277 | linux_boot = (kernel_filename != NULL); |
275 | 278 | |
... | ... | @@ -415,6 +418,8 @@ static void sun4uv_init(ram_addr_t RAM_size, int vga_ram_size, |
415 | 418 | graphic_width, graphic_height, graphic_depth, |
416 | 419 | (uint8_t *)&nd_table[0].macaddr); |
417 | 420 | |
421 | + fw_cfg = fw_cfg_init(BIOS_CFG_IOPORT, BIOS_CFG_IOPORT + 1, 0, 0); | |
422 | + fw_cfg_add_i32(fw_cfg, FW_CFG_ID, 1); | |
418 | 423 | } |
419 | 424 | |
420 | 425 | static const struct hwdef hwdefs[] = { | ... | ... |