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