qemu/bootdevice.c
<<
>>
Prefs
   1/*
   2 * QEMU Boot Device Implement
   3 *
   4 * Copyright (c) 2014 HUAWEI TECHNOLOGIES CO., LTD.
   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
  25#include "qemu/osdep.h"
  26#include "qapi/error.h"
  27#include "sysemu/sysemu.h"
  28#include "qapi/visitor.h"
  29#include "qemu/error-report.h"
  30#include "sysemu/reset.h"
  31#include "hw/qdev-core.h"
  32#include "hw/boards.h"
  33
  34typedef struct FWBootEntry FWBootEntry;
  35
  36struct FWBootEntry {
  37    QTAILQ_ENTRY(FWBootEntry) link;
  38    int32_t bootindex;
  39    DeviceState *dev;
  40    char *suffix;
  41};
  42
  43static QTAILQ_HEAD(, FWBootEntry) fw_boot_order =
  44    QTAILQ_HEAD_INITIALIZER(fw_boot_order);
  45static QEMUBootSetHandler *boot_set_handler;
  46static void *boot_set_opaque;
  47
  48void qemu_register_boot_set(QEMUBootSetHandler *func, void *opaque)
  49{
  50    boot_set_handler = func;
  51    boot_set_opaque = opaque;
  52}
  53
  54void qemu_boot_set(const char *boot_order, Error **errp)
  55{
  56    Error *local_err = NULL;
  57
  58    if (!boot_set_handler) {
  59        error_setg(errp, "no function defined to set boot device list for"
  60                         " this architecture");
  61        return;
  62    }
  63
  64    validate_bootdevices(boot_order, &local_err);
  65    if (local_err) {
  66        error_propagate(errp, local_err);
  67        return;
  68    }
  69
  70    boot_set_handler(boot_set_opaque, boot_order, errp);
  71}
  72
  73void validate_bootdevices(const char *devices, Error **errp)
  74{
  75    /* We just do some generic consistency checks */
  76    const char *p;
  77    int bitmap = 0;
  78
  79    for (p = devices; *p != '\0'; p++) {
  80        /* Allowed boot devices are:
  81         * a-b: floppy disk drives
  82         * c-f: IDE disk drives
  83         * g-m: machine implementation dependent drives
  84         * n-p: network devices
  85         * It's up to each machine implementation to check if the given boot
  86         * devices match the actual hardware implementation and firmware
  87         * features.
  88         */
  89        if (*p < 'a' || *p > 'p') {
  90            error_setg(errp, "Invalid boot device '%c'", *p);
  91            return;
  92        }
  93        if (bitmap & (1 << (*p - 'a'))) {
  94            error_setg(errp, "Boot device '%c' was given twice", *p);
  95            return;
  96        }
  97        bitmap |= 1 << (*p - 'a');
  98    }
  99}
 100
 101void restore_boot_order(void *opaque)
 102{
 103    char *normal_boot_order = opaque;
 104    static int first = 1;
 105
 106    /* Restore boot order and remove ourselves after the first boot */
 107    if (first) {
 108        first = 0;
 109        return;
 110    }
 111
 112    if (boot_set_handler) {
 113        qemu_boot_set(normal_boot_order, &error_abort);
 114    }
 115
 116    qemu_unregister_reset(restore_boot_order, normal_boot_order);
 117    g_free(normal_boot_order);
 118}
 119
 120void check_boot_index(int32_t bootindex, Error **errp)
 121{
 122    FWBootEntry *i;
 123
 124    if (bootindex >= 0) {
 125        QTAILQ_FOREACH(i, &fw_boot_order, link) {
 126            if (i->bootindex == bootindex) {
 127                error_setg(errp, "The bootindex %d has already been used",
 128                           bootindex);
 129                return;
 130            }
 131        }
 132    }
 133}
 134
 135void del_boot_device_path(DeviceState *dev, const char *suffix)
 136{
 137    FWBootEntry *i;
 138
 139    if (dev == NULL) {
 140        return;
 141    }
 142
 143    QTAILQ_FOREACH(i, &fw_boot_order, link) {
 144        if ((!suffix || !g_strcmp0(i->suffix, suffix)) &&
 145             i->dev == dev) {
 146            QTAILQ_REMOVE(&fw_boot_order, i, link);
 147            g_free(i->suffix);
 148            g_free(i);
 149
 150            break;
 151        }
 152    }
 153}
 154
 155void add_boot_device_path(int32_t bootindex, DeviceState *dev,
 156                          const char *suffix)
 157{
 158    FWBootEntry *node, *i;
 159
 160    if (bootindex < 0) {
 161        del_boot_device_path(dev, suffix);
 162        return;
 163    }
 164
 165    assert(dev != NULL || suffix != NULL);
 166
 167    del_boot_device_path(dev, suffix);
 168
 169    node = g_malloc0(sizeof(FWBootEntry));
 170    node->bootindex = bootindex;
 171    node->suffix = g_strdup(suffix);
 172    node->dev = dev;
 173
 174    QTAILQ_FOREACH(i, &fw_boot_order, link) {
 175        if (i->bootindex == bootindex) {
 176            error_report("Two devices with same boot index %d", bootindex);
 177            exit(1);
 178        } else if (i->bootindex < bootindex) {
 179            continue;
 180        }
 181        QTAILQ_INSERT_BEFORE(i, node, link);
 182        return;
 183    }
 184    QTAILQ_INSERT_TAIL(&fw_boot_order, node, link);
 185}
 186
 187DeviceState *get_boot_device(uint32_t position)
 188{
 189    uint32_t counter = 0;
 190    FWBootEntry *i = NULL;
 191    DeviceState *res = NULL;
 192
 193    if (!QTAILQ_EMPTY(&fw_boot_order)) {
 194        QTAILQ_FOREACH(i, &fw_boot_order, link) {
 195            if (counter == position) {
 196                res = i->dev;
 197                break;
 198            }
 199            counter++;
 200        }
 201    }
 202    return res;
 203}
 204
 205/*
 206 * This function returns null terminated string that consist of new line
 207 * separated device paths.
 208 *
 209 * memory pointed by "size" is assigned total length of the array in bytes
 210 *
 211 */
 212char *get_boot_devices_list(size_t *size)
 213{
 214    FWBootEntry *i;
 215    size_t total = 0;
 216    char *list = NULL;
 217    MachineClass *mc = MACHINE_GET_CLASS(qdev_get_machine());
 218    bool ignore_suffixes = mc->ignore_boot_device_suffixes;
 219
 220    QTAILQ_FOREACH(i, &fw_boot_order, link) {
 221        char *devpath = NULL,  *suffix = NULL;
 222        char *bootpath;
 223        char *d;
 224        size_t len;
 225
 226        if (i->dev) {
 227            devpath = qdev_get_fw_dev_path(i->dev);
 228            assert(devpath);
 229        }
 230
 231        if (!ignore_suffixes) {
 232            if (i->dev) {
 233                d = qdev_get_own_fw_dev_path_from_handler(i->dev->parent_bus,
 234                                                          i->dev);
 235                if (d) {
 236                    assert(!i->suffix);
 237                    suffix = d;
 238                } else {
 239                    suffix = g_strdup(i->suffix);
 240                }
 241            } else {
 242                suffix = g_strdup(i->suffix);
 243            }
 244        }
 245
 246        bootpath = g_strdup_printf("%s%s",
 247                                   devpath ? devpath : "",
 248                                   suffix ? suffix : "");
 249        g_free(devpath);
 250        g_free(suffix);
 251
 252        if (total) {
 253            list[total-1] = '\n';
 254        }
 255        len = strlen(bootpath) + 1;
 256        list = g_realloc(list, total + len);
 257        memcpy(&list[total], bootpath, len);
 258        total += len;
 259        g_free(bootpath);
 260    }
 261
 262    *size = total;
 263
 264    if (boot_strict && *size > 0) {
 265        list[total-1] = '\n';
 266        list = g_realloc(list, total + 5);
 267        memcpy(&list[total], "HALT", 5);
 268        *size = total + 5;
 269    }
 270    return list;
 271}
 272
 273typedef struct {
 274    int32_t *bootindex;
 275    const char *suffix;
 276    DeviceState *dev;
 277} BootIndexProperty;
 278
 279static void device_get_bootindex(Object *obj, Visitor *v, const char *name,
 280                                 void *opaque, Error **errp)
 281{
 282    BootIndexProperty *prop = opaque;
 283    visit_type_int32(v, name, prop->bootindex, errp);
 284}
 285
 286static void device_set_bootindex(Object *obj, Visitor *v, const char *name,
 287                                 void *opaque, Error **errp)
 288{
 289    BootIndexProperty *prop = opaque;
 290    int32_t boot_index;
 291    Error *local_err = NULL;
 292
 293    visit_type_int32(v, name, &boot_index, &local_err);
 294    if (local_err) {
 295        goto out;
 296    }
 297    /* check whether bootindex is present in fw_boot_order list  */
 298    check_boot_index(boot_index, &local_err);
 299    if (local_err) {
 300        goto out;
 301    }
 302    /* change bootindex to a new one */
 303    *prop->bootindex = boot_index;
 304
 305    add_boot_device_path(*prop->bootindex, prop->dev, prop->suffix);
 306
 307out:
 308    error_propagate(errp, local_err);
 309}
 310
 311static void property_release_bootindex(Object *obj, const char *name,
 312                                       void *opaque)
 313
 314{
 315    BootIndexProperty *prop = opaque;
 316
 317    del_boot_device_path(prop->dev, prop->suffix);
 318    g_free(prop);
 319}
 320
 321void device_add_bootindex_property(Object *obj, int32_t *bootindex,
 322                                   const char *name, const char *suffix,
 323                                   DeviceState *dev, Error **errp)
 324{
 325    Error *local_err = NULL;
 326    BootIndexProperty *prop = g_malloc0(sizeof(*prop));
 327
 328    prop->bootindex = bootindex;
 329    prop->suffix = suffix;
 330    prop->dev = dev;
 331
 332    object_property_add(obj, name, "int32",
 333                        device_get_bootindex,
 334                        device_set_bootindex,
 335                        property_release_bootindex,
 336                        prop, &local_err);
 337
 338    if (local_err) {
 339        error_propagate(errp, local_err);
 340        g_free(prop);
 341        return;
 342    }
 343    /* initialize devices' bootindex property to -1 */
 344    object_property_set_int(obj, -1, name, NULL);
 345}
 346