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