qemu/softmmu/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
 205static char *get_boot_device_path(DeviceState *dev, bool ignore_suffixes,
 206                                  const char *suffix)
 207{
 208    char *devpath = NULL, *s = NULL, *d, *bootpath;
 209
 210    if (dev) {
 211        devpath = qdev_get_fw_dev_path(dev);
 212        assert(devpath);
 213    }
 214
 215    if (!ignore_suffixes) {
 216        if (dev) {
 217            d = qdev_get_own_fw_dev_path_from_handler(dev->parent_bus, dev);
 218            if (d) {
 219                assert(!suffix);
 220                s = d;
 221            } else {
 222                s = g_strdup(suffix);
 223            }
 224        } else {
 225            s = g_strdup(suffix);
 226        }
 227    }
 228
 229    bootpath = g_strdup_printf("%s%s",
 230                               devpath ? devpath : "",
 231                               s ? s : "");
 232    g_free(devpath);
 233    g_free(s);
 234
 235    return bootpath;
 236}
 237
 238/*
 239 * This function returns null terminated string that consist of new line
 240 * separated device paths.
 241 *
 242 * memory pointed by "size" is assigned total length of the array in bytes
 243 *
 244 */
 245char *get_boot_devices_list(size_t *size)
 246{
 247    FWBootEntry *i;
 248    size_t total = 0;
 249    char *list = NULL;
 250    MachineClass *mc = MACHINE_GET_CLASS(qdev_get_machine());
 251    bool ignore_suffixes = mc->ignore_boot_device_suffixes;
 252
 253    QTAILQ_FOREACH(i, &fw_boot_order, link) {
 254        char *bootpath;
 255        size_t len;
 256
 257        bootpath = get_boot_device_path(i->dev, ignore_suffixes, i->suffix);
 258
 259        if (total) {
 260            list[total-1] = '\n';
 261        }
 262        len = strlen(bootpath) + 1;
 263        list = g_realloc(list, total + len);
 264        memcpy(&list[total], bootpath, len);
 265        total += len;
 266        g_free(bootpath);
 267    }
 268
 269    *size = total;
 270
 271    if (boot_strict && *size > 0) {
 272        list[total-1] = '\n';
 273        list = g_realloc(list, total + 5);
 274        memcpy(&list[total], "HALT", 5);
 275        *size = total + 5;
 276    }
 277    return list;
 278}
 279
 280typedef struct {
 281    int32_t *bootindex;
 282    const char *suffix;
 283    DeviceState *dev;
 284} BootIndexProperty;
 285
 286static void device_get_bootindex(Object *obj, Visitor *v, const char *name,
 287                                 void *opaque, Error **errp)
 288{
 289    BootIndexProperty *prop = opaque;
 290    visit_type_int32(v, name, prop->bootindex, errp);
 291}
 292
 293static void device_set_bootindex(Object *obj, Visitor *v, const char *name,
 294                                 void *opaque, Error **errp)
 295{
 296    BootIndexProperty *prop = opaque;
 297    int32_t boot_index;
 298    Error *local_err = NULL;
 299
 300    if (!visit_type_int32(v, name, &boot_index, errp)) {
 301        return;
 302    }
 303    /* check whether bootindex is present in fw_boot_order list  */
 304    check_boot_index(boot_index, &local_err);
 305    if (local_err) {
 306        error_propagate(errp, local_err);
 307        return;
 308    }
 309    /* change bootindex to a new one */
 310    *prop->bootindex = boot_index;
 311
 312    add_boot_device_path(*prop->bootindex, prop->dev, prop->suffix);
 313}
 314
 315static void property_release_bootindex(Object *obj, const char *name,
 316                                       void *opaque)
 317
 318{
 319    BootIndexProperty *prop = opaque;
 320
 321    del_boot_device_path(prop->dev, prop->suffix);
 322    g_free(prop);
 323}
 324
 325void device_add_bootindex_property(Object *obj, int32_t *bootindex,
 326                                   const char *name, const char *suffix,
 327                                   DeviceState *dev)
 328{
 329    BootIndexProperty *prop = g_malloc0(sizeof(*prop));
 330
 331    prop->bootindex = bootindex;
 332    prop->suffix = suffix;
 333    prop->dev = dev;
 334
 335    object_property_add(obj, name, "int32",
 336                        device_get_bootindex,
 337                        device_set_bootindex,
 338                        property_release_bootindex,
 339                        prop);
 340
 341    /* initialize devices' bootindex property to -1 */
 342    object_property_set_int(obj, name, -1, NULL);
 343}
 344
 345typedef struct FWLCHSEntry FWLCHSEntry;
 346
 347struct FWLCHSEntry {
 348    QTAILQ_ENTRY(FWLCHSEntry) link;
 349    DeviceState *dev;
 350    char *suffix;
 351    uint32_t lcyls;
 352    uint32_t lheads;
 353    uint32_t lsecs;
 354};
 355
 356static QTAILQ_HEAD(, FWLCHSEntry) fw_lchs =
 357    QTAILQ_HEAD_INITIALIZER(fw_lchs);
 358
 359void add_boot_device_lchs(DeviceState *dev, const char *suffix,
 360                          uint32_t lcyls, uint32_t lheads, uint32_t lsecs)
 361{
 362    FWLCHSEntry *node;
 363
 364    if (!lcyls && !lheads && !lsecs) {
 365        return;
 366    }
 367
 368    assert(dev != NULL || suffix != NULL);
 369
 370    node = g_malloc0(sizeof(FWLCHSEntry));
 371    node->suffix = g_strdup(suffix);
 372    node->dev = dev;
 373    node->lcyls = lcyls;
 374    node->lheads = lheads;
 375    node->lsecs = lsecs;
 376
 377    QTAILQ_INSERT_TAIL(&fw_lchs, node, link);
 378}
 379
 380void del_boot_device_lchs(DeviceState *dev, const char *suffix)
 381{
 382    FWLCHSEntry *i;
 383
 384    if (dev == NULL) {
 385        return;
 386    }
 387
 388    QTAILQ_FOREACH(i, &fw_lchs, link) {
 389        if ((!suffix || !g_strcmp0(i->suffix, suffix)) &&
 390             i->dev == dev) {
 391            QTAILQ_REMOVE(&fw_lchs, i, link);
 392            g_free(i->suffix);
 393            g_free(i);
 394
 395            break;
 396        }
 397    }
 398}
 399
 400char *get_boot_devices_lchs_list(size_t *size)
 401{
 402    FWLCHSEntry *i;
 403    size_t total = 0;
 404    char *list = NULL;
 405
 406    QTAILQ_FOREACH(i, &fw_lchs, link) {
 407        char *bootpath;
 408        char *chs_string;
 409        size_t len;
 410
 411        bootpath = get_boot_device_path(i->dev, false, i->suffix);
 412        chs_string = g_strdup_printf("%s %" PRIu32 " %" PRIu32 " %" PRIu32,
 413                                     bootpath, i->lcyls, i->lheads, i->lsecs);
 414
 415        if (total) {
 416            list[total - 1] = '\n';
 417        }
 418        len = strlen(chs_string) + 1;
 419        list = g_realloc(list, total + len);
 420        memcpy(&list[total], chs_string, len);
 421        total += len;
 422        g_free(chs_string);
 423        g_free(bootpath);
 424    }
 425
 426    *size = total;
 427
 428    return list;
 429}
 430