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 "migration/qemu-file.h"
  16#include "hw/s390x/storage-keys.h"
  17#include "qemu/error-report.h"
  18#include "sysemu/kvm.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
 233    skeyclass->skeys_enabled = qemu_s390_skeys_enabled;
 234    skeyclass->get_skeys = qemu_s390_skeys_get;
 235    skeyclass->set_skeys = qemu_s390_skeys_set;
 236}
 237
 238static const TypeInfo qemu_s390_skeys_info = {
 239    .name          = TYPE_QEMU_S390_SKEYS,
 240    .parent        = TYPE_S390_SKEYS,
 241    .instance_init = qemu_s390_skeys_init,
 242    .instance_size = sizeof(QEMUS390SKeysState),
 243    .class_init    = qemu_s390_skeys_class_init,
 244    .class_size    = sizeof(S390SKeysClass),
 245};
 246
 247static void s390_storage_keys_save(QEMUFile *f, void *opaque)
 248{
 249    S390SKeysState *ss = S390_SKEYS(opaque);
 250    S390SKeysClass *skeyclass = S390_SKEYS_GET_CLASS(ss);
 251    uint64_t pages_left = ram_size / TARGET_PAGE_SIZE;
 252    uint64_t read_count, eos = S390_SKEYS_SAVE_FLAG_EOS;
 253    vaddr cur_gfn = 0;
 254    int error = 0;
 255    uint8_t *buf;
 256
 257    if (!skeyclass->skeys_enabled(ss)) {
 258        goto end_stream;
 259    }
 260
 261    buf = g_try_malloc(S390_SKEYS_BUFFER_SIZE);
 262    if (!buf) {
 263        error_report("storage key save could not allocate memory");
 264        goto end_stream;
 265    }
 266
 267    /* We only support initial memory. Standby memory is not handled yet. */
 268    qemu_put_be64(f, (cur_gfn * TARGET_PAGE_SIZE) | S390_SKEYS_SAVE_FLAG_SKEYS);
 269    qemu_put_be64(f, pages_left);
 270
 271    while (pages_left) {
 272        read_count = MIN(pages_left, S390_SKEYS_BUFFER_SIZE);
 273
 274        if (!error) {
 275            error = skeyclass->get_skeys(ss, cur_gfn, read_count, buf);
 276            if (error) {
 277                /*
 278                 * If error: we want to fill the stream with valid data instead
 279                 * of stopping early so we pad the stream with 0x00 values and
 280                 * use S390_SKEYS_SAVE_FLAG_ERROR to indicate failure to the
 281                 * reading side.
 282                 */
 283                error_report("S390_GET_KEYS error %d", error);
 284                memset(buf, 0, S390_SKEYS_BUFFER_SIZE);
 285                eos = S390_SKEYS_SAVE_FLAG_ERROR;
 286            }
 287        }
 288
 289        qemu_put_buffer(f, buf, read_count);
 290        cur_gfn += read_count;
 291        pages_left -= read_count;
 292    }
 293
 294    g_free(buf);
 295end_stream:
 296    qemu_put_be64(f, eos);
 297}
 298
 299static int s390_storage_keys_load(QEMUFile *f, void *opaque, int version_id)
 300{
 301    S390SKeysState *ss = S390_SKEYS(opaque);
 302    S390SKeysClass *skeyclass = S390_SKEYS_GET_CLASS(ss);
 303    int ret = 0;
 304
 305    while (!ret) {
 306        ram_addr_t addr;
 307        int flags;
 308
 309        addr = qemu_get_be64(f);
 310        flags = addr & ~TARGET_PAGE_MASK;
 311        addr &= TARGET_PAGE_MASK;
 312
 313        switch (flags) {
 314        case S390_SKEYS_SAVE_FLAG_SKEYS: {
 315            const uint64_t total_count = qemu_get_be64(f);
 316            uint64_t handled_count = 0, cur_count;
 317            uint64_t cur_gfn = addr / TARGET_PAGE_SIZE;
 318            uint8_t *buf = g_try_malloc(S390_SKEYS_BUFFER_SIZE);
 319
 320            if (!buf) {
 321                error_report("storage key load could not allocate memory");
 322                ret = -ENOMEM;
 323                break;
 324            }
 325
 326            while (handled_count < total_count) {
 327                cur_count = MIN(total_count - handled_count,
 328                                S390_SKEYS_BUFFER_SIZE);
 329                qemu_get_buffer(f, buf, cur_count);
 330
 331                ret = skeyclass->set_skeys(ss, cur_gfn, cur_count, buf);
 332                if (ret < 0) {
 333                    error_report("S390_SET_KEYS error %d", ret);
 334                    break;
 335                }
 336                handled_count += cur_count;
 337                cur_gfn += cur_count;
 338            }
 339            g_free(buf);
 340            break;
 341        }
 342        case S390_SKEYS_SAVE_FLAG_ERROR: {
 343            error_report("Storage key data is incomplete");
 344            ret = -EINVAL;
 345            break;
 346        }
 347        case S390_SKEYS_SAVE_FLAG_EOS:
 348            /* normal exit */
 349            return 0;
 350        default:
 351            error_report("Unexpected storage key flag data: %#x", flags);
 352            ret = -EINVAL;
 353        }
 354    }
 355
 356    return ret;
 357}
 358
 359static inline bool s390_skeys_get_migration_enabled(Object *obj, Error **errp)
 360{
 361    S390SKeysState *ss = S390_SKEYS(obj);
 362
 363    return ss->migration_enabled;
 364}
 365
 366static inline void s390_skeys_set_migration_enabled(Object *obj, bool value,
 367                                            Error **errp)
 368{
 369    S390SKeysState *ss = S390_SKEYS(obj);
 370
 371    /* Prevent double registration of savevm handler */
 372    if (ss->migration_enabled == value) {
 373        return;
 374    }
 375
 376    ss->migration_enabled = value;
 377
 378    if (ss->migration_enabled) {
 379        register_savevm(NULL, TYPE_S390_SKEYS, 0, 1, s390_storage_keys_save,
 380                        s390_storage_keys_load, ss);
 381    } else {
 382        unregister_savevm(DEVICE(ss), TYPE_S390_SKEYS, ss);
 383    }
 384}
 385
 386static void s390_skeys_instance_init(Object *obj)
 387{
 388    object_property_add_bool(obj, "migration-enabled",
 389                             s390_skeys_get_migration_enabled,
 390                             s390_skeys_set_migration_enabled, NULL);
 391    object_property_set_bool(obj, true, "migration-enabled", NULL);
 392}
 393
 394static void s390_skeys_class_init(ObjectClass *oc, void *data)
 395{
 396    DeviceClass *dc = DEVICE_CLASS(oc);
 397
 398    dc->hotpluggable = false;
 399    set_bit(DEVICE_CATEGORY_MISC, dc->categories);
 400}
 401
 402static const TypeInfo s390_skeys_info = {
 403    .name          = TYPE_S390_SKEYS,
 404    .parent        = TYPE_DEVICE,
 405    .instance_init = s390_skeys_instance_init,
 406    .instance_size = sizeof(S390SKeysState),
 407    .class_init    = s390_skeys_class_init,
 408    .class_size    = sizeof(S390SKeysClass),
 409    .abstract = true,
 410};
 411
 412static void qemu_s390_skeys_register_types(void)
 413{
 414    type_register_static(&s390_skeys_info);
 415    type_register_static(&qemu_s390_skeys_info);
 416}
 417
 418type_init(qemu_s390_skeys_register_types)
 419