| /* | 
 |  * s390 storage attributes device | 
 |  * | 
 |  * Copyright 2016 IBM Corp. | 
 |  * Author(s): Claudio Imbrenda <imbrenda@linux.vnet.ibm.com> | 
 |  * | 
 |  * This work is licensed under the terms of the GNU GPL, version 2 or (at | 
 |  * your option) any later version. See the COPYING file in the top-level | 
 |  * directory. | 
 |  */ | 
 |  | 
 | #include "qemu/osdep.h" | 
 | #include "qemu/units.h" | 
 | #include "migration/qemu-file.h" | 
 | #include "migration/register.h" | 
 | #include "hw/qdev-properties.h" | 
 | #include "hw/s390x/storage-attributes.h" | 
 | #include "qemu/error-report.h" | 
 | #include "exec/ram_addr.h" | 
 | #include "qapi/error.h" | 
 | #include "qapi/qmp/qdict.h" | 
 | #include "cpu.h" | 
 |  | 
 | /* 512KiB cover 2GB of guest memory */ | 
 | #define CMMA_BLOCK_SIZE  (512 * KiB) | 
 |  | 
 | #define STATTR_FLAG_EOS     0x01ULL | 
 | #define STATTR_FLAG_MORE    0x02ULL | 
 | #define STATTR_FLAG_ERROR   0x04ULL | 
 | #define STATTR_FLAG_DONE    0x08ULL | 
 |  | 
 | static S390StAttribState *s390_get_stattrib_device(void) | 
 | { | 
 |     S390StAttribState *sas; | 
 |  | 
 |     sas = S390_STATTRIB(object_resolve_path_type("", TYPE_S390_STATTRIB, NULL)); | 
 |     assert(sas); | 
 |     return sas; | 
 | } | 
 |  | 
 | void s390_stattrib_init(void) | 
 | { | 
 |     Object *obj; | 
 |  | 
 |     obj = kvm_s390_stattrib_create(); | 
 |     if (!obj) { | 
 |         obj = object_new(TYPE_QEMU_S390_STATTRIB); | 
 |     } | 
 |  | 
 |     object_property_add_child(qdev_get_machine(), TYPE_S390_STATTRIB, | 
 |                               obj); | 
 |     object_unref(obj); | 
 |  | 
 |     qdev_realize(DEVICE(obj), NULL, &error_fatal); | 
 | } | 
 |  | 
 | /* Console commands: */ | 
 |  | 
 | void hmp_migrationmode(Monitor *mon, const QDict *qdict) | 
 | { | 
 |     S390StAttribState *sas = s390_get_stattrib_device(); | 
 |     S390StAttribClass *sac = S390_STATTRIB_GET_CLASS(sas); | 
 |     uint64_t what = qdict_get_int(qdict, "mode"); | 
 |     Error *local_err = NULL; | 
 |     int r; | 
 |  | 
 |     r = sac->set_migrationmode(sas, what, &local_err); | 
 |     if (r < 0) { | 
 |         monitor_printf(mon, "Error: %s", error_get_pretty(local_err)); | 
 |         error_free(local_err); | 
 |     } | 
 | } | 
 |  | 
 | void hmp_info_cmma(Monitor *mon, const QDict *qdict) | 
 | { | 
 |     S390StAttribState *sas = s390_get_stattrib_device(); | 
 |     S390StAttribClass *sac = S390_STATTRIB_GET_CLASS(sas); | 
 |     uint64_t addr = qdict_get_int(qdict, "addr"); | 
 |     uint64_t buflen = qdict_get_try_int(qdict, "count", 8); | 
 |     uint8_t *vals; | 
 |     int cx, len; | 
 |  | 
 |     vals = g_try_malloc(buflen); | 
 |     if (!vals) { | 
 |         monitor_printf(mon, "Error: %s\n", strerror(errno)); | 
 |         return; | 
 |     } | 
 |  | 
 |     len = sac->peek_stattr(sas, addr / TARGET_PAGE_SIZE, buflen, vals); | 
 |     if (len < 0) { | 
 |         monitor_printf(mon, "Error: %s", strerror(-len)); | 
 |         goto out; | 
 |     } | 
 |  | 
 |     monitor_printf(mon, "  CMMA attributes, " | 
 |                    "pages %" PRIu64 "+%d (0x%" PRIx64 "):\n", | 
 |                    addr / TARGET_PAGE_SIZE, len, addr & ~TARGET_PAGE_MASK); | 
 |     for (cx = 0; cx < len; cx++) { | 
 |         if (cx % 8 == 7) { | 
 |             monitor_printf(mon, "%02x\n", vals[cx]); | 
 |         } else { | 
 |             monitor_printf(mon, "%02x", vals[cx]); | 
 |         } | 
 |     } | 
 |     monitor_printf(mon, "\n"); | 
 |  | 
 | out: | 
 |     g_free(vals); | 
 | } | 
 |  | 
 | /* Migration support: */ | 
 |  | 
 | static int cmma_load(QEMUFile *f, void *opaque, int version_id) | 
 | { | 
 |     S390StAttribState *sas = S390_STATTRIB(opaque); | 
 |     S390StAttribClass *sac = S390_STATTRIB_GET_CLASS(sas); | 
 |     uint64_t count, cur_gfn; | 
 |     int flags, ret = 0; | 
 |     ram_addr_t addr; | 
 |     uint8_t *buf; | 
 |  | 
 |     while (!ret) { | 
 |         addr = qemu_get_be64(f); | 
 |         flags = addr & ~TARGET_PAGE_MASK; | 
 |         addr &= TARGET_PAGE_MASK; | 
 |  | 
 |         switch (flags) { | 
 |         case STATTR_FLAG_MORE: { | 
 |             cur_gfn = addr / TARGET_PAGE_SIZE; | 
 |             count = qemu_get_be64(f); | 
 |             buf = g_try_malloc(count); | 
 |             if (!buf) { | 
 |                 error_report("cmma_load could not allocate memory"); | 
 |                 ret = -ENOMEM; | 
 |                 break; | 
 |             } | 
 |  | 
 |             qemu_get_buffer(f, buf, count); | 
 |             ret = sac->set_stattr(sas, cur_gfn, count, buf); | 
 |             if (ret < 0) { | 
 |                 error_report("Error %d while setting storage attributes", ret); | 
 |             } | 
 |             g_free(buf); | 
 |             break; | 
 |         } | 
 |         case STATTR_FLAG_ERROR: { | 
 |             error_report("Storage attributes data is incomplete"); | 
 |             ret = -EINVAL; | 
 |             break; | 
 |         } | 
 |         case STATTR_FLAG_DONE: | 
 |             /* This is after the last pre-copied value has been sent, nothing | 
 |              * more will be sent after this. Pre-copy has finished, and we | 
 |              * are done flushing all the remaining values. Now the target | 
 |              * system is about to take over. We synchronize the buffer to | 
 |              * apply the actual correct values where needed. | 
 |              */ | 
 |              sac->synchronize(sas); | 
 |             break; | 
 |         case STATTR_FLAG_EOS: | 
 |             /* Normal exit */ | 
 |             return 0; | 
 |         default: | 
 |             error_report("Unexpected storage attribute flag data: %#x", flags); | 
 |             ret = -EINVAL; | 
 |         } | 
 |     } | 
 |  | 
 |     return ret; | 
 | } | 
 |  | 
 | static int cmma_save_setup(QEMUFile *f, void *opaque, Error **errp) | 
 | { | 
 |     S390StAttribState *sas = S390_STATTRIB(opaque); | 
 |     S390StAttribClass *sac = S390_STATTRIB_GET_CLASS(sas); | 
 |     int res; | 
 |     /* | 
 |      * Signal that we want to start a migration, thus needing PGSTE dirty | 
 |      * tracking. | 
 |      */ | 
 |     res = sac->set_migrationmode(sas, true, errp); | 
 |     if (res) { | 
 |         return res; | 
 |     } | 
 |     qemu_put_be64(f, STATTR_FLAG_EOS); | 
 |     return 0; | 
 | } | 
 |  | 
 | static void cmma_state_pending(void *opaque, uint64_t *must_precopy, | 
 |                                uint64_t *can_postcopy) | 
 | { | 
 |     S390StAttribState *sas = S390_STATTRIB(opaque); | 
 |     S390StAttribClass *sac = S390_STATTRIB_GET_CLASS(sas); | 
 |     long long res = sac->get_dirtycount(sas); | 
 |  | 
 |     if (res >= 0) { | 
 |         *must_precopy += res; | 
 |     } | 
 | } | 
 |  | 
 | static int cmma_save(QEMUFile *f, void *opaque, int final) | 
 | { | 
 |     S390StAttribState *sas = S390_STATTRIB(opaque); | 
 |     S390StAttribClass *sac = S390_STATTRIB_GET_CLASS(sas); | 
 |     uint8_t *buf; | 
 |     int r, cx, reallen = 0, ret = 0; | 
 |     uint32_t buflen = CMMA_BLOCK_SIZE; | 
 |     uint64_t start_gfn = sas->migration_cur_gfn; | 
 |  | 
 |     buf = g_try_malloc(buflen); | 
 |     if (!buf) { | 
 |         error_report("Could not allocate memory to save storage attributes"); | 
 |         return -ENOMEM; | 
 |     } | 
 |  | 
 |     while (final ? 1 : migration_rate_exceeded(f) == 0) { | 
 |         reallen = sac->get_stattr(sas, &start_gfn, buflen, buf); | 
 |         if (reallen < 0) { | 
 |             g_free(buf); | 
 |             return reallen; | 
 |         } | 
 |  | 
 |         ret = 1; | 
 |         if (!reallen) { | 
 |             break; | 
 |         } | 
 |         qemu_put_be64(f, (start_gfn << TARGET_PAGE_BITS) | STATTR_FLAG_MORE); | 
 |         qemu_put_be64(f, reallen); | 
 |         for (cx = 0; cx < reallen; cx++) { | 
 |             qemu_put_byte(f, buf[cx]); | 
 |         } | 
 |         if (!sac->get_dirtycount(sas)) { | 
 |             break; | 
 |         } | 
 |     } | 
 |  | 
 |     sas->migration_cur_gfn = start_gfn + reallen; | 
 |     g_free(buf); | 
 |     if (final) { | 
 |         qemu_put_be64(f, STATTR_FLAG_DONE); | 
 |     } | 
 |     qemu_put_be64(f, STATTR_FLAG_EOS); | 
 |  | 
 |     r = qemu_file_get_error(f); | 
 |     if (r < 0) { | 
 |         return r; | 
 |     } | 
 |  | 
 |     return ret; | 
 | } | 
 |  | 
 | static int cmma_save_iterate(QEMUFile *f, void *opaque) | 
 | { | 
 |     return cmma_save(f, opaque, 0); | 
 | } | 
 |  | 
 | static int cmma_save_complete(QEMUFile *f, void *opaque) | 
 | { | 
 |     return cmma_save(f, opaque, 1); | 
 | } | 
 |  | 
 | static void cmma_save_cleanup(void *opaque) | 
 | { | 
 |     S390StAttribState *sas = S390_STATTRIB(opaque); | 
 |     S390StAttribClass *sac = S390_STATTRIB_GET_CLASS(sas); | 
 |     sac->set_migrationmode(sas, false, NULL); | 
 | } | 
 |  | 
 | static bool cmma_active(void *opaque) | 
 | { | 
 |     S390StAttribState *sas = S390_STATTRIB(opaque); | 
 |     S390StAttribClass *sac = S390_STATTRIB_GET_CLASS(sas); | 
 |     return sac->get_active(sas); | 
 | } | 
 |  | 
 | /* QEMU object: */ | 
 |  | 
 | static void qemu_s390_stattrib_instance_init(Object *obj) | 
 | { | 
 | } | 
 |  | 
 | static int qemu_s390_peek_stattr_stub(S390StAttribState *sa, uint64_t start_gfn, | 
 |                                      uint32_t count, uint8_t *values) | 
 | { | 
 |     return 0; | 
 | } | 
 | static void qemu_s390_synchronize_stub(S390StAttribState *sa) | 
 | { | 
 | } | 
 | static int qemu_s390_get_stattr_stub(S390StAttribState *sa, uint64_t *start_gfn, | 
 |                                      uint32_t count, uint8_t *values) | 
 | { | 
 |     return 0; | 
 | } | 
 | static long long qemu_s390_get_dirtycount_stub(S390StAttribState *sa) | 
 | { | 
 |     return 0; | 
 | } | 
 | static int qemu_s390_set_migrationmode_stub(S390StAttribState *sa, bool value, | 
 |                                             Error **errp) | 
 | { | 
 |     return 0; | 
 | } | 
 |  | 
 | static int qemu_s390_get_active(S390StAttribState *sa) | 
 | { | 
 |     return sa->migration_enabled; | 
 | } | 
 |  | 
 | static void qemu_s390_stattrib_class_init(ObjectClass *oc, void *data) | 
 | { | 
 |     S390StAttribClass *sa_cl = S390_STATTRIB_CLASS(oc); | 
 |     DeviceClass *dc = DEVICE_CLASS(oc); | 
 |  | 
 |     sa_cl->synchronize = qemu_s390_synchronize_stub; | 
 |     sa_cl->get_stattr = qemu_s390_get_stattr_stub; | 
 |     sa_cl->set_stattr = qemu_s390_peek_stattr_stub; | 
 |     sa_cl->peek_stattr = qemu_s390_peek_stattr_stub; | 
 |     sa_cl->set_migrationmode = qemu_s390_set_migrationmode_stub; | 
 |     sa_cl->get_dirtycount = qemu_s390_get_dirtycount_stub; | 
 |     sa_cl->get_active = qemu_s390_get_active; | 
 |  | 
 |     /* Reason: Can only be instantiated one time (internally) */ | 
 |     dc->user_creatable = false; | 
 | } | 
 |  | 
 | static const TypeInfo qemu_s390_stattrib_info = { | 
 |     .name          = TYPE_QEMU_S390_STATTRIB, | 
 |     .parent        = TYPE_S390_STATTRIB, | 
 |     .instance_init = qemu_s390_stattrib_instance_init, | 
 |     .instance_size = sizeof(QEMUS390StAttribState), | 
 |     .class_init    = qemu_s390_stattrib_class_init, | 
 |     .class_size    = sizeof(S390StAttribClass), | 
 | }; | 
 |  | 
 | /* Generic abstract object: */ | 
 |  | 
 | static SaveVMHandlers savevm_s390_stattrib_handlers = { | 
 |     .save_setup = cmma_save_setup, | 
 |     .save_live_iterate = cmma_save_iterate, | 
 |     .save_live_complete_precopy = cmma_save_complete, | 
 |     .state_pending_exact = cmma_state_pending, | 
 |     .state_pending_estimate = cmma_state_pending, | 
 |     .save_cleanup = cmma_save_cleanup, | 
 |     .load_state = cmma_load, | 
 |     .is_active = cmma_active, | 
 | }; | 
 |  | 
 | static void s390_stattrib_realize(DeviceState *dev, Error **errp) | 
 | { | 
 |     bool ambiguous = false; | 
 |  | 
 |     object_resolve_path_type("", TYPE_S390_STATTRIB, &ambiguous); | 
 |     if (ambiguous) { | 
 |         error_setg(errp, "storage_attributes device already exists"); | 
 |         return; | 
 |     } | 
 |  | 
 |     register_savevm_live(TYPE_S390_STATTRIB, 0, 0, | 
 |                          &savevm_s390_stattrib_handlers, dev); | 
 | } | 
 |  | 
 | static const Property s390_stattrib_props[] = { | 
 |     DEFINE_PROP_BOOL("migration-enabled", S390StAttribState, migration_enabled, true), | 
 |     DEFINE_PROP_END_OF_LIST(), | 
 | }; | 
 |  | 
 | static void s390_stattrib_class_init(ObjectClass *oc, void *data) | 
 | { | 
 |     DeviceClass *dc = DEVICE_CLASS(oc); | 
 |  | 
 |     dc->hotpluggable = false; | 
 |     set_bit(DEVICE_CATEGORY_MISC, dc->categories); | 
 |     dc->realize = s390_stattrib_realize; | 
 |     device_class_set_props(dc, s390_stattrib_props); | 
 | } | 
 |  | 
 | static void s390_stattrib_instance_init(Object *obj) | 
 | { | 
 |     S390StAttribState *sas = S390_STATTRIB(obj); | 
 |  | 
 |     sas->migration_cur_gfn = 0; | 
 | } | 
 |  | 
 | static const TypeInfo s390_stattrib_info = { | 
 |     .name          = TYPE_S390_STATTRIB, | 
 |     .parent        = TYPE_DEVICE, | 
 |     .instance_init = s390_stattrib_instance_init, | 
 |     .instance_size = sizeof(S390StAttribState), | 
 |     .class_init    = s390_stattrib_class_init, | 
 |     .class_size    = sizeof(S390StAttribClass), | 
 |     .abstract      = true, | 
 | }; | 
 |  | 
 | static void s390_stattrib_register_types(void) | 
 | { | 
 |     type_register_static(&s390_stattrib_info); | 
 |     type_register_static(&qemu_s390_stattrib_info); | 
 | } | 
 |  | 
 | type_init(s390_stattrib_register_types) |