qemu/hw/s390x/s390-skeys.c
<<
>>
Prefs
   1/*
   2 * s390 storage key device
   3 *
   4 * Copyright 2015 IBM Corp.
   5 * Author(s): Jason J. Herne <jjherne@linux.vnet.ibm.com>
   6 *
   7 * This work is licensed under the terms of the GNU GPL, version 2 or (at
   8 * your option) any later version. See the COPYING file in the top-level
   9 * directory.
  10 */
  11
  12#include "qemu/osdep.h"
  13#include "hw/boards.h"
  14#include "qmp-commands.h"
  15#include "hw/s390x/storage-keys.h"
  16#include "qemu/error-report.h"
  17#include "sysemu/kvm.h"
  18#include "migration/register.h"
  19
  20#define S390_SKEYS_BUFFER_SIZE 131072  /* Room for 128k storage keys */
  21#define S390_SKEYS_SAVE_FLAG_EOS 0x01
  22#define S390_SKEYS_SAVE_FLAG_SKEYS 0x02
  23#define S390_SKEYS_SAVE_FLAG_ERROR 0x04
  24
  25S390SKeysState *s390_get_skeys_device(void)
  26{
  27    S390SKeysState *ss;
  28
  29    ss = S390_SKEYS(object_resolve_path_type("", TYPE_S390_SKEYS, NULL));
  30    assert(ss);
  31    return ss;
  32}
  33
  34void s390_skeys_init(void)
  35{
  36    Object *obj;
  37
  38    if (kvm_enabled()) {
  39        obj = object_new(TYPE_KVM_S390_SKEYS);
  40    } else {
  41        obj = object_new(TYPE_QEMU_S390_SKEYS);
  42    }
  43    object_property_add_child(qdev_get_machine(), TYPE_S390_SKEYS,
  44                              obj, NULL);
  45    object_unref(obj);
  46
  47    qdev_init_nofail(DEVICE(obj));
  48}
  49
  50static void write_keys(FILE *f, uint8_t *keys, uint64_t startgfn,
  51                       uint64_t count, Error **errp)
  52{
  53    uint64_t curpage = startgfn;
  54    uint64_t maxpage = curpage + count - 1;
  55
  56    for (; curpage <= maxpage; curpage++) {
  57        uint8_t acc = (*keys & 0xF0) >> 4;
  58        int fp =  (*keys & 0x08);
  59        int ref = (*keys & 0x04);
  60        int ch = (*keys & 0x02);
  61        int res = (*keys & 0x01);
  62
  63        fprintf(f, "page=%03" PRIx64 ": key(%d) => ACC=%X, FP=%d, REF=%d,"
  64                " ch=%d, reserved=%d\n",
  65                curpage, *keys, acc, fp, ref, ch, res);
  66        keys++;
  67    }
  68}
  69
  70void hmp_info_skeys(Monitor *mon, const QDict *qdict)
  71{
  72    S390SKeysState *ss = s390_get_skeys_device();
  73    S390SKeysClass *skeyclass = S390_SKEYS_GET_CLASS(ss);
  74    uint64_t addr = qdict_get_int(qdict, "addr");
  75    uint8_t key;
  76    int r;
  77
  78    /* Quick check to see if guest is using storage keys*/
  79    if (!skeyclass->skeys_enabled(ss)) {
  80        monitor_printf(mon, "Error: This guest is not using storage keys\n");
  81        return;
  82    }
  83
  84    r = skeyclass->get_skeys(ss, addr / TARGET_PAGE_SIZE, 1, &key);
  85    if (r < 0) {
  86        monitor_printf(mon, "Error: %s\n", strerror(-r));
  87        return;
  88    }
  89
  90    monitor_printf(mon, "  key: 0x%X\n", key);
  91}
  92
  93void hmp_dump_skeys(Monitor *mon, const QDict *qdict)
  94{
  95    const char *filename = qdict_get_str(qdict, "filename");
  96    Error *err = NULL;
  97
  98    qmp_dump_skeys(filename, &err);
  99    if (err) {
 100        error_report_err(err);
 101    }
 102}
 103
 104void qmp_dump_skeys(const char *filename, Error **errp)
 105{
 106    S390SKeysState *ss = s390_get_skeys_device();
 107    S390SKeysClass *skeyclass = S390_SKEYS_GET_CLASS(ss);
 108    const uint64_t total_count = ram_size / TARGET_PAGE_SIZE;
 109    uint64_t handled_count = 0, cur_count;
 110    Error *lerr = NULL;
 111    vaddr cur_gfn = 0;
 112    uint8_t *buf;
 113    int ret;
 114    int fd;
 115    FILE *f;
 116
 117    /* Quick check to see if guest is using storage keys*/
 118    if (!skeyclass->skeys_enabled(ss)) {
 119        error_setg(errp, "This guest is not using storage keys - "
 120                         "nothing to dump");
 121        return;
 122    }
 123
 124    fd = qemu_open(filename, O_WRONLY | O_CREAT | O_TRUNC, 0600);
 125    if (fd < 0) {
 126        error_setg_file_open(errp, errno, filename);
 127        return;
 128    }
 129    f = fdopen(fd, "wb");
 130    if (!f) {
 131        close(fd);
 132        error_setg_file_open(errp, errno, filename);
 133        return;
 134    }
 135
 136    buf = g_try_malloc(S390_SKEYS_BUFFER_SIZE);
 137    if (!buf) {
 138        error_setg(errp, "Could not allocate memory");
 139        goto out;
 140    }
 141
 142    /* we'll only dump initial memory for now */
 143    while (handled_count < total_count) {
 144        /* Calculate how many keys to ask for & handle overflow case */
 145        cur_count = MIN(total_count - handled_count, S390_SKEYS_BUFFER_SIZE);
 146
 147        ret = skeyclass->get_skeys(ss, cur_gfn, cur_count, buf);
 148        if (ret < 0) {
 149            error_setg(errp, "get_keys error %d", ret);
 150            goto out_free;
 151        }
 152
 153        /* write keys to stream */
 154        write_keys(f, buf, cur_gfn, cur_count, &lerr);
 155        if (lerr) {
 156            goto out_free;
 157        }
 158
 159        cur_gfn += cur_count;
 160        handled_count += cur_count;
 161    }
 162
 163out_free:
 164    error_propagate(errp, lerr);
 165    g_free(buf);
 166out:
 167    fclose(f);
 168}
 169
 170static void qemu_s390_skeys_init(Object *obj)
 171{
 172    QEMUS390SKeysState *skeys = QEMU_S390_SKEYS(obj);
 173    MachineState *machine = MACHINE(qdev_get_machine());
 174
 175    skeys->key_count = machine->maxram_size / TARGET_PAGE_SIZE;
 176    skeys->keydata = g_malloc0(skeys->key_count);
 177}
 178
 179static int qemu_s390_skeys_enabled(S390SKeysState *ss)
 180{
 181    return 1;
 182}
 183
 184/*
 185 * TODO: for memory hotplug support qemu_s390_skeys_set and qemu_s390_skeys_get
 186 * will have to make sure that the given gfn belongs to a memory region and not
 187 * a memory hole.
 188 */
 189static int qemu_s390_skeys_set(S390SKeysState *ss, uint64_t start_gfn,
 190                              uint64_t count, uint8_t *keys)
 191{
 192    QEMUS390SKeysState *skeydev = QEMU_S390_SKEYS(ss);
 193    int i;
 194
 195    /* Check for uint64 overflow and access beyond end of key data */
 196    if (start_gfn + count > skeydev->key_count || start_gfn + count < count) {
 197        error_report("Error: Setting storage keys for page beyond the end "
 198                     "of memory: gfn=%" PRIx64 " count=%" PRId64,
 199                     start_gfn, count);
 200        return -EINVAL;
 201    }
 202
 203    for (i = 0; i < count; i++) {
 204        skeydev->keydata[start_gfn + i] = keys[i];
 205    }
 206    return 0;
 207}
 208
 209static int qemu_s390_skeys_get(S390SKeysState *ss, uint64_t start_gfn,
 210                               uint64_t count, uint8_t *keys)
 211{
 212    QEMUS390SKeysState *skeydev = QEMU_S390_SKEYS(ss);
 213    int i;
 214
 215    /* Check for uint64 overflow and access beyond end of key data */
 216    if (start_gfn + count > skeydev->key_count || start_gfn + count < count) {
 217        error_report("Error: Getting storage keys for page beyond the end "
 218                     "of memory: gfn=%" PRIx64 " count=%" PRId64,
 219                     start_gfn, count);
 220        return -EINVAL;
 221    }
 222
 223    for (i = 0; i < count; i++) {
 224        keys[i] = skeydev->keydata[start_gfn + i];
 225    }
 226    return 0;
 227}
 228
 229static void qemu_s390_skeys_class_init(ObjectClass *oc, void *data)
 230{
 231    S390SKeysClass *skeyclass = S390_SKEYS_CLASS(oc);
 232    DeviceClass *dc = DEVICE_CLASS(oc);
 233
 234    skeyclass->skeys_enabled = qemu_s390_skeys_enabled;
 235    skeyclass->get_skeys = qemu_s390_skeys_get;
 236    skeyclass->set_skeys = qemu_s390_skeys_set;
 237
 238    /* Reason: Internal device (only one skeys device for the whole memory) */
 239    dc->user_creatable = false;
 240}
 241
 242static const TypeInfo qemu_s390_skeys_info = {
 243    .name          = TYPE_QEMU_S390_SKEYS,
 244    .parent        = TYPE_S390_SKEYS,
 245    .instance_init = qemu_s390_skeys_init,
 246    .instance_size = sizeof(QEMUS390SKeysState),
 247    .class_init    = qemu_s390_skeys_class_init,
 248    .class_size    = sizeof(S390SKeysClass),
 249};
 250
 251static void s390_storage_keys_save(QEMUFile *f, void *opaque)
 252{
 253    S390SKeysState *ss = S390_SKEYS(opaque);
 254    S390SKeysClass *skeyclass = S390_SKEYS_GET_CLASS(ss);
 255    uint64_t pages_left = ram_size / TARGET_PAGE_SIZE;
 256    uint64_t read_count, eos = S390_SKEYS_SAVE_FLAG_EOS;
 257    vaddr cur_gfn = 0;
 258    int error = 0;
 259    uint8_t *buf;
 260
 261    if (!skeyclass->skeys_enabled(ss)) {
 262        goto end_stream;
 263    }
 264
 265    buf = g_try_malloc(S390_SKEYS_BUFFER_SIZE);
 266    if (!buf) {
 267        error_report("storage key save could not allocate memory");
 268        goto end_stream;
 269    }
 270
 271    /* We only support initial memory. Standby memory is not handled yet. */
 272    qemu_put_be64(f, (cur_gfn * TARGET_PAGE_SIZE) | S390_SKEYS_SAVE_FLAG_SKEYS);
 273    qemu_put_be64(f, pages_left);
 274
 275    while (pages_left) {
 276        read_count = MIN(pages_left, S390_SKEYS_BUFFER_SIZE);
 277
 278        if (!error) {
 279            error = skeyclass->get_skeys(ss, cur_gfn, read_count, buf);
 280            if (error) {
 281                /*
 282                 * If error: we want to fill the stream with valid data instead
 283                 * of stopping early so we pad the stream with 0x00 values and
 284                 * use S390_SKEYS_SAVE_FLAG_ERROR to indicate failure to the
 285                 * reading side.
 286                 */
 287                error_report("S390_GET_KEYS error %d", error);
 288                memset(buf, 0, S390_SKEYS_BUFFER_SIZE);
 289                eos = S390_SKEYS_SAVE_FLAG_ERROR;
 290            }
 291        }
 292
 293        qemu_put_buffer(f, buf, read_count);
 294        cur_gfn += read_count;
 295        pages_left -= read_count;
 296    }
 297
 298    g_free(buf);
 299end_stream:
 300    qemu_put_be64(f, eos);
 301}
 302
 303static int s390_storage_keys_load(QEMUFile *f, void *opaque, int version_id)
 304{
 305    S390SKeysState *ss = S390_SKEYS(opaque);
 306    S390SKeysClass *skeyclass = S390_SKEYS_GET_CLASS(ss);
 307    int ret = 0;
 308
 309    while (!ret) {
 310        ram_addr_t addr;
 311        int flags;
 312
 313        addr = qemu_get_be64(f);
 314        flags = addr & ~TARGET_PAGE_MASK;
 315        addr &= TARGET_PAGE_MASK;
 316
 317        switch (flags) {
 318        case S390_SKEYS_SAVE_FLAG_SKEYS: {
 319            const uint64_t total_count = qemu_get_be64(f);
 320            uint64_t handled_count = 0, cur_count;
 321            uint64_t cur_gfn = addr / TARGET_PAGE_SIZE;
 322            uint8_t *buf = g_try_malloc(S390_SKEYS_BUFFER_SIZE);
 323
 324            if (!buf) {
 325                error_report("storage key load could not allocate memory");
 326                ret = -ENOMEM;
 327                break;
 328            }
 329
 330            while (handled_count < total_count) {
 331                cur_count = MIN(total_count - handled_count,
 332                                S390_SKEYS_BUFFER_SIZE);
 333                qemu_get_buffer(f, buf, cur_count);
 334
 335                ret = skeyclass->set_skeys(ss, cur_gfn, cur_count, buf);
 336                if (ret < 0) {
 337                    error_report("S390_SET_KEYS error %d", ret);
 338                    break;
 339                }
 340                handled_count += cur_count;
 341                cur_gfn += cur_count;
 342            }
 343            g_free(buf);
 344            break;
 345        }
 346        case S390_SKEYS_SAVE_FLAG_ERROR: {
 347            error_report("Storage key data is incomplete");
 348            ret = -EINVAL;
 349            break;
 350        }
 351        case S390_SKEYS_SAVE_FLAG_EOS:
 352            /* normal exit */
 353            return 0;
 354        default:
 355            error_report("Unexpected storage key flag data: %#x", flags);
 356            ret = -EINVAL;
 357        }
 358    }
 359
 360    return ret;
 361}
 362
 363static inline bool s390_skeys_get_migration_enabled(Object *obj, Error **errp)
 364{
 365    S390SKeysState *ss = S390_SKEYS(obj);
 366
 367    return ss->migration_enabled;
 368}
 369
 370static SaveVMHandlers savevm_s390_storage_keys = {
 371    .save_state = s390_storage_keys_save,
 372    .load_state = s390_storage_keys_load,
 373};
 374
 375static inline void s390_skeys_set_migration_enabled(Object *obj, bool value,
 376                                            Error **errp)
 377{
 378    S390SKeysState *ss = S390_SKEYS(obj);
 379
 380    /* Prevent double registration of savevm handler */
 381    if (ss->migration_enabled == value) {
 382        return;
 383    }
 384
 385    ss->migration_enabled = value;
 386
 387    if (ss->migration_enabled) {
 388        register_savevm_live(NULL, TYPE_S390_SKEYS, 0, 1,
 389                             &savevm_s390_storage_keys, ss);
 390    } else {
 391        unregister_savevm(DEVICE(ss), TYPE_S390_SKEYS, ss);
 392    }
 393}
 394
 395static void s390_skeys_instance_init(Object *obj)
 396{
 397    object_property_add_bool(obj, "migration-enabled",
 398                             s390_skeys_get_migration_enabled,
 399                             s390_skeys_set_migration_enabled, NULL);
 400    object_property_set_bool(obj, true, "migration-enabled", NULL);
 401}
 402
 403static void s390_skeys_class_init(ObjectClass *oc, void *data)
 404{
 405    DeviceClass *dc = DEVICE_CLASS(oc);
 406
 407    dc->hotpluggable = false;
 408    set_bit(DEVICE_CATEGORY_MISC, dc->categories);
 409}
 410
 411static const TypeInfo s390_skeys_info = {
 412    .name          = TYPE_S390_SKEYS,
 413    .parent        = TYPE_DEVICE,
 414    .instance_init = s390_skeys_instance_init,
 415    .instance_size = sizeof(S390SKeysState),
 416    .class_init    = s390_skeys_class_init,
 417    .class_size    = sizeof(S390SKeysClass),
 418    .abstract = true,
 419};
 420
 421static void qemu_s390_skeys_register_types(void)
 422{
 423    type_register_static(&s390_skeys_info);
 424    type_register_static(&qemu_s390_skeys_info);
 425}
 426
 427type_init(qemu_s390_skeys_register_types)
 428