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[] = { | ... | ... |