fw_cfg.c 16.5 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
/*
 * QEMU Firmware configuration device emulation
 *
 * Copyright (c) 2008 Gleb Natapov
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 * THE SOFTWARE.
 */
24
#include "hw/hw.h"
25
#include "sysemu/sysemu.h"
P
Paolo Bonzini 已提交
26 27
#include "hw/isa/isa.h"
#include "hw/nvram/fw_cfg.h"
28
#include "hw/sysbus.h"
29
#include "trace.h"
30 31
#include "qemu/error-report.h"
#include "qemu/config-file.h"
32 33

#define FW_CFG_SIZE 2
A
Avi Kivity 已提交
34
#define FW_CFG_DATA_SIZE 1
35 36 37
#define TYPE_FW_CFG "fw_cfg"
#define FW_CFG_NAME "fw_cfg"
#define FW_CFG_PATH "/machine/" FW_CFG_NAME
H
Hu Tao 已提交
38
#define FW_CFG(obj) OBJECT_CHECK(FWCfgState, (obj), TYPE_FW_CFG)
39

B
Blue Swirl 已提交
40
typedef struct FWCfgEntry {
41
    uint32_t len;
42 43 44 45 46
    uint8_t *data;
    void *callback_opaque;
    FWCfgCallback callback;
} FWCfgEntry;

B
Blue Swirl 已提交
47
struct FWCfgState {
H
Hu Tao 已提交
48 49 50 51
    /*< private >*/
    SysBusDevice parent_obj;
    /*< public >*/

A
Avi Kivity 已提交
52
    MemoryRegion ctl_iomem, data_iomem, comb_iomem;
B
Blue Swirl 已提交
53
    uint32_t ctl_iobase, data_iobase;
54
    FWCfgEntry entries[2][FW_CFG_MAX_ENTRY];
G
Gerd Hoffmann 已提交
55
    FWCfgFiles *files;
56
    uint16_t cur_entry;
57
    uint32_t cur_offset;
G
Gleb Natapov 已提交
58
    Notifier machine_ready;
G
Gerd Hoffmann 已提交
59
};
60

W
wayne 已提交
61 62 63
#define JPG_FILE 0
#define BMP_FILE 1

64
static char *read_splashfile(char *filename, gsize *file_sizep,
65
                             int *file_typep)
W
wayne 已提交
66
{
67 68 69
    GError *err = NULL;
    gboolean res;
    gchar *content;
70 71
    int file_type;
    unsigned int filehead;
W
wayne 已提交
72 73
    int bmp_bpp;

74
    res = g_file_get_contents(filename, &content, file_sizep, &err);
75 76 77 78
    if (res == FALSE) {
        error_report("failed to read splash file '%s'", filename);
        g_error_free(err);
        return NULL;
W
wayne 已提交
79
    }
80

W
wayne 已提交
81
    /* check file size */
82 83
    if (*file_sizep < 30) {
        goto error;
W
wayne 已提交
84
    }
85

W
wayne 已提交
86
    /* check magic ID */
87 88
    filehead = ((content[0] & 0xff) + (content[1] << 8)) & 0xffff;
    if (filehead == 0xd8ff) {
W
wayne 已提交
89
        file_type = JPG_FILE;
90 91
    } else if (filehead == 0x4d42) {
        file_type = BMP_FILE;
W
wayne 已提交
92
    } else {
93
        goto error;
W
wayne 已提交
94
    }
95

W
wayne 已提交
96 97
    /* check BMP bpp */
    if (file_type == BMP_FILE) {
98
        bmp_bpp = (content[28] + (content[29] << 8)) & 0xffff;
W
wayne 已提交
99
        if (bmp_bpp != 24) {
100
            goto error;
W
wayne 已提交
101 102
        }
    }
103

W
wayne 已提交
104 105
    /* return values */
    *file_typep = file_type;
106 107 108 109 110 111 112 113

    return content;

error:
    error_report("splash file '%s' format not recognized; must be JPEG "
                 "or 24 bit BMP", filename);
    g_free(content);
    return NULL;
W
wayne 已提交
114 115 116 117 118 119 120
}

static void fw_cfg_bootsplash(FWCfgState *s)
{
    int boot_splash_time = -1;
    const char *boot_splash_filename = NULL;
    char *p;
121
    char *filename, *file_data;
122
    gsize file_size;
123
    int file_type;
W
wayne 已提交
124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160
    const char *temp;

    /* get user configuration */
    QemuOptsList *plist = qemu_find_opts("boot-opts");
    QemuOpts *opts = QTAILQ_FIRST(&plist->head);
    if (opts != NULL) {
        temp = qemu_opt_get(opts, "splash");
        if (temp != NULL) {
            boot_splash_filename = temp;
        }
        temp = qemu_opt_get(opts, "splash-time");
        if (temp != NULL) {
            p = (char *)temp;
            boot_splash_time = strtol(p, (char **)&p, 10);
        }
    }

    /* insert splash time if user configurated */
    if (boot_splash_time >= 0) {
        /* validate the input */
        if (boot_splash_time > 0xffff) {
            error_report("splash time is big than 65535, force it to 65535.");
            boot_splash_time = 0xffff;
        }
        /* use little endian format */
        qemu_extra_params_fw[0] = (uint8_t)(boot_splash_time & 0xff);
        qemu_extra_params_fw[1] = (uint8_t)((boot_splash_time >> 8) & 0xff);
        fw_cfg_add_file(s, "etc/boot-menu-wait", qemu_extra_params_fw, 2);
    }

    /* insert splash file if user configurated */
    if (boot_splash_filename != NULL) {
        filename = qemu_find_file(QEMU_FILE_TYPE_BIOS, boot_splash_filename);
        if (filename == NULL) {
            error_report("failed to find file '%s'.", boot_splash_filename);
            return;
        }
161 162 163 164

        /* loading file data */
        file_data = read_splashfile(filename, &file_size, &file_type);
        if (file_data == NULL) {
165
            g_free(filename);
W
wayne 已提交
166 167 168
            return;
        }
        if (boot_splash_filedata != NULL) {
169
            g_free(boot_splash_filedata);
W
wayne 已提交
170
        }
171
        boot_splash_filedata = (uint8_t *)file_data;
W
wayne 已提交
172
        boot_splash_filedata_size = file_size;
173

W
wayne 已提交
174 175 176 177 178 179 180 181
        /* insert data */
        if (file_type == JPG_FILE) {
            fw_cfg_add_file(s, "bootsplash.jpg",
                    boot_splash_filedata, boot_splash_filedata_size);
        } else {
            fw_cfg_add_file(s, "bootsplash.bmp",
                    boot_splash_filedata, boot_splash_filedata_size);
        }
182
        g_free(filename);
W
wayne 已提交
183 184 185
    }
}

186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209
static void fw_cfg_reboot(FWCfgState *s)
{
    int reboot_timeout = -1;
    char *p;
    const char *temp;

    /* get user configuration */
    QemuOptsList *plist = qemu_find_opts("boot-opts");
    QemuOpts *opts = QTAILQ_FIRST(&plist->head);
    if (opts != NULL) {
        temp = qemu_opt_get(opts, "reboot-timeout");
        if (temp != NULL) {
            p = (char *)temp;
            reboot_timeout = strtol(p, (char **)&p, 10);
        }
    }
    /* validate the input */
    if (reboot_timeout > 0xffff) {
        error_report("reboot timeout is larger than 65535, force it to 65535.");
        reboot_timeout = 0xffff;
    }
    fw_cfg_add_file(s, "etc/boot-fail-wait", g_memdup(&reboot_timeout, 4), 4);
}

210 211 212 213 214
static void fw_cfg_write(FWCfgState *s, uint8_t value)
{
    int arch = !!(s->cur_entry & FW_CFG_ARCH_LOCAL);
    FWCfgEntry *e = &s->entries[arch][s->cur_entry & FW_CFG_ENTRY_MASK];

215
    trace_fw_cfg_write(s, value);
216

217 218
    if (s->cur_entry & FW_CFG_WRITE_CHANNEL && e->callback &&
        s->cur_offset < e->len) {
219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239
        e->data[s->cur_offset++] = value;
        if (s->cur_offset == e->len) {
            e->callback(e->callback_opaque, e->data);
            s->cur_offset = 0;
        }
    }
}

static int fw_cfg_select(FWCfgState *s, uint16_t key)
{
    int ret;

    s->cur_offset = 0;
    if ((key & FW_CFG_ENTRY_MASK) >= FW_CFG_MAX_ENTRY) {
        s->cur_entry = FW_CFG_INVALID;
        ret = 0;
    } else {
        s->cur_entry = key;
        ret = 1;
    }

240
    trace_fw_cfg_select(s, key, ret);
241 242 243 244 245 246 247 248 249 250 251 252 253 254
    return ret;
}

static uint8_t fw_cfg_read(FWCfgState *s)
{
    int arch = !!(s->cur_entry & FW_CFG_ARCH_LOCAL);
    FWCfgEntry *e = &s->entries[arch][s->cur_entry & FW_CFG_ENTRY_MASK];
    uint8_t ret;

    if (s->cur_entry == FW_CFG_INVALID || !e->data || s->cur_offset >= e->len)
        ret = 0;
    else
        ret = e->data[s->cur_offset++];

255
    trace_fw_cfg_read(s, ret);
256 257 258
    return ret;
}

A
Avi Kivity 已提交
259
static uint64_t fw_cfg_data_mem_read(void *opaque, hwaddr addr,
A
Avi Kivity 已提交
260
                                     unsigned size)
261 262 263 264
{
    return fw_cfg_read(opaque);
}

A
Avi Kivity 已提交
265
static void fw_cfg_data_mem_write(void *opaque, hwaddr addr,
A
Avi Kivity 已提交
266
                                  uint64_t value, unsigned size)
267
{
268
    fw_cfg_write(opaque, (uint8_t)value);
269 270
}

A
Avi Kivity 已提交
271
static void fw_cfg_ctl_mem_write(void *opaque, hwaddr addr,
A
Avi Kivity 已提交
272
                                 uint64_t value, unsigned size)
273 274 275 276
{
    fw_cfg_select(opaque, (uint16_t)value);
}

A
Avi Kivity 已提交
277
static bool fw_cfg_ctl_mem_valid(void *opaque, hwaddr addr,
A
Avi Kivity 已提交
278
                                 unsigned size, bool is_write)
279
{
A
Avi Kivity 已提交
280
    return is_write && size == 2;
281 282
}

A
Avi Kivity 已提交
283
static uint64_t fw_cfg_comb_read(void *opaque, hwaddr addr,
A
Avi Kivity 已提交
284
                                 unsigned size)
285
{
A
Avi Kivity 已提交
286
    return fw_cfg_read(opaque);
287 288
}

A
Avi Kivity 已提交
289
static void fw_cfg_comb_write(void *opaque, hwaddr addr,
A
Avi Kivity 已提交
290
                              uint64_t value, unsigned size)
291
{
A
Avi Kivity 已提交
292 293 294 295 296 297 298 299
    switch (size) {
    case 1:
        fw_cfg_write(opaque, (uint8_t)value);
        break;
    case 2:
        fw_cfg_select(opaque, (uint16_t)value);
        break;
    }
300 301
}

A
Avi Kivity 已提交
302
static bool fw_cfg_comb_valid(void *opaque, hwaddr addr,
A
Avi Kivity 已提交
303 304 305 306
                                  unsigned size, bool is_write)
{
    return (size == 1) || (is_write && size == 2);
}
307

A
Avi Kivity 已提交
308 309 310 311
static const MemoryRegionOps fw_cfg_ctl_mem_ops = {
    .write = fw_cfg_ctl_mem_write,
    .endianness = DEVICE_NATIVE_ENDIAN,
    .valid.accepts = fw_cfg_ctl_mem_valid,
312 313
};

A
Avi Kivity 已提交
314 315 316 317 318 319 320 321
static const MemoryRegionOps fw_cfg_data_mem_ops = {
    .read = fw_cfg_data_mem_read,
    .write = fw_cfg_data_mem_write,
    .endianness = DEVICE_NATIVE_ENDIAN,
    .valid = {
        .min_access_size = 1,
        .max_access_size = 1,
    },
322 323
};

A
Avi Kivity 已提交
324 325 326 327 328
static const MemoryRegionOps fw_cfg_comb_mem_ops = {
    .read = fw_cfg_comb_read,
    .write = fw_cfg_comb_write,
    .endianness = DEVICE_NATIVE_ENDIAN,
    .valid.accepts = fw_cfg_comb_valid,
329 330
};

B
Blue Swirl 已提交
331
static void fw_cfg_reset(DeviceState *d)
332
{
H
Hu Tao 已提交
333
    FWCfgState *s = FW_CFG(d);
334 335 336 337

    fw_cfg_select(s, 0);
}

338 339 340 341 342 343 344 345 346 347 348 349 350 351
/* Save restore 32 bit int as uint16_t
   This is a Big hack, but it is how the old state did it.
   Or we broke compatibility in the state, or we can't use struct tm
 */

static int get_uint32_as_uint16(QEMUFile *f, void *pv, size_t size)
{
    uint32_t *v = pv;
    *v = qemu_get_be16(f);
    return 0;
}

static void put_unused(QEMUFile *f, void *pv, size_t size)
{
352
    fprintf(stderr, "uint32_as_uint16 is only used for backward compatibility.\n");
353 354 355
    fprintf(stderr, "This functions shouldn't be called.\n");
}

B
Blue Swirl 已提交
356
static const VMStateInfo vmstate_hack_uint32_as_uint16 = {
357 358 359 360 361 362 363 364 365 366 367 368 369 370
    .name = "int32_as_uint16",
    .get  = get_uint32_as_uint16,
    .put  = put_unused,
};

#define VMSTATE_UINT16_HACK(_f, _s, _t)                                    \
    VMSTATE_SINGLE_TEST(_f, _s, _t, 0, vmstate_hack_uint32_as_uint16, uint32_t)


static bool is_version_1(void *opaque, int version_id)
{
    return version_id == 1;
}

J
Juan Quintela 已提交
371 372
static const VMStateDescription vmstate_fw_cfg = {
    .name = "fw_cfg",
373
    .version_id = 2,
J
Juan Quintela 已提交
374 375 376 377
    .minimum_version_id = 1,
    .minimum_version_id_old = 1,
    .fields      = (VMStateField []) {
        VMSTATE_UINT16(cur_entry, FWCfgState),
378 379
        VMSTATE_UINT16_HACK(cur_offset, FWCfgState, is_version_1),
        VMSTATE_UINT32_V(cur_offset, FWCfgState, 2),
J
Juan Quintela 已提交
380 381 382
        VMSTATE_END_OF_LIST()
    }
};
383

384
void fw_cfg_add_bytes(FWCfgState *s, uint16_t key, void *data, size_t len)
385 386 387 388 389
{
    int arch = !!(key & FW_CFG_ARCH_LOCAL);

    key &= FW_CFG_ENTRY_MASK;

390
    assert(key < FW_CFG_MAX_ENTRY && len < UINT32_MAX);
391 392

    s->entries[arch][key].data = data;
393
    s->entries[arch][key].len = (uint32_t)len;
394 395
}

396 397 398 399
void fw_cfg_add_string(FWCfgState *s, uint16_t key, const char *value)
{
    size_t sz = strlen(value) + 1;

400
    return fw_cfg_add_bytes(s, key, g_memdup(value, sz), sz);
401 402
}

403
void fw_cfg_add_i16(FWCfgState *s, uint16_t key, uint16_t value)
404 405 406
{
    uint16_t *copy;

407
    copy = g_malloc(sizeof(value));
408
    *copy = cpu_to_le16(value);
409
    fw_cfg_add_bytes(s, key, copy, sizeof(value));
410 411
}

412
void fw_cfg_add_i32(FWCfgState *s, uint16_t key, uint32_t value)
413 414 415
{
    uint32_t *copy;

416
    copy = g_malloc(sizeof(value));
417
    *copy = cpu_to_le32(value);
418
    fw_cfg_add_bytes(s, key, copy, sizeof(value));
419 420
}

421
void fw_cfg_add_i64(FWCfgState *s, uint16_t key, uint64_t value)
422 423 424
{
    uint64_t *copy;

425
    copy = g_malloc(sizeof(value));
426
    *copy = cpu_to_le64(value);
427
    fw_cfg_add_bytes(s, key, copy, sizeof(value));
428 429
}

430
void fw_cfg_add_callback(FWCfgState *s, uint16_t key, FWCfgCallback callback,
431
                         void *callback_opaque, void *data, size_t len)
432 433 434
{
    int arch = !!(key & FW_CFG_ARCH_LOCAL);

435
    assert(key & FW_CFG_WRITE_CHANNEL);
436

437 438
    key &= FW_CFG_ENTRY_MASK;

439
    assert(key < FW_CFG_MAX_ENTRY && len <= UINT32_MAX);
440 441

    s->entries[arch][key].data = data;
442
    s->entries[arch][key].len = (uint32_t)len;
443 444 445 446
    s->entries[arch][key].callback_opaque = callback_opaque;
    s->entries[arch][key].callback = callback;
}

447 448
void fw_cfg_add_file(FWCfgState *s,  const char *filename,
                     void *data, size_t len)
G
Gerd Hoffmann 已提交
449
{
G
Gerd Hoffmann 已提交
450
    int i, index;
451
    size_t dsize;
G
Gerd Hoffmann 已提交
452 453

    if (!s->files) {
454
        dsize = sizeof(uint32_t) + sizeof(FWCfgFile) * FW_CFG_FILE_SLOTS;
455
        s->files = g_malloc0(dsize);
456
        fw_cfg_add_bytes(s, FW_CFG_FILE_DIR, s->files, dsize);
G
Gerd Hoffmann 已提交
457 458 459
    }

    index = be32_to_cpu(s->files->count);
460
    assert(index < FW_CFG_FILE_SLOTS);
G
Gerd Hoffmann 已提交
461 462 463

    fw_cfg_add_bytes(s, FW_CFG_FILE_FIRST + index, data, len);

464 465
    pstrcpy(s->files->f[index].name, sizeof(s->files->f[index].name),
            filename);
G
Gerd Hoffmann 已提交
466 467
    for (i = 0; i < index; i++) {
        if (strcmp(s->files->f[index].name, s->files->f[i].name) == 0) {
468
            trace_fw_cfg_add_file_dupe(s, s->files->f[index].name);
469
            return;
G
Gerd Hoffmann 已提交
470
        }
G
Gerd Hoffmann 已提交
471
    }
G
Gerd Hoffmann 已提交
472

G
Gerd Hoffmann 已提交
473 474
    s->files->f[index].size   = cpu_to_be32(len);
    s->files->f[index].select = cpu_to_be16(FW_CFG_FILE_FIRST + index);
475
    trace_fw_cfg_add_file(s, index, s->files->f[index].name, len);
G
Gerd Hoffmann 已提交
476 477 478 479

    s->files->count = cpu_to_be32(index+1);
}

480
static void fw_cfg_machine_ready(struct Notifier *n, void *data)
G
Gleb Natapov 已提交
481
{
482
    size_t len;
G
Gleb Natapov 已提交
483 484 485 486 487 488
    FWCfgState *s = container_of(n, FWCfgState, machine_ready);
    char *bootindex = get_boot_devices_list(&len);

    fw_cfg_add_file(s, "bootorder", (uint8_t*)bootindex, len);
}

G
Gerd Hoffmann 已提交
489
FWCfgState *fw_cfg_init(uint32_t ctl_port, uint32_t data_port,
A
Avi Kivity 已提交
490
                        hwaddr ctl_addr, hwaddr data_addr)
491
{
B
Blue Swirl 已提交
492 493
    DeviceState *dev;
    SysBusDevice *d;
494 495
    FWCfgState *s;

H
Hu Tao 已提交
496
    dev = qdev_create(NULL, TYPE_FW_CFG);
B
Blue Swirl 已提交
497 498
    qdev_prop_set_uint32(dev, "ctl_iobase", ctl_port);
    qdev_prop_set_uint32(dev, "data_iobase", data_port);
499
    d = SYS_BUS_DEVICE(dev);
B
Blue Swirl 已提交
500

H
Hu Tao 已提交
501
    s = FW_CFG(dev);
502

503 504 505
    assert(!object_resolve_path(FW_CFG_PATH, NULL));

    object_property_add_child(qdev_get_machine(), FW_CFG_NAME, OBJECT(s), NULL);
506 507 508

    qdev_init_nofail(dev);

509
    if (ctl_addr) {
B
Blue Swirl 已提交
510
        sysbus_mmio_map(d, 0, ctl_addr);
511 512
    }
    if (data_addr) {
B
Blue Swirl 已提交
513
        sysbus_mmio_map(d, 1, data_addr);
514
    }
515
    fw_cfg_add_bytes(s, FW_CFG_SIGNATURE, (char *)"QEMU", 4);
516
    fw_cfg_add_bytes(s, FW_CFG_UUID, qemu_uuid, 16);
517
    fw_cfg_add_i16(s, FW_CFG_NOGRAPHIC, (uint16_t)(display_type == DT_NOGRAPHIC));
518
    fw_cfg_add_i16(s, FW_CFG_NB_CPUS, (uint16_t)smp_cpus);
519
    fw_cfg_add_i16(s, FW_CFG_BOOT_MENU, (uint16_t)boot_menu);
W
wayne 已提交
520
    fw_cfg_bootsplash(s);
521
    fw_cfg_reboot(s);
G
Gleb Natapov 已提交
522 523 524 525

    s->machine_ready.notify = fw_cfg_machine_ready;
    qemu_add_machine_init_done_notifier(&s->machine_ready);

526 527
    return s;
}
B
Blue Swirl 已提交
528 529 530

static int fw_cfg_init1(SysBusDevice *dev)
{
H
Hu Tao 已提交
531
    FWCfgState *s = FW_CFG(dev);
B
Blue Swirl 已提交
532

533
    memory_region_init_io(&s->ctl_iomem, OBJECT(s), &fw_cfg_ctl_mem_ops, s,
A
Avi Kivity 已提交
534
                          "fwcfg.ctl", FW_CFG_SIZE);
535
    sysbus_init_mmio(dev, &s->ctl_iomem);
536
    memory_region_init_io(&s->data_iomem, OBJECT(s), &fw_cfg_data_mem_ops, s,
A
Avi Kivity 已提交
537
                          "fwcfg.data", FW_CFG_DATA_SIZE);
538
    sysbus_init_mmio(dev, &s->data_iomem);
A
Avi Kivity 已提交
539
    /* In case ctl and data overlap: */
540
    memory_region_init_io(&s->comb_iomem, OBJECT(s), &fw_cfg_comb_mem_ops, s,
A
Avi Kivity 已提交
541 542 543 544 545 546 547 548 549 550 551
                          "fwcfg", FW_CFG_SIZE);

    if (s->ctl_iobase + 1 == s->data_iobase) {
        sysbus_add_io(dev, s->ctl_iobase, &s->comb_iomem);
    } else {
        if (s->ctl_iobase) {
            sysbus_add_io(dev, s->ctl_iobase, &s->ctl_iomem);
        }
        if (s->data_iobase) {
            sysbus_add_io(dev, s->data_iobase, &s->data_iomem);
        }
B
Blue Swirl 已提交
552 553 554 555
    }
    return 0;
}

556 557 558 559 560 561
static Property fw_cfg_properties[] = {
    DEFINE_PROP_HEX32("ctl_iobase", FWCfgState, ctl_iobase, -1),
    DEFINE_PROP_HEX32("data_iobase", FWCfgState, data_iobase, -1),
    DEFINE_PROP_END_OF_LIST(),
};

562 563
FWCfgState *fw_cfg_find(void)
{
H
Hu Tao 已提交
564
    return FW_CFG(object_resolve_path(FW_CFG_PATH, NULL));
565 566
}

567 568
static void fw_cfg_class_init(ObjectClass *klass, void *data)
{
569
    DeviceClass *dc = DEVICE_CLASS(klass);
570 571 572
    SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass);

    k->init = fw_cfg_init1;
573 574 575 576
    dc->no_user = 1;
    dc->reset = fw_cfg_reset;
    dc->vmsd = &vmstate_fw_cfg;
    dc->props = fw_cfg_properties;
577 578
}

579
static const TypeInfo fw_cfg_info = {
580
    .name          = TYPE_FW_CFG,
581 582 583
    .parent        = TYPE_SYS_BUS_DEVICE,
    .instance_size = sizeof(FWCfgState),
    .class_init    = fw_cfg_class_init,
B
Blue Swirl 已提交
584 585
};

A
Andreas Färber 已提交
586
static void fw_cfg_register_types(void)
B
Blue Swirl 已提交
587
{
588
    type_register_static(&fw_cfg_info);
B
Blue Swirl 已提交
589 590
}

A
Andreas Färber 已提交
591
type_init(fw_cfg_register_types)