fw_cfg.c 14.7 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 24
/*
 * 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.
 */
#include "hw.h"
25
#include "sysemu.h"
26 27
#include "isa.h"
#include "fw_cfg.h"
B
Blue Swirl 已提交
28
#include "sysbus.h"
W
wayne 已提交
29
#include "qemu-error.h"
30 31 32 33 34

/* debug firmware config */
//#define DEBUG_FW_CFG

#ifdef DEBUG_FW_CFG
35 36
#define FW_CFG_DPRINTF(fmt, ...)                        \
    do { printf("FW_CFG: " fmt , ## __VA_ARGS__); } while (0)
37
#else
38
#define FW_CFG_DPRINTF(fmt, ...)
39 40 41 42
#endif

#define FW_CFG_SIZE 2

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

B
Blue Swirl 已提交
50
struct FWCfgState {
B
Blue Swirl 已提交
51 52
    SysBusDevice busdev;
    uint32_t ctl_iobase, data_iobase;
53
    FWCfgEntry entries[2][FW_CFG_MAX_ENTRY];
G
Gerd Hoffmann 已提交
54
    FWCfgFiles *files;
55
    uint16_t cur_entry;
56
    uint32_t cur_offset;
G
Gleb Natapov 已提交
57
    Notifier machine_ready;
G
Gerd Hoffmann 已提交
58
};
59

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

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

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

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

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

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

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

    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 已提交
112 113 114 115 116 117 118
}

static void fw_cfg_bootsplash(FWCfgState *s)
{
    int boot_splash_time = -1;
    const char *boot_splash_filename = NULL;
    char *p;
119
    char *filename, *file_data;
W
wayne 已提交
120 121 122 123 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
    int file_size;
    int file_type = -1;
    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;
        }
159 160 161 162

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

W
wayne 已提交
172 173 174 175 176 177 178 179
        /* 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);
        }
180
        g_free(filename);
W
wayne 已提交
181 182 183
    }
}

184 185 186 187 188 189 190
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];

    FW_CFG_DPRINTF("write %d\n", value);

191 192
    if (s->cur_entry & FW_CFG_WRITE_CHANNEL && e->callback &&
        s->cur_offset < e->len) {
193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241
        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;
    }

    FW_CFG_DPRINTF("select key %d (%sfound)\n", key, ret ? "" : "not ");

    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++];

    FW_CFG_DPRINTF("read %d\n", ret);

    return ret;
}

static uint32_t fw_cfg_io_readb(void *opaque, uint32_t addr)
{
    return fw_cfg_read(opaque);
}

static void fw_cfg_io_writeb(void *opaque, uint32_t addr, uint32_t value)
{
242
    fw_cfg_write(opaque, (uint8_t)value);
243 244 245 246 247 248 249
}

static void fw_cfg_io_writew(void *opaque, uint32_t addr, uint32_t value)
{
    fw_cfg_select(opaque, (uint16_t)value);
}

A
Anthony Liguori 已提交
250
static uint32_t fw_cfg_mem_readb(void *opaque, target_phys_addr_t addr)
251 252 253 254
{
    return fw_cfg_read(opaque);
}

A
Anthony Liguori 已提交
255
static void fw_cfg_mem_writeb(void *opaque, target_phys_addr_t addr,
256 257
                              uint32_t value)
{
258
    fw_cfg_write(opaque, (uint8_t)value);
259 260
}

A
Anthony Liguori 已提交
261
static void fw_cfg_mem_writew(void *opaque, target_phys_addr_t addr,
262 263 264 265 266
                              uint32_t value)
{
    fw_cfg_select(opaque, (uint16_t)value);
}

267
static CPUReadMemoryFunc * const fw_cfg_ctl_mem_read[3] = {
268 269 270 271 272
    NULL,
    NULL,
    NULL,
};

273
static CPUWriteMemoryFunc * const fw_cfg_ctl_mem_write[3] = {
274 275 276 277 278
    NULL,
    fw_cfg_mem_writew,
    NULL,
};

279
static CPUReadMemoryFunc * const fw_cfg_data_mem_read[3] = {
280 281 282 283 284
    fw_cfg_mem_readb,
    NULL,
    NULL,
};

285
static CPUWriteMemoryFunc * const fw_cfg_data_mem_write[3] = {
286 287 288 289 290
    fw_cfg_mem_writeb,
    NULL,
    NULL,
};

B
Blue Swirl 已提交
291
static void fw_cfg_reset(DeviceState *d)
292
{
B
Blue Swirl 已提交
293
    FWCfgState *s = DO_UPCAST(FWCfgState, busdev.qdev, d);
294 295 296 297

    fw_cfg_select(s, 0);
}

298 299 300 301 302 303 304 305 306 307 308 309 310 311
/* 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)
{
312
    fprintf(stderr, "uint32_as_uint16 is only used for backward compatibility.\n");
313 314 315
    fprintf(stderr, "This functions shouldn't be called.\n");
}

B
Blue Swirl 已提交
316
static const VMStateInfo vmstate_hack_uint32_as_uint16 = {
317 318 319 320 321 322 323 324 325 326 327 328 329 330
    .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 已提交
331 332
static const VMStateDescription vmstate_fw_cfg = {
    .name = "fw_cfg",
333
    .version_id = 2,
J
Juan Quintela 已提交
334 335 336 337
    .minimum_version_id = 1,
    .minimum_version_id_old = 1,
    .fields      = (VMStateField []) {
        VMSTATE_UINT16(cur_entry, FWCfgState),
338 339
        VMSTATE_UINT16_HACK(cur_offset, FWCfgState, is_version_1),
        VMSTATE_UINT32_V(cur_offset, FWCfgState, 2),
J
Juan Quintela 已提交
340 341 342
        VMSTATE_END_OF_LIST()
    }
};
343

G
Gerd Hoffmann 已提交
344
int fw_cfg_add_bytes(FWCfgState *s, uint16_t key, uint8_t *data, uint32_t len)
345 346 347 348 349 350 351 352 353 354 355 356 357 358
{
    int arch = !!(key & FW_CFG_ARCH_LOCAL);

    key &= FW_CFG_ENTRY_MASK;

    if (key >= FW_CFG_MAX_ENTRY)
        return 0;

    s->entries[arch][key].data = data;
    s->entries[arch][key].len = len;

    return 1;
}

G
Gerd Hoffmann 已提交
359
int fw_cfg_add_i16(FWCfgState *s, uint16_t key, uint16_t value)
360 361 362
{
    uint16_t *copy;

363
    copy = g_malloc(sizeof(value));
364
    *copy = cpu_to_le16(value);
G
Gerd Hoffmann 已提交
365
    return fw_cfg_add_bytes(s, key, (uint8_t *)copy, sizeof(value));
366 367
}

G
Gerd Hoffmann 已提交
368
int fw_cfg_add_i32(FWCfgState *s, uint16_t key, uint32_t value)
369 370 371
{
    uint32_t *copy;

372
    copy = g_malloc(sizeof(value));
373
    *copy = cpu_to_le32(value);
G
Gerd Hoffmann 已提交
374
    return fw_cfg_add_bytes(s, key, (uint8_t *)copy, sizeof(value));
375 376
}

G
Gerd Hoffmann 已提交
377
int fw_cfg_add_i64(FWCfgState *s, uint16_t key, uint64_t value)
378 379 380
{
    uint64_t *copy;

381
    copy = g_malloc(sizeof(value));
382
    *copy = cpu_to_le64(value);
G
Gerd Hoffmann 已提交
383
    return fw_cfg_add_bytes(s, key, (uint8_t *)copy, sizeof(value));
384 385
}

G
Gerd Hoffmann 已提交
386
int fw_cfg_add_callback(FWCfgState *s, uint16_t key, FWCfgCallback callback,
387 388 389 390
                        void *callback_opaque, uint8_t *data, size_t len)
{
    int arch = !!(key & FW_CFG_ARCH_LOCAL);

391 392 393
    if (!(key & FW_CFG_WRITE_CHANNEL))
        return 0;

394 395
    key &= FW_CFG_ENTRY_MASK;

396
    if (key >= FW_CFG_MAX_ENTRY || len > 65535)
397 398 399 400 401 402 403 404 405 406
        return 0;

    s->entries[arch][key].data = data;
    s->entries[arch][key].len = len;
    s->entries[arch][key].callback_opaque = callback_opaque;
    s->entries[arch][key].callback = callback;

    return 1;
}

407 408
int fw_cfg_add_file(FWCfgState *s,  const char *filename, uint8_t *data,
                    uint32_t len)
G
Gerd Hoffmann 已提交
409
{
G
Gerd Hoffmann 已提交
410
    int i, index;
G
Gerd Hoffmann 已提交
411 412 413

    if (!s->files) {
        int dsize = sizeof(uint32_t) + sizeof(FWCfgFile) * FW_CFG_FILE_SLOTS;
414
        s->files = g_malloc0(dsize);
G
Gerd Hoffmann 已提交
415 416 417 418 419 420 421 422 423 424 425
        fw_cfg_add_bytes(s, FW_CFG_FILE_DIR, (uint8_t*)s->files, dsize);
    }

    index = be32_to_cpu(s->files->count);
    if (index == FW_CFG_FILE_SLOTS) {
        fprintf(stderr, "fw_cfg: out of file slots\n");
        return 0;
    }

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

426 427
    pstrcpy(s->files->f[index].name, sizeof(s->files->f[index].name),
            filename);
G
Gerd Hoffmann 已提交
428 429 430 431 432 433
    for (i = 0; i < index; i++) {
        if (strcmp(s->files->f[index].name, s->files->f[i].name) == 0) {
            FW_CFG_DPRINTF("%s: skip duplicate: %s\n", __FUNCTION__,
                           s->files->f[index].name);
            return 1;
        }
G
Gerd Hoffmann 已提交
434
    }
G
Gerd Hoffmann 已提交
435

G
Gerd Hoffmann 已提交
436 437 438 439 440 441 442 443 444
    s->files->f[index].size   = cpu_to_be32(len);
    s->files->f[index].select = cpu_to_be16(FW_CFG_FILE_FIRST + index);
    FW_CFG_DPRINTF("%s: #%d: %s (%d bytes)\n", __FUNCTION__,
                   index, s->files->f[index].name, len);

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

445
static void fw_cfg_machine_ready(struct Notifier *n, void *data)
G
Gleb Natapov 已提交
446 447 448 449 450 451 452 453
{
    uint32_t len;
    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 已提交
454 455
FWCfgState *fw_cfg_init(uint32_t ctl_port, uint32_t data_port,
                        target_phys_addr_t ctl_addr, target_phys_addr_t data_addr)
456
{
B
Blue Swirl 已提交
457 458
    DeviceState *dev;
    SysBusDevice *d;
459 460
    FWCfgState *s;

B
Blue Swirl 已提交
461 462 463 464 465 466 467
    dev = qdev_create(NULL, "fw_cfg");
    qdev_prop_set_uint32(dev, "ctl_iobase", ctl_port);
    qdev_prop_set_uint32(dev, "data_iobase", data_port);
    qdev_init_nofail(dev);
    d = sysbus_from_qdev(dev);

    s = DO_UPCAST(FWCfgState, busdev.qdev, dev);
468 469

    if (ctl_addr) {
B
Blue Swirl 已提交
470
        sysbus_mmio_map(d, 0, ctl_addr);
471 472
    }
    if (data_addr) {
B
Blue Swirl 已提交
473
        sysbus_mmio_map(d, 1, data_addr);
474 475
    }
    fw_cfg_add_bytes(s, FW_CFG_SIGNATURE, (uint8_t *)"QEMU", 4);
476
    fw_cfg_add_bytes(s, FW_CFG_UUID, qemu_uuid, 16);
477
    fw_cfg_add_i16(s, FW_CFG_NOGRAPHIC, (uint16_t)(display_type == DT_NOGRAPHIC));
478
    fw_cfg_add_i16(s, FW_CFG_NB_CPUS, (uint16_t)smp_cpus);
479
    fw_cfg_add_i16(s, FW_CFG_MAX_CPUS, (uint16_t)max_cpus);
480
    fw_cfg_add_i16(s, FW_CFG_BOOT_MENU, (uint16_t)boot_menu);
W
wayne 已提交
481
    fw_cfg_bootsplash(s);
G
Gleb Natapov 已提交
482 483 484 485

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

486 487
    return s;
}
B
Blue Swirl 已提交
488 489 490 491 492 493 494

static int fw_cfg_init1(SysBusDevice *dev)
{
    FWCfgState *s = FROM_SYSBUS(FWCfgState, dev);
    int io_ctl_memory, io_data_memory;

    io_ctl_memory = cpu_register_io_memory(fw_cfg_ctl_mem_read,
495 496
                                           fw_cfg_ctl_mem_write, s,
                                           DEVICE_NATIVE_ENDIAN);
B
Blue Swirl 已提交
497 498 499
    sysbus_init_mmio(dev, FW_CFG_SIZE, io_ctl_memory);

    io_data_memory = cpu_register_io_memory(fw_cfg_data_mem_read,
500 501
                                            fw_cfg_data_mem_write, s,
                                            DEVICE_NATIVE_ENDIAN);
B
Blue Swirl 已提交
502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533
    sysbus_init_mmio(dev, FW_CFG_SIZE, io_data_memory);

    if (s->ctl_iobase) {
        register_ioport_write(s->ctl_iobase, 2, 2, fw_cfg_io_writew, s);
    }
    if (s->data_iobase) {
        register_ioport_read(s->data_iobase, 1, 1, fw_cfg_io_readb, s);
        register_ioport_write(s->data_iobase, 1, 1, fw_cfg_io_writeb, s);
    }
    return 0;
}

static SysBusDeviceInfo fw_cfg_info = {
    .init = fw_cfg_init1,
    .qdev.name = "fw_cfg",
    .qdev.size = sizeof(FWCfgState),
    .qdev.vmsd = &vmstate_fw_cfg,
    .qdev.reset = fw_cfg_reset,
    .qdev.no_user = 1,
    .qdev.props = (Property[]) {
        DEFINE_PROP_HEX32("ctl_iobase", FWCfgState, ctl_iobase, -1),
        DEFINE_PROP_HEX32("data_iobase", FWCfgState, data_iobase, -1),
        DEFINE_PROP_END_OF_LIST(),
    },
};

static void fw_cfg_register_devices(void)
{
    sysbus_register_withprop(&fw_cfg_info);
}

device_init(fw_cfg_register_devices)