Statistics
| Branch: | Revision:

root / hw / fw_cfg.c @ 0e7a7592

History | View | Annotate | Download (16.1 kB)

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 "sysemu/sysemu.h"
26
#include "isa.h"
27
#include "fw_cfg.h"
28
#include "sysbus.h"
29
#include "trace.h"
30
#include "qemu/error-report.h"
31
#include "qemu/config-file.h"
32

    
33
#define FW_CFG_SIZE 2
34
#define FW_CFG_DATA_SIZE 1
35

    
36
typedef struct FWCfgEntry {
37
    uint32_t len;
38
    uint8_t *data;
39
    void *callback_opaque;
40
    FWCfgCallback callback;
41
} FWCfgEntry;
42

    
43
struct FWCfgState {
44
    SysBusDevice busdev;
45
    MemoryRegion ctl_iomem, data_iomem, comb_iomem;
46
    uint32_t ctl_iobase, data_iobase;
47
    FWCfgEntry entries[2][FW_CFG_MAX_ENTRY];
48
    FWCfgFiles *files;
49
    uint16_t cur_entry;
50
    uint32_t cur_offset;
51
    Notifier machine_ready;
52
};
53

    
54
#define JPG_FILE 0
55
#define BMP_FILE 1
56

    
57
static char *read_splashfile(char *filename, int *file_sizep, int *file_typep)
58
{
59
    GError *err = NULL;
60
    gboolean res;
61
    gchar *content;
62
    int file_type = -1;
63
    unsigned int filehead = 0;
64
    int bmp_bpp;
65

    
66
    res = g_file_get_contents(filename, &content, (gsize *)file_sizep, &err);
67
    if (res == FALSE) {
68
        error_report("failed to read splash file '%s'", filename);
69
        g_error_free(err);
70
        return NULL;
71
    }
72

    
73
    /* check file size */
74
    if (*file_sizep < 30) {
75
        goto error;
76
    }
77

    
78
    /* check magic ID */
79
    filehead = ((content[0] & 0xff) + (content[1] << 8)) & 0xffff;
80
    if (filehead == 0xd8ff) {
81
        file_type = JPG_FILE;
82
    } else if (filehead == 0x4d42) {
83
        file_type = BMP_FILE;
84
    } else {
85
        goto error;
86
    }
87

    
88
    /* check BMP bpp */
89
    if (file_type == BMP_FILE) {
90
        bmp_bpp = (content[28] + (content[29] << 8)) & 0xffff;
91
        if (bmp_bpp != 24) {
92
            goto error;
93
        }
94
    }
95

    
96
    /* return values */
97
    *file_typep = file_type;
98

    
99
    return content;
100

    
101
error:
102
    error_report("splash file '%s' format not recognized; must be JPEG "
103
                 "or 24 bit BMP", filename);
104
    g_free(content);
105
    return NULL;
106
}
107

    
108
static void fw_cfg_bootsplash(FWCfgState *s)
109
{
110
    int boot_splash_time = -1;
111
    const char *boot_splash_filename = NULL;
112
    char *p;
113
    char *filename, *file_data;
114
    int file_size;
115
    int file_type = -1;
116
    const char *temp;
117

    
118
    /* get user configuration */
119
    QemuOptsList *plist = qemu_find_opts("boot-opts");
120
    QemuOpts *opts = QTAILQ_FIRST(&plist->head);
121
    if (opts != NULL) {
122
        temp = qemu_opt_get(opts, "splash");
123
        if (temp != NULL) {
124
            boot_splash_filename = temp;
125
        }
126
        temp = qemu_opt_get(opts, "splash-time");
127
        if (temp != NULL) {
128
            p = (char *)temp;
129
            boot_splash_time = strtol(p, (char **)&p, 10);
130
        }
131
    }
132

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

    
146
    /* insert splash file if user configurated */
147
    if (boot_splash_filename != NULL) {
148
        filename = qemu_find_file(QEMU_FILE_TYPE_BIOS, boot_splash_filename);
149
        if (filename == NULL) {
150
            error_report("failed to find file '%s'.", boot_splash_filename);
151
            return;
152
        }
153

    
154
        /* loading file data */
155
        file_data = read_splashfile(filename, &file_size, &file_type);
156
        if (file_data == NULL) {
157
            g_free(filename);
158
            return;
159
        }
160
        if (boot_splash_filedata != NULL) {
161
            g_free(boot_splash_filedata);
162
        }
163
        boot_splash_filedata = (uint8_t *)file_data;
164
        boot_splash_filedata_size = file_size;
165

    
166
        /* insert data */
167
        if (file_type == JPG_FILE) {
168
            fw_cfg_add_file(s, "bootsplash.jpg",
169
                    boot_splash_filedata, boot_splash_filedata_size);
170
        } else {
171
            fw_cfg_add_file(s, "bootsplash.bmp",
172
                    boot_splash_filedata, boot_splash_filedata_size);
173
        }
174
        g_free(filename);
175
    }
176
}
177

    
178
static void fw_cfg_reboot(FWCfgState *s)
179
{
180
    int reboot_timeout = -1;
181
    char *p;
182
    const char *temp;
183

    
184
    /* get user configuration */
185
    QemuOptsList *plist = qemu_find_opts("boot-opts");
186
    QemuOpts *opts = QTAILQ_FIRST(&plist->head);
187
    if (opts != NULL) {
188
        temp = qemu_opt_get(opts, "reboot-timeout");
189
        if (temp != NULL) {
190
            p = (char *)temp;
191
            reboot_timeout = strtol(p, (char **)&p, 10);
192
        }
193
    }
194
    /* validate the input */
195
    if (reboot_timeout > 0xffff) {
196
        error_report("reboot timeout is larger than 65535, force it to 65535.");
197
        reboot_timeout = 0xffff;
198
    }
199
    fw_cfg_add_file(s, "etc/boot-fail-wait", g_memdup(&reboot_timeout, 4), 4);
200
}
201

    
202
static void fw_cfg_write(FWCfgState *s, uint8_t value)
203
{
204
    int arch = !!(s->cur_entry & FW_CFG_ARCH_LOCAL);
205
    FWCfgEntry *e = &s->entries[arch][s->cur_entry & FW_CFG_ENTRY_MASK];
206

    
207
    trace_fw_cfg_write(s, value);
208

    
209
    if (s->cur_entry & FW_CFG_WRITE_CHANNEL && e->callback &&
210
        s->cur_offset < e->len) {
211
        e->data[s->cur_offset++] = value;
212
        if (s->cur_offset == e->len) {
213
            e->callback(e->callback_opaque, e->data);
214
            s->cur_offset = 0;
215
        }
216
    }
217
}
218

    
219
static int fw_cfg_select(FWCfgState *s, uint16_t key)
220
{
221
    int ret;
222

    
223
    s->cur_offset = 0;
224
    if ((key & FW_CFG_ENTRY_MASK) >= FW_CFG_MAX_ENTRY) {
225
        s->cur_entry = FW_CFG_INVALID;
226
        ret = 0;
227
    } else {
228
        s->cur_entry = key;
229
        ret = 1;
230
    }
231

    
232
    trace_fw_cfg_select(s, key, ret);
233
    return ret;
234
}
235

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

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

    
247
    trace_fw_cfg_read(s, ret);
248
    return ret;
249
}
250

    
251
static uint64_t fw_cfg_data_mem_read(void *opaque, hwaddr addr,
252
                                     unsigned size)
253
{
254
    return fw_cfg_read(opaque);
255
}
256

    
257
static void fw_cfg_data_mem_write(void *opaque, hwaddr addr,
258
                                  uint64_t value, unsigned size)
259
{
260
    fw_cfg_write(opaque, (uint8_t)value);
261
}
262

    
263
static void fw_cfg_ctl_mem_write(void *opaque, hwaddr addr,
264
                                 uint64_t value, unsigned size)
265
{
266
    fw_cfg_select(opaque, (uint16_t)value);
267
}
268

    
269
static bool fw_cfg_ctl_mem_valid(void *opaque, hwaddr addr,
270
                                 unsigned size, bool is_write)
271
{
272
    return is_write && size == 2;
273
}
274

    
275
static uint64_t fw_cfg_comb_read(void *opaque, hwaddr addr,
276
                                 unsigned size)
277
{
278
    return fw_cfg_read(opaque);
279
}
280

    
281
static void fw_cfg_comb_write(void *opaque, hwaddr addr,
282
                              uint64_t value, unsigned size)
283
{
284
    switch (size) {
285
    case 1:
286
        fw_cfg_write(opaque, (uint8_t)value);
287
        break;
288
    case 2:
289
        fw_cfg_select(opaque, (uint16_t)value);
290
        break;
291
    }
292
}
293

    
294
static bool fw_cfg_comb_valid(void *opaque, hwaddr addr,
295
                                  unsigned size, bool is_write)
296
{
297
    return (size == 1) || (is_write && size == 2);
298
}
299

    
300
static const MemoryRegionOps fw_cfg_ctl_mem_ops = {
301
    .write = fw_cfg_ctl_mem_write,
302
    .endianness = DEVICE_NATIVE_ENDIAN,
303
    .valid.accepts = fw_cfg_ctl_mem_valid,
304
};
305

    
306
static const MemoryRegionOps fw_cfg_data_mem_ops = {
307
    .read = fw_cfg_data_mem_read,
308
    .write = fw_cfg_data_mem_write,
309
    .endianness = DEVICE_NATIVE_ENDIAN,
310
    .valid = {
311
        .min_access_size = 1,
312
        .max_access_size = 1,
313
    },
314
};
315

    
316
static const MemoryRegionOps fw_cfg_comb_mem_ops = {
317
    .read = fw_cfg_comb_read,
318
    .write = fw_cfg_comb_write,
319
    .endianness = DEVICE_NATIVE_ENDIAN,
320
    .valid.accepts = fw_cfg_comb_valid,
321
};
322

    
323
static void fw_cfg_reset(DeviceState *d)
324
{
325
    FWCfgState *s = DO_UPCAST(FWCfgState, busdev.qdev, d);
326

    
327
    fw_cfg_select(s, 0);
328
}
329

    
330
/* Save restore 32 bit int as uint16_t
331
   This is a Big hack, but it is how the old state did it.
332
   Or we broke compatibility in the state, or we can't use struct tm
333
 */
334

    
335
static int get_uint32_as_uint16(QEMUFile *f, void *pv, size_t size)
336
{
337
    uint32_t *v = pv;
338
    *v = qemu_get_be16(f);
339
    return 0;
340
}
341

    
342
static void put_unused(QEMUFile *f, void *pv, size_t size)
343
{
344
    fprintf(stderr, "uint32_as_uint16 is only used for backward compatibility.\n");
345
    fprintf(stderr, "This functions shouldn't be called.\n");
346
}
347

    
348
static const VMStateInfo vmstate_hack_uint32_as_uint16 = {
349
    .name = "int32_as_uint16",
350
    .get  = get_uint32_as_uint16,
351
    .put  = put_unused,
352
};
353

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

    
357

    
358
static bool is_version_1(void *opaque, int version_id)
359
{
360
    return version_id == 1;
361
}
362

    
363
static const VMStateDescription vmstate_fw_cfg = {
364
    .name = "fw_cfg",
365
    .version_id = 2,
366
    .minimum_version_id = 1,
367
    .minimum_version_id_old = 1,
368
    .fields      = (VMStateField []) {
369
        VMSTATE_UINT16(cur_entry, FWCfgState),
370
        VMSTATE_UINT16_HACK(cur_offset, FWCfgState, is_version_1),
371
        VMSTATE_UINT32_V(cur_offset, FWCfgState, 2),
372
        VMSTATE_END_OF_LIST()
373
    }
374
};
375

    
376
void fw_cfg_add_bytes(FWCfgState *s, uint16_t key, void *data, size_t len)
377
{
378
    int arch = !!(key & FW_CFG_ARCH_LOCAL);
379

    
380
    key &= FW_CFG_ENTRY_MASK;
381

    
382
    assert(key < FW_CFG_MAX_ENTRY && len < UINT32_MAX);
383

    
384
    s->entries[arch][key].data = data;
385
    s->entries[arch][key].len = (uint32_t)len;
386
}
387

    
388
void fw_cfg_add_string(FWCfgState *s, uint16_t key, const char *value)
389
{
390
    size_t sz = strlen(value) + 1;
391

    
392
    return fw_cfg_add_bytes(s, key, g_memdup(value, sz), sz);
393
}
394

    
395
void fw_cfg_add_i16(FWCfgState *s, uint16_t key, uint16_t value)
396
{
397
    uint16_t *copy;
398

    
399
    copy = g_malloc(sizeof(value));
400
    *copy = cpu_to_le16(value);
401
    fw_cfg_add_bytes(s, key, copy, sizeof(value));
402
}
403

    
404
void fw_cfg_add_i32(FWCfgState *s, uint16_t key, uint32_t value)
405
{
406
    uint32_t *copy;
407

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

    
413
void fw_cfg_add_i64(FWCfgState *s, uint16_t key, uint64_t value)
414
{
415
    uint64_t *copy;
416

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

    
422
void fw_cfg_add_callback(FWCfgState *s, uint16_t key, FWCfgCallback callback,
423
                         void *callback_opaque, void *data, size_t len)
424
{
425
    int arch = !!(key & FW_CFG_ARCH_LOCAL);
426

    
427
    assert(key & FW_CFG_WRITE_CHANNEL);
428

    
429
    key &= FW_CFG_ENTRY_MASK;
430

    
431
    assert(key < FW_CFG_MAX_ENTRY && len <= UINT32_MAX);
432

    
433
    s->entries[arch][key].data = data;
434
    s->entries[arch][key].len = (uint32_t)len;
435
    s->entries[arch][key].callback_opaque = callback_opaque;
436
    s->entries[arch][key].callback = callback;
437
}
438

    
439
void fw_cfg_add_file(FWCfgState *s,  const char *filename,
440
                     void *data, size_t len)
441
{
442
    int i, index;
443
    size_t dsize;
444

    
445
    if (!s->files) {
446
        dsize = sizeof(uint32_t) + sizeof(FWCfgFile) * FW_CFG_FILE_SLOTS;
447
        s->files = g_malloc0(dsize);
448
        fw_cfg_add_bytes(s, FW_CFG_FILE_DIR, s->files, dsize);
449
    }
450

    
451
    index = be32_to_cpu(s->files->count);
452
    assert(index < FW_CFG_FILE_SLOTS);
453

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

    
456
    pstrcpy(s->files->f[index].name, sizeof(s->files->f[index].name),
457
            filename);
458
    for (i = 0; i < index; i++) {
459
        if (strcmp(s->files->f[index].name, s->files->f[i].name) == 0) {
460
            trace_fw_cfg_add_file_dupe(s, s->files->f[index].name);
461
            return;
462
        }
463
    }
464

    
465
    s->files->f[index].size   = cpu_to_be32(len);
466
    s->files->f[index].select = cpu_to_be16(FW_CFG_FILE_FIRST + index);
467
    trace_fw_cfg_add_file(s, index, s->files->f[index].name, len);
468

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

    
472
static void fw_cfg_machine_ready(struct Notifier *n, void *data)
473
{
474
    size_t len;
475
    FWCfgState *s = container_of(n, FWCfgState, machine_ready);
476
    char *bootindex = get_boot_devices_list(&len);
477

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

    
481
FWCfgState *fw_cfg_init(uint32_t ctl_port, uint32_t data_port,
482
                        hwaddr ctl_addr, hwaddr data_addr)
483
{
484
    DeviceState *dev;
485
    SysBusDevice *d;
486
    FWCfgState *s;
487

    
488
    dev = qdev_create(NULL, "fw_cfg");
489
    qdev_prop_set_uint32(dev, "ctl_iobase", ctl_port);
490
    qdev_prop_set_uint32(dev, "data_iobase", data_port);
491
    qdev_init_nofail(dev);
492
    d = sysbus_from_qdev(dev);
493

    
494
    s = DO_UPCAST(FWCfgState, busdev.qdev, dev);
495

    
496
    if (ctl_addr) {
497
        sysbus_mmio_map(d, 0, ctl_addr);
498
    }
499
    if (data_addr) {
500
        sysbus_mmio_map(d, 1, data_addr);
501
    }
502
    fw_cfg_add_bytes(s, FW_CFG_SIGNATURE, (char *)"QEMU", 4);
503
    fw_cfg_add_bytes(s, FW_CFG_UUID, qemu_uuid, 16);
504
    fw_cfg_add_i16(s, FW_CFG_NOGRAPHIC, (uint16_t)(display_type == DT_NOGRAPHIC));
505
    fw_cfg_add_i16(s, FW_CFG_NB_CPUS, (uint16_t)smp_cpus);
506
    fw_cfg_add_i16(s, FW_CFG_MAX_CPUS, (uint16_t)max_cpus);
507
    fw_cfg_add_i16(s, FW_CFG_BOOT_MENU, (uint16_t)boot_menu);
508
    fw_cfg_bootsplash(s);
509
    fw_cfg_reboot(s);
510

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

    
514
    return s;
515
}
516

    
517
static int fw_cfg_init1(SysBusDevice *dev)
518
{
519
    FWCfgState *s = FROM_SYSBUS(FWCfgState, dev);
520

    
521
    memory_region_init_io(&s->ctl_iomem, &fw_cfg_ctl_mem_ops, s,
522
                          "fwcfg.ctl", FW_CFG_SIZE);
523
    sysbus_init_mmio(dev, &s->ctl_iomem);
524
    memory_region_init_io(&s->data_iomem, &fw_cfg_data_mem_ops, s,
525
                          "fwcfg.data", FW_CFG_DATA_SIZE);
526
    sysbus_init_mmio(dev, &s->data_iomem);
527
    /* In case ctl and data overlap: */
528
    memory_region_init_io(&s->comb_iomem, &fw_cfg_comb_mem_ops, s,
529
                          "fwcfg", FW_CFG_SIZE);
530

    
531
    if (s->ctl_iobase + 1 == s->data_iobase) {
532
        sysbus_add_io(dev, s->ctl_iobase, &s->comb_iomem);
533
    } else {
534
        if (s->ctl_iobase) {
535
            sysbus_add_io(dev, s->ctl_iobase, &s->ctl_iomem);
536
        }
537
        if (s->data_iobase) {
538
            sysbus_add_io(dev, s->data_iobase, &s->data_iomem);
539
        }
540
    }
541
    return 0;
542
}
543

    
544
static Property fw_cfg_properties[] = {
545
    DEFINE_PROP_HEX32("ctl_iobase", FWCfgState, ctl_iobase, -1),
546
    DEFINE_PROP_HEX32("data_iobase", FWCfgState, data_iobase, -1),
547
    DEFINE_PROP_END_OF_LIST(),
548
};
549

    
550
static void fw_cfg_class_init(ObjectClass *klass, void *data)
551
{
552
    DeviceClass *dc = DEVICE_CLASS(klass);
553
    SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass);
554

    
555
    k->init = fw_cfg_init1;
556
    dc->no_user = 1;
557
    dc->reset = fw_cfg_reset;
558
    dc->vmsd = &vmstate_fw_cfg;
559
    dc->props = fw_cfg_properties;
560
}
561

    
562
static const TypeInfo fw_cfg_info = {
563
    .name          = "fw_cfg",
564
    .parent        = TYPE_SYS_BUS_DEVICE,
565
    .instance_size = sizeof(FWCfgState),
566
    .class_init    = fw_cfg_class_init,
567
};
568

    
569
static void fw_cfg_register_types(void)
570
{
571
    type_register_static(&fw_cfg_info);
572
}
573

    
574
type_init(fw_cfg_register_types)