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 "qemu/units.h"
  14#include "hw/boards.h"
  15#include "hw/s390x/storage-keys.h"
  16#include "qapi/error.h"
  17#include "qapi/qapi-commands-misc-target.h"
  18#include "qapi/qmp/qdict.h"
  19#include "qemu/error-report.h"
  20#include "sysemu/memory_mapping.h"
  21#include "exec/address-spaces.h"
  22#include "sysemu/kvm.h"
  23#include "migration/qemu-file-types.h"
  24#include "migration/register.h"
  25
  26#define S390_SKEYS_BUFFER_SIZE (128 * KiB)  /* Room for 128k storage keys */
  27#define S390_SKEYS_SAVE_FLAG_EOS 0x01
  28#define S390_SKEYS_SAVE_FLAG_SKEYS 0x02
  29#define S390_SKEYS_SAVE_FLAG_ERROR 0x04
  30
  31S390SKeysState *s390_get_skeys_device(void)
  32{
  33    S390SKeysState *ss;
  34
  35    ss = S390_SKEYS(object_resolve_path_type("", TYPE_S390_SKEYS, NULL));
  36    assert(ss);
  37    return ss;
  38}
  39
  40void s390_skeys_init(void)
  41{
  42    Object *obj;
  43
  44    if (kvm_enabled()) {
  45        obj = object_new(TYPE_KVM_S390_SKEYS);
  46    } else {
  47        obj = object_new(TYPE_QEMU_S390_SKEYS);
  48    }
  49    object_property_add_child(qdev_get_machine(), TYPE_S390_SKEYS,
  50                              obj);
  51    object_unref(obj);
  52
  53    qdev_realize(DEVICE(obj), NULL, &error_fatal);
  54}
  55
  56static void write_keys(FILE *f, uint8_t *keys, uint64_t startgfn,
  57                       uint64_t count, Error **errp)
  58{
  59    uint64_t curpage = startgfn;
  60    uint64_t maxpage = curpage + count - 1;
  61
  62    for (; curpage <= maxpage; curpage++) {
  63        uint8_t acc = (*keys & 0xF0) >> 4;
  64        int fp =  (*keys & 0x08);
  65        int ref = (*keys & 0x04);
  66        int ch = (*keys & 0x02);
  67        int res = (*keys & 0x01);
  68
  69        fprintf(f, "page=%03" PRIx64 ": key(%d) => ACC=%X, FP=%d, REF=%d,"
  70                " ch=%d, reserved=%d\n",
  71                curpage, *keys, acc, fp, ref, ch, res);
  72        keys++;
  73    }
  74}
  75
  76void hmp_info_skeys(Monitor *mon, const QDict *qdict)
  77{
  78    S390SKeysState *ss = s390_get_skeys_device();
  79    S390SKeysClass *skeyclass = S390_SKEYS_GET_CLASS(ss);
  80    uint64_t addr = qdict_get_int(qdict, "addr");
  81    uint8_t key;
  82    int r;
  83
  84    /* Quick check to see if guest is using storage keys*/
  85    if (!skeyclass->skeys_are_enabled(ss)) {
  86        monitor_printf(mon, "Error: This guest is not using storage keys\n");
  87        return;
  88    }
  89
  90    if (!address_space_access_valid(&address_space_memory,
  91                                    addr & TARGET_PAGE_MASK, TARGET_PAGE_SIZE,
  92                                    false, MEMTXATTRS_UNSPECIFIED)) {
  93        monitor_printf(mon, "Error: The given address is not valid\n");
  94        return;
  95    }
  96
  97    r = skeyclass->get_skeys(ss, addr / TARGET_PAGE_SIZE, 1, &key);
  98    if (r < 0) {
  99        monitor_printf(mon, "Error: %s\n", strerror(-r));
 100        return;
 101    }
 102
 103    monitor_printf(mon, "  key: 0x%X\n", key);
 104}
 105
 106void hmp_dump_skeys(Monitor *mon, const QDict *qdict)
 107{
 108    const char *filename = qdict_get_str(qdict, "filename");
 109    Error *err = NULL;
 110
 111    qmp_dump_skeys(filename, &err);
 112    if (err) {
 113        error_report_err(err);
 114    }
 115}
 116
 117void qmp_dump_skeys(const char *filename, Error **errp)
 118{
 119    S390SKeysState *ss = s390_get_skeys_device();
 120    S390SKeysClass *skeyclass = S390_SKEYS_GET_CLASS(ss);
 121    GuestPhysBlockList guest_phys_blocks;
 122    GuestPhysBlock *block;
 123    uint64_t pages, gfn;
 124    Error *lerr = NULL;
 125    uint8_t *buf;
 126    int ret;
 127    int fd;
 128    FILE *f;
 129
 130    /* Quick check to see if guest is using storage keys*/
 131    if (!skeyclass->skeys_are_enabled(ss)) {
 132        error_setg(errp, "This guest is not using storage keys - "
 133                         "nothing to dump");
 134        return;
 135    }
 136
 137    fd = qemu_open_old(filename, O_WRONLY | O_CREAT | O_TRUNC, 0600);
 138    if (fd < 0) {
 139        error_setg_file_open(errp, errno, filename);
 140        return;
 141    }
 142    f = fdopen(fd, "wb");
 143    if (!f) {
 144        close(fd);
 145        error_setg_file_open(errp, errno, filename);
 146        return;
 147    }
 148
 149    buf = g_try_malloc(S390_SKEYS_BUFFER_SIZE);
 150    if (!buf) {
 151        error_setg(errp, "Could not allocate memory");
 152        goto out;
 153    }
 154
 155    assert(qemu_mutex_iothread_locked());
 156    guest_phys_blocks_init(&guest_phys_blocks);
 157    guest_phys_blocks_append(&guest_phys_blocks);
 158
 159    QTAILQ_FOREACH(block, &guest_phys_blocks.head, next) {
 160        assert(QEMU_IS_ALIGNED(block->target_start, TARGET_PAGE_SIZE));
 161        assert(QEMU_IS_ALIGNED(block->target_end, TARGET_PAGE_SIZE));
 162
 163        gfn = block->target_start / TARGET_PAGE_SIZE;
 164        pages = (block->target_end - block->target_start) / TARGET_PAGE_SIZE;
 165
 166        while (pages) {
 167            const uint64_t cur_pages = MIN(pages, S390_SKEYS_BUFFER_SIZE);
 168
 169            ret = skeyclass->get_skeys(ss, gfn, cur_pages, buf);
 170            if (ret < 0) {
 171                error_setg_errno(errp, -ret, "get_keys error");
 172                goto out_free;
 173            }
 174
 175            /* write keys to stream */
 176            write_keys(f, buf, gfn, cur_pages, &lerr);
 177            if (lerr) {
 178                goto out_free;
 179            }
 180
 181            gfn += cur_pages;
 182            pages -= cur_pages;
 183        }
 184    }
 185
 186out_free:
 187    guest_phys_blocks_free(&guest_phys_blocks);
 188    error_propagate(errp, lerr);
 189    g_free(buf);
 190out:
 191    fclose(f);
 192}
 193
 194static bool qemu_s390_skeys_are_enabled(S390SKeysState *ss)
 195{
 196    QEMUS390SKeysState *skeys = QEMU_S390_SKEYS(ss);
 197
 198    /* Lockless check is sufficient. */
 199    return !!skeys->keydata;
 200}
 201
 202static bool qemu_s390_enable_skeys(S390SKeysState *ss)
 203{
 204    QEMUS390SKeysState *skeys = QEMU_S390_SKEYS(ss);
 205    static gsize initialized;
 206
 207    if (likely(skeys->keydata)) {
 208        return true;
 209    }
 210
 211    /*
 212     * TODO: Modern Linux doesn't use storage keys unless running KVM guests
 213     *       that use storage keys. Therefore, we keep it simple for now.
 214     *
 215     * 1) We should initialize to "referenced+changed" for an initial
 216     *    over-indication. Let's avoid touching megabytes of data for now and
 217     *    assume that any sane user will issue a storage key instruction before
 218     *    actually relying on this data.
 219     * 2) Relying on ram_size and allocating a big array is ugly. We should
 220     *    allocate and manage storage key data per RAMBlock or optimally using
 221     *    some sparse data structure.
 222     * 3) We only ever have a single S390SKeysState, so relying on
 223     *    g_once_init_enter() is good enough.
 224     */
 225    if (g_once_init_enter(&initialized)) {
 226        MachineState *machine = MACHINE(qdev_get_machine());
 227
 228        skeys->key_count = machine->ram_size / TARGET_PAGE_SIZE;
 229        skeys->keydata = g_malloc0(skeys->key_count);
 230        g_once_init_leave(&initialized, 1);
 231    }
 232    return false;
 233}
 234
 235static int qemu_s390_skeys_set(S390SKeysState *ss, uint64_t start_gfn,
 236                              uint64_t count, uint8_t *keys)
 237{
 238    QEMUS390SKeysState *skeydev = QEMU_S390_SKEYS(ss);
 239    int i;
 240
 241    /* Check for uint64 overflow and access beyond end of key data */
 242    if (unlikely(!skeydev->keydata || start_gfn + count > skeydev->key_count ||
 243                  start_gfn + count < count)) {
 244        error_report("Error: Setting storage keys for pages with unallocated "
 245                     "storage key memory: gfn=%" PRIx64 " count=%" PRId64,
 246                     start_gfn, count);
 247        return -EINVAL;
 248    }
 249
 250    for (i = 0; i < count; i++) {
 251        skeydev->keydata[start_gfn + i] = keys[i];
 252    }
 253    return 0;
 254}
 255
 256static int qemu_s390_skeys_get(S390SKeysState *ss, uint64_t start_gfn,
 257                               uint64_t count, uint8_t *keys)
 258{
 259    QEMUS390SKeysState *skeydev = QEMU_S390_SKEYS(ss);
 260    int i;
 261
 262    /* Check for uint64 overflow and access beyond end of key data */
 263    if (unlikely(!skeydev->keydata || start_gfn + count > skeydev->key_count ||
 264                  start_gfn + count < count)) {
 265        error_report("Error: Getting storage keys for pages with unallocated "
 266                     "storage key memory: gfn=%" PRIx64 " count=%" PRId64,
 267                     start_gfn, count);
 268        return -EINVAL;
 269    }
 270
 271    for (i = 0; i < count; i++) {
 272        keys[i] = skeydev->keydata[start_gfn + i];
 273    }
 274    return 0;
 275}
 276
 277static void qemu_s390_skeys_class_init(ObjectClass *oc, void *data)
 278{
 279    S390SKeysClass *skeyclass = S390_SKEYS_CLASS(oc);
 280    DeviceClass *dc = DEVICE_CLASS(oc);
 281
 282    skeyclass->skeys_are_enabled = qemu_s390_skeys_are_enabled;
 283    skeyclass->enable_skeys = qemu_s390_enable_skeys;
 284    skeyclass->get_skeys = qemu_s390_skeys_get;
 285    skeyclass->set_skeys = qemu_s390_skeys_set;
 286
 287    /* Reason: Internal device (only one skeys device for the whole memory) */
 288    dc->user_creatable = false;
 289}
 290
 291static const TypeInfo qemu_s390_skeys_info = {
 292    .name          = TYPE_QEMU_S390_SKEYS,
 293    .parent        = TYPE_S390_SKEYS,
 294    .instance_size = sizeof(QEMUS390SKeysState),
 295    .class_init    = qemu_s390_skeys_class_init,
 296    .class_size    = sizeof(S390SKeysClass),
 297};
 298
 299static void s390_storage_keys_save(QEMUFile *f, void *opaque)
 300{
 301    S390SKeysState *ss = S390_SKEYS(opaque);
 302    S390SKeysClass *skeyclass = S390_SKEYS_GET_CLASS(ss);
 303    GuestPhysBlockList guest_phys_blocks;
 304    GuestPhysBlock *block;
 305    uint64_t pages, gfn;
 306    int error = 0;
 307    uint8_t *buf;
 308
 309    if (!skeyclass->skeys_are_enabled(ss)) {
 310        goto end_stream;
 311    }
 312
 313    buf = g_try_malloc(S390_SKEYS_BUFFER_SIZE);
 314    if (!buf) {
 315        error_report("storage key save could not allocate memory");
 316        goto end_stream;
 317    }
 318
 319    guest_phys_blocks_init(&guest_phys_blocks);
 320    guest_phys_blocks_append(&guest_phys_blocks);
 321
 322    /* Send each contiguous physical memory range separately. */
 323    QTAILQ_FOREACH(block, &guest_phys_blocks.head, next) {
 324        assert(QEMU_IS_ALIGNED(block->target_start, TARGET_PAGE_SIZE));
 325        assert(QEMU_IS_ALIGNED(block->target_end, TARGET_PAGE_SIZE));
 326
 327        gfn = block->target_start / TARGET_PAGE_SIZE;
 328        pages = (block->target_end - block->target_start) / TARGET_PAGE_SIZE;
 329        qemu_put_be64(f, block->target_start | S390_SKEYS_SAVE_FLAG_SKEYS);
 330        qemu_put_be64(f, pages);
 331
 332        while (pages) {
 333            const uint64_t cur_pages = MIN(pages, S390_SKEYS_BUFFER_SIZE);
 334
 335            if (!error) {
 336                error = skeyclass->get_skeys(ss, gfn, cur_pages, buf);
 337                if (error) {
 338                    /*
 339                     * Create a valid stream with all 0x00 and indicate
 340                     * S390_SKEYS_SAVE_FLAG_ERROR to the destination.
 341                     */
 342                    error_report("S390_GET_KEYS error %d", error);
 343                    memset(buf, 0, S390_SKEYS_BUFFER_SIZE);
 344                }
 345            }
 346
 347            qemu_put_buffer(f, buf, cur_pages);
 348            gfn += cur_pages;
 349            pages -= cur_pages;
 350        }
 351
 352        if (error) {
 353            break;
 354        }
 355    }
 356
 357    guest_phys_blocks_free(&guest_phys_blocks);
 358    g_free(buf);
 359end_stream:
 360    if (error) {
 361        qemu_put_be64(f, S390_SKEYS_SAVE_FLAG_ERROR);
 362    } else {
 363        qemu_put_be64(f, S390_SKEYS_SAVE_FLAG_EOS);
 364    }
 365}
 366
 367static int s390_storage_keys_load(QEMUFile *f, void *opaque, int version_id)
 368{
 369    S390SKeysState *ss = S390_SKEYS(opaque);
 370    S390SKeysClass *skeyclass = S390_SKEYS_GET_CLASS(ss);
 371    int ret = 0;
 372
 373    /*
 374     * Make sure to lazy-enable if required to be done explicitly. No need to
 375     * flush any TLB as the VM is not running yet.
 376     */
 377    if (skeyclass->enable_skeys) {
 378        skeyclass->enable_skeys(ss);
 379    }
 380
 381    while (!ret) {
 382        ram_addr_t addr;
 383        int flags;
 384
 385        addr = qemu_get_be64(f);
 386        flags = addr & ~TARGET_PAGE_MASK;
 387        addr &= TARGET_PAGE_MASK;
 388
 389        switch (flags) {
 390        case S390_SKEYS_SAVE_FLAG_SKEYS: {
 391            const uint64_t total_count = qemu_get_be64(f);
 392            uint64_t handled_count = 0, cur_count;
 393            uint64_t cur_gfn = addr / TARGET_PAGE_SIZE;
 394            uint8_t *buf = g_try_malloc(S390_SKEYS_BUFFER_SIZE);
 395
 396            if (!buf) {
 397                error_report("storage key load could not allocate memory");
 398                ret = -ENOMEM;
 399                break;
 400            }
 401
 402            while (handled_count < total_count) {
 403                cur_count = MIN(total_count - handled_count,
 404                                S390_SKEYS_BUFFER_SIZE);
 405                qemu_get_buffer(f, buf, cur_count);
 406
 407                ret = skeyclass->set_skeys(ss, cur_gfn, cur_count, buf);
 408                if (ret < 0) {
 409                    error_report("S390_SET_KEYS error %d", ret);
 410                    break;
 411                }
 412                handled_count += cur_count;
 413                cur_gfn += cur_count;
 414            }
 415            g_free(buf);
 416            break;
 417        }
 418        case S390_SKEYS_SAVE_FLAG_ERROR: {
 419            error_report("Storage key data is incomplete");
 420            ret = -EINVAL;
 421            break;
 422        }
 423        case S390_SKEYS_SAVE_FLAG_EOS:
 424            /* normal exit */
 425            return 0;
 426        default:
 427            error_report("Unexpected storage key flag data: %#x", flags);
 428            ret = -EINVAL;
 429        }
 430    }
 431
 432    return ret;
 433}
 434
 435static inline bool s390_skeys_get_migration_enabled(Object *obj, Error **errp)
 436{
 437    S390SKeysState *ss = S390_SKEYS(obj);
 438
 439    return ss->migration_enabled;
 440}
 441
 442static SaveVMHandlers savevm_s390_storage_keys = {
 443    .save_state = s390_storage_keys_save,
 444    .load_state = s390_storage_keys_load,
 445};
 446
 447static inline void s390_skeys_set_migration_enabled(Object *obj, bool value,
 448                                            Error **errp)
 449{
 450    S390SKeysState *ss = S390_SKEYS(obj);
 451
 452    /* Prevent double registration of savevm handler */
 453    if (ss->migration_enabled == value) {
 454        return;
 455    }
 456
 457    ss->migration_enabled = value;
 458
 459    if (ss->migration_enabled) {
 460        register_savevm_live(TYPE_S390_SKEYS, 0, 1,
 461                             &savevm_s390_storage_keys, ss);
 462    } else {
 463        unregister_savevm(VMSTATE_IF(ss), TYPE_S390_SKEYS, ss);
 464    }
 465}
 466
 467static void s390_skeys_instance_init(Object *obj)
 468{
 469    object_property_add_bool(obj, "migration-enabled",
 470                             s390_skeys_get_migration_enabled,
 471                             s390_skeys_set_migration_enabled);
 472    object_property_set_bool(obj, "migration-enabled", true, NULL);
 473}
 474
 475static void s390_skeys_class_init(ObjectClass *oc, void *data)
 476{
 477    DeviceClass *dc = DEVICE_CLASS(oc);
 478
 479    dc->hotpluggable = false;
 480    set_bit(DEVICE_CATEGORY_MISC, dc->categories);
 481}
 482
 483static const TypeInfo s390_skeys_info = {
 484    .name          = TYPE_S390_SKEYS,
 485    .parent        = TYPE_DEVICE,
 486    .instance_init = s390_skeys_instance_init,
 487    .instance_size = sizeof(S390SKeysState),
 488    .class_init    = s390_skeys_class_init,
 489    .class_size    = sizeof(S390SKeysClass),
 490    .abstract = true,
 491};
 492
 493static void qemu_s390_skeys_register_types(void)
 494{
 495    type_register_static(&s390_skeys_info);
 496    type_register_static(&qemu_s390_skeys_info);
 497}
 498
 499type_init(qemu_s390_skeys_register_types)
 500