xref: /openbmc/qemu/hw/s390x/s390-skeys.c (revision 80748eb4fbc70f0a3ae423f2c01cb5a4584d803f)
10efe406cSJason J. Herne /*
20efe406cSJason J. Herne  * s390 storage key device
30efe406cSJason J. Herne  *
40efe406cSJason J. Herne  * Copyright 2015 IBM Corp.
50efe406cSJason J. Herne  * Author(s): Jason J. Herne <jjherne@linux.vnet.ibm.com>
60efe406cSJason J. Herne  *
70efe406cSJason J. Herne  * This work is licensed under the terms of the GNU GPL, version 2 or (at
80efe406cSJason J. Herne  * your option) any later version. See the COPYING file in the top-level
90efe406cSJason J. Herne  * directory.
100efe406cSJason J. Herne  */
110efe406cSJason J. Herne 
129615495aSPeter Maydell #include "qemu/osdep.h"
13393fc4c7SPhilippe Mathieu-Daudé #include "qemu/units.h"
140efe406cSJason J. Herne #include "hw/boards.h"
152fb40d1bSThomas Huth #include "hw/qdev-properties.h"
160efe406cSJason J. Herne #include "hw/s390x/storage-keys.h"
17e688df6bSMarkus Armbruster #include "qapi/error.h"
18b0227cdbSMarkus Armbruster #include "qapi/qapi-commands-misc-target.h"
19452fcdbcSMarkus Armbruster #include "qapi/qmp/qdict.h"
200efe406cSJason J. Herne #include "qemu/error-report.h"
2167db1306SDavid Hildenbrand #include "sysemu/memory_mapping.h"
222162faf7SDavid Hildenbrand #include "exec/address-spaces.h"
23bd3f16acSPaolo Bonzini #include "sysemu/kvm.h"
24ca77ee28SMarkus Armbruster #include "migration/qemu-file-types.h"
25f2a8f0a6SJuan Quintela #include "migration/register.h"
26*b9268953SPhilippe Mathieu-Daudé #include "trace.h"
270efe406cSJason J. Herne 
28393fc4c7SPhilippe Mathieu-Daudé #define S390_SKEYS_BUFFER_SIZE (128 * KiB)  /* Room for 128k storage keys */
29186208faSJason J. Herne #define S390_SKEYS_SAVE_FLAG_EOS 0x01
30186208faSJason J. Herne #define S390_SKEYS_SAVE_FLAG_SKEYS 0x02
31186208faSJason J. Herne #define S390_SKEYS_SAVE_FLAG_ERROR 0x04
327ee0c3e3SJason J. Herne 
s390_get_skeys_device(void)330efe406cSJason J. Herne S390SKeysState *s390_get_skeys_device(void)
340efe406cSJason J. Herne {
350efe406cSJason J. Herne     S390SKeysState *ss;
360efe406cSJason J. Herne 
370efe406cSJason J. Herne     ss = S390_SKEYS(object_resolve_path_type("", TYPE_S390_SKEYS, NULL));
380efe406cSJason J. Herne     assert(ss);
390efe406cSJason J. Herne     return ss;
400efe406cSJason J. Herne }
410efe406cSJason J. Herne 
s390_skeys_init(void)420efe406cSJason J. Herne void s390_skeys_init(void)
430efe406cSJason J. Herne {
440efe406cSJason J. Herne     Object *obj;
450efe406cSJason J. Herne 
460efe406cSJason J. Herne     if (kvm_enabled()) {
470efe406cSJason J. Herne         obj = object_new(TYPE_KVM_S390_SKEYS);
480efe406cSJason J. Herne     } else {
490efe406cSJason J. Herne         obj = object_new(TYPE_QEMU_S390_SKEYS);
500efe406cSJason J. Herne     }
510efe406cSJason J. Herne     object_property_add_child(qdev_get_machine(), TYPE_S390_SKEYS,
52d2623129SMarkus Armbruster                               obj);
530efe406cSJason J. Herne     object_unref(obj);
540efe406cSJason J. Herne 
55ce189ab2SMarkus Armbruster     qdev_realize(DEVICE(obj), NULL, &error_fatal);
560efe406cSJason J. Herne }
570efe406cSJason J. Herne 
s390_skeys_get(S390SKeysState * ks,uint64_t start_gfn,uint64_t count,uint8_t * keys)58*b9268953SPhilippe Mathieu-Daudé int s390_skeys_get(S390SKeysState *ks, uint64_t start_gfn,
59*b9268953SPhilippe Mathieu-Daudé                    uint64_t count, uint8_t *keys)
60*b9268953SPhilippe Mathieu-Daudé {
61*b9268953SPhilippe Mathieu-Daudé     S390SKeysClass *kc = S390_SKEYS_GET_CLASS(ks);
62*b9268953SPhilippe Mathieu-Daudé     int rc;
63*b9268953SPhilippe Mathieu-Daudé 
64*b9268953SPhilippe Mathieu-Daudé     rc = kc->get_skeys(ks, start_gfn, count, keys);
65*b9268953SPhilippe Mathieu-Daudé     if (rc) {
66*b9268953SPhilippe Mathieu-Daudé         trace_s390_skeys_get_nonzero(rc);
67*b9268953SPhilippe Mathieu-Daudé     }
68*b9268953SPhilippe Mathieu-Daudé     return rc;
69*b9268953SPhilippe Mathieu-Daudé }
70*b9268953SPhilippe Mathieu-Daudé 
s390_skeys_set(S390SKeysState * ks,uint64_t start_gfn,uint64_t count,uint8_t * keys)71*b9268953SPhilippe Mathieu-Daudé int s390_skeys_set(S390SKeysState *ks, uint64_t start_gfn,
72*b9268953SPhilippe Mathieu-Daudé                    uint64_t count, uint8_t *keys)
73*b9268953SPhilippe Mathieu-Daudé {
74*b9268953SPhilippe Mathieu-Daudé     S390SKeysClass *kc = S390_SKEYS_GET_CLASS(ks);
75*b9268953SPhilippe Mathieu-Daudé     int rc;
76*b9268953SPhilippe Mathieu-Daudé 
77*b9268953SPhilippe Mathieu-Daudé     rc = kc->set_skeys(ks, start_gfn, count, keys);
78*b9268953SPhilippe Mathieu-Daudé     if (rc) {
79*b9268953SPhilippe Mathieu-Daudé         trace_s390_skeys_set_nonzero(rc);
80*b9268953SPhilippe Mathieu-Daudé     }
81*b9268953SPhilippe Mathieu-Daudé     return rc;
82*b9268953SPhilippe Mathieu-Daudé }
83*b9268953SPhilippe Mathieu-Daudé 
write_keys(FILE * f,uint8_t * keys,uint64_t startgfn,uint64_t count,Error ** errp)841fd791f0SDaniel P. Berrange static void write_keys(FILE *f, uint8_t *keys, uint64_t startgfn,
857ee0c3e3SJason J. Herne                        uint64_t count, Error **errp)
867ee0c3e3SJason J. Herne {
877ee0c3e3SJason J. Herne     uint64_t curpage = startgfn;
887ee0c3e3SJason J. Herne     uint64_t maxpage = curpage + count - 1;
897ee0c3e3SJason J. Herne 
907ee0c3e3SJason J. Herne     for (; curpage <= maxpage; curpage++) {
917ee0c3e3SJason J. Herne         uint8_t acc = (*keys & 0xF0) >> 4;
927ee0c3e3SJason J. Herne         int fp =  (*keys & 0x08);
937ee0c3e3SJason J. Herne         int ref = (*keys & 0x04);
947ee0c3e3SJason J. Herne         int ch = (*keys & 0x02);
957ee0c3e3SJason J. Herne         int res = (*keys & 0x01);
967ee0c3e3SJason J. Herne 
971fd791f0SDaniel P. Berrange         fprintf(f, "page=%03" PRIx64 ": key(%d) => ACC=%X, FP=%d, REF=%d,"
981fd791f0SDaniel P. Berrange                 " ch=%d, reserved=%d\n",
991fd791f0SDaniel P. Berrange                 curpage, *keys, acc, fp, ref, ch, res);
1007ee0c3e3SJason J. Herne         keys++;
1017ee0c3e3SJason J. Herne     }
1027ee0c3e3SJason J. Herne }
1037ee0c3e3SJason J. Herne 
hmp_info_skeys(Monitor * mon,const QDict * qdict)104a08f0081SJason J. Herne void hmp_info_skeys(Monitor *mon, const QDict *qdict)
105a08f0081SJason J. Herne {
106a08f0081SJason J. Herne     S390SKeysState *ss = s390_get_skeys_device();
107a08f0081SJason J. Herne     S390SKeysClass *skeyclass = S390_SKEYS_GET_CLASS(ss);
108a08f0081SJason J. Herne     uint64_t addr = qdict_get_int(qdict, "addr");
109a08f0081SJason J. Herne     uint8_t key;
110a08f0081SJason J. Herne     int r;
111a08f0081SJason J. Herne 
112a08f0081SJason J. Herne     /* Quick check to see if guest is using storage keys*/
1135227b326SDavid Hildenbrand     if (!skeyclass->skeys_are_enabled(ss)) {
114a08f0081SJason J. Herne         monitor_printf(mon, "Error: This guest is not using storage keys\n");
115a08f0081SJason J. Herne         return;
116a08f0081SJason J. Herne     }
117a08f0081SJason J. Herne 
1182162faf7SDavid Hildenbrand     if (!address_space_access_valid(&address_space_memory,
1192162faf7SDavid Hildenbrand                                     addr & TARGET_PAGE_MASK, TARGET_PAGE_SIZE,
1202162faf7SDavid Hildenbrand                                     false, MEMTXATTRS_UNSPECIFIED)) {
1212162faf7SDavid Hildenbrand         monitor_printf(mon, "Error: The given address is not valid\n");
1222162faf7SDavid Hildenbrand         return;
1232162faf7SDavid Hildenbrand     }
1242162faf7SDavid Hildenbrand 
125a08f0081SJason J. Herne     r = skeyclass->get_skeys(ss, addr / TARGET_PAGE_SIZE, 1, &key);
126a08f0081SJason J. Herne     if (r < 0) {
127a08f0081SJason J. Herne         monitor_printf(mon, "Error: %s\n", strerror(-r));
128a08f0081SJason J. Herne         return;
129a08f0081SJason J. Herne     }
130a08f0081SJason J. Herne 
131a08f0081SJason J. Herne     monitor_printf(mon, "  key: 0x%X\n", key);
132a08f0081SJason J. Herne }
133a08f0081SJason J. Herne 
hmp_dump_skeys(Monitor * mon,const QDict * qdict)134a4538a5cSJason J. Herne void hmp_dump_skeys(Monitor *mon, const QDict *qdict)
135a4538a5cSJason J. Herne {
136a4538a5cSJason J. Herne     const char *filename = qdict_get_str(qdict, "filename");
137a4538a5cSJason J. Herne     Error *err = NULL;
138a4538a5cSJason J. Herne 
139a4538a5cSJason J. Herne     qmp_dump_skeys(filename, &err);
140a4538a5cSJason J. Herne     if (err) {
141193227f9SMarkus Armbruster         error_report_err(err);
142a4538a5cSJason J. Herne     }
143a4538a5cSJason J. Herne }
144a4538a5cSJason J. Herne 
qmp_dump_skeys(const char * filename,Error ** errp)1457ee0c3e3SJason J. Herne void qmp_dump_skeys(const char *filename, Error **errp)
1467ee0c3e3SJason J. Herne {
1477ee0c3e3SJason J. Herne     S390SKeysState *ss = s390_get_skeys_device();
1487ee0c3e3SJason J. Herne     S390SKeysClass *skeyclass = S390_SKEYS_GET_CLASS(ss);
14978eedc60SDavid Hildenbrand     GuestPhysBlockList guest_phys_blocks;
15078eedc60SDavid Hildenbrand     GuestPhysBlock *block;
15178eedc60SDavid Hildenbrand     uint64_t pages, gfn;
1527ee0c3e3SJason J. Herne     Error *lerr = NULL;
1537ee0c3e3SJason J. Herne     uint8_t *buf;
1547ee0c3e3SJason J. Herne     int ret;
1551fd791f0SDaniel P. Berrange     int fd;
1561fd791f0SDaniel P. Berrange     FILE *f;
1577ee0c3e3SJason J. Herne 
1587ee0c3e3SJason J. Herne     /* Quick check to see if guest is using storage keys*/
1595227b326SDavid Hildenbrand     if (!skeyclass->skeys_are_enabled(ss)) {
1607ee0c3e3SJason J. Herne         error_setg(errp, "This guest is not using storage keys - "
1617ee0c3e3SJason J. Herne                          "nothing to dump");
1627ee0c3e3SJason J. Herne         return;
1637ee0c3e3SJason J. Herne     }
1647ee0c3e3SJason J. Herne 
165448058aaSDaniel P. Berrangé     fd = qemu_open_old(filename, O_WRONLY | O_CREAT | O_TRUNC, 0600);
1661fd791f0SDaniel P. Berrange     if (fd < 0) {
1671fd791f0SDaniel P. Berrange         error_setg_file_open(errp, errno, filename);
1681fd791f0SDaniel P. Berrange         return;
1691fd791f0SDaniel P. Berrange     }
1701fd791f0SDaniel P. Berrange     f = fdopen(fd, "wb");
1717ee0c3e3SJason J. Herne     if (!f) {
1721fd791f0SDaniel P. Berrange         close(fd);
1737ee0c3e3SJason J. Herne         error_setg_file_open(errp, errno, filename);
1747ee0c3e3SJason J. Herne         return;
1757ee0c3e3SJason J. Herne     }
1767ee0c3e3SJason J. Herne 
1777ee0c3e3SJason J. Herne     buf = g_try_malloc(S390_SKEYS_BUFFER_SIZE);
1787ee0c3e3SJason J. Herne     if (!buf) {
1797ee0c3e3SJason J. Herne         error_setg(errp, "Could not allocate memory");
1807ee0c3e3SJason J. Herne         goto out;
1817ee0c3e3SJason J. Herne     }
1827ee0c3e3SJason J. Herne 
183195801d7SStefan Hajnoczi     assert(bql_locked());
18478eedc60SDavid Hildenbrand     guest_phys_blocks_init(&guest_phys_blocks);
18578eedc60SDavid Hildenbrand     guest_phys_blocks_append(&guest_phys_blocks);
1867ee0c3e3SJason J. Herne 
18778eedc60SDavid Hildenbrand     QTAILQ_FOREACH(block, &guest_phys_blocks.head, next) {
18878eedc60SDavid Hildenbrand         assert(QEMU_IS_ALIGNED(block->target_start, TARGET_PAGE_SIZE));
18978eedc60SDavid Hildenbrand         assert(QEMU_IS_ALIGNED(block->target_end, TARGET_PAGE_SIZE));
19078eedc60SDavid Hildenbrand 
19178eedc60SDavid Hildenbrand         gfn = block->target_start / TARGET_PAGE_SIZE;
19278eedc60SDavid Hildenbrand         pages = (block->target_end - block->target_start) / TARGET_PAGE_SIZE;
19378eedc60SDavid Hildenbrand 
19478eedc60SDavid Hildenbrand         while (pages) {
19578eedc60SDavid Hildenbrand             const uint64_t cur_pages = MIN(pages, S390_SKEYS_BUFFER_SIZE);
19678eedc60SDavid Hildenbrand 
19778eedc60SDavid Hildenbrand             ret = skeyclass->get_skeys(ss, gfn, cur_pages, buf);
1987ee0c3e3SJason J. Herne             if (ret < 0) {
19978eedc60SDavid Hildenbrand                 error_setg_errno(errp, -ret, "get_keys error");
2007ee0c3e3SJason J. Herne                 goto out_free;
2017ee0c3e3SJason J. Herne             }
2027ee0c3e3SJason J. Herne 
2037ee0c3e3SJason J. Herne             /* write keys to stream */
20478eedc60SDavid Hildenbrand             write_keys(f, buf, gfn, cur_pages, &lerr);
2057ee0c3e3SJason J. Herne             if (lerr) {
2067ee0c3e3SJason J. Herne                 goto out_free;
2077ee0c3e3SJason J. Herne             }
2087ee0c3e3SJason J. Herne 
20978eedc60SDavid Hildenbrand             gfn += cur_pages;
21078eedc60SDavid Hildenbrand             pages -= cur_pages;
21178eedc60SDavid Hildenbrand         }
2127ee0c3e3SJason J. Herne     }
2137ee0c3e3SJason J. Herne 
2147ee0c3e3SJason J. Herne out_free:
21578eedc60SDavid Hildenbrand     guest_phys_blocks_free(&guest_phys_blocks);
2167ee0c3e3SJason J. Herne     error_propagate(errp, lerr);
2177ee0c3e3SJason J. Herne     g_free(buf);
2187ee0c3e3SJason J. Herne out:
2191fd791f0SDaniel P. Berrange     fclose(f);
2207ee0c3e3SJason J. Herne }
2217ee0c3e3SJason J. Herne 
qemu_s390_skeys_are_enabled(S390SKeysState * ss)222c3562238SDavid Hildenbrand static bool qemu_s390_skeys_are_enabled(S390SKeysState *ss)
2230efe406cSJason J. Herne {
224c3562238SDavid Hildenbrand     QEMUS390SKeysState *skeys = QEMU_S390_SKEYS(ss);
225c3562238SDavid Hildenbrand 
226c3562238SDavid Hildenbrand     /* Lockless check is sufficient. */
227c3562238SDavid Hildenbrand     return !!skeys->keydata;
228c3562238SDavid Hildenbrand }
229c3562238SDavid Hildenbrand 
qemu_s390_enable_skeys(S390SKeysState * ss)230c3562238SDavid Hildenbrand static bool qemu_s390_enable_skeys(S390SKeysState *ss)
231c3562238SDavid Hildenbrand {
232c3562238SDavid Hildenbrand     QEMUS390SKeysState *skeys = QEMU_S390_SKEYS(ss);
233c3562238SDavid Hildenbrand     static gsize initialized;
234c3562238SDavid Hildenbrand 
235c3562238SDavid Hildenbrand     if (likely(skeys->keydata)) {
236c3562238SDavid Hildenbrand         return true;
237c3562238SDavid Hildenbrand     }
238c3562238SDavid Hildenbrand 
239c3562238SDavid Hildenbrand     /*
240c3562238SDavid Hildenbrand      * TODO: Modern Linux doesn't use storage keys unless running KVM guests
241c3562238SDavid Hildenbrand      *       that use storage keys. Therefore, we keep it simple for now.
242c3562238SDavid Hildenbrand      *
243c3562238SDavid Hildenbrand      * 1) We should initialize to "referenced+changed" for an initial
244c3562238SDavid Hildenbrand      *    over-indication. Let's avoid touching megabytes of data for now and
245c3562238SDavid Hildenbrand      *    assume that any sane user will issue a storage key instruction before
246c3562238SDavid Hildenbrand      *    actually relying on this data.
247c3562238SDavid Hildenbrand      * 2) Relying on ram_size and allocating a big array is ugly. We should
248c3562238SDavid Hildenbrand      *    allocate and manage storage key data per RAMBlock or optimally using
249c3562238SDavid Hildenbrand      *    some sparse data structure.
250c3562238SDavid Hildenbrand      * 3) We only ever have a single S390SKeysState, so relying on
251c3562238SDavid Hildenbrand      *    g_once_init_enter() is good enough.
252c3562238SDavid Hildenbrand      */
253c3562238SDavid Hildenbrand     if (g_once_init_enter(&initialized)) {
2540efe406cSJason J. Herne         MachineState *machine = MACHINE(qdev_get_machine());
2550efe406cSJason J. Herne 
2565c30ef93SChristian Borntraeger         skeys->key_count = machine->ram_size / TARGET_PAGE_SIZE;
2570efe406cSJason J. Herne         skeys->keydata = g_malloc0(skeys->key_count);
258c3562238SDavid Hildenbrand         g_once_init_leave(&initialized, 1);
2590efe406cSJason J. Herne     }
260c3562238SDavid Hildenbrand     return false;
2610efe406cSJason J. Herne }
2620efe406cSJason J. Herne 
qemu_s390_skeys_set(S390SKeysState * ss,uint64_t start_gfn,uint64_t count,uint8_t * keys)2630efe406cSJason J. Herne static int qemu_s390_skeys_set(S390SKeysState *ss, uint64_t start_gfn,
2640efe406cSJason J. Herne                               uint64_t count, uint8_t *keys)
2650efe406cSJason J. Herne {
2660efe406cSJason J. Herne     QEMUS390SKeysState *skeydev = QEMU_S390_SKEYS(ss);
2670efe406cSJason J. Herne     int i;
2680efe406cSJason J. Herne 
2690efe406cSJason J. Herne     /* Check for uint64 overflow and access beyond end of key data */
270c3562238SDavid Hildenbrand     if (unlikely(!skeydev->keydata || start_gfn + count > skeydev->key_count ||
271c3562238SDavid Hildenbrand                   start_gfn + count < count)) {
272c3562238SDavid Hildenbrand         error_report("Error: Setting storage keys for pages with unallocated "
273c3562238SDavid Hildenbrand                      "storage key memory: gfn=%" PRIx64 " count=%" PRId64,
2749af9e0feSMarkus Armbruster                      start_gfn, count);
2750efe406cSJason J. Herne         return -EINVAL;
2760efe406cSJason J. Herne     }
2770efe406cSJason J. Herne 
2780efe406cSJason J. Herne     for (i = 0; i < count; i++) {
2790efe406cSJason J. Herne         skeydev->keydata[start_gfn + i] = keys[i];
2800efe406cSJason J. Herne     }
2810efe406cSJason J. Herne     return 0;
2820efe406cSJason J. Herne }
2830efe406cSJason J. Herne 
qemu_s390_skeys_get(S390SKeysState * ss,uint64_t start_gfn,uint64_t count,uint8_t * keys)2840efe406cSJason J. Herne static int qemu_s390_skeys_get(S390SKeysState *ss, uint64_t start_gfn,
2850efe406cSJason J. Herne                                uint64_t count, uint8_t *keys)
2860efe406cSJason J. Herne {
2870efe406cSJason J. Herne     QEMUS390SKeysState *skeydev = QEMU_S390_SKEYS(ss);
2880efe406cSJason J. Herne     int i;
2890efe406cSJason J. Herne 
2900efe406cSJason J. Herne     /* Check for uint64 overflow and access beyond end of key data */
291c3562238SDavid Hildenbrand     if (unlikely(!skeydev->keydata || start_gfn + count > skeydev->key_count ||
292c3562238SDavid Hildenbrand                   start_gfn + count < count)) {
293c3562238SDavid Hildenbrand         error_report("Error: Getting storage keys for pages with unallocated "
294c3562238SDavid Hildenbrand                      "storage key memory: gfn=%" PRIx64 " count=%" PRId64,
2959af9e0feSMarkus Armbruster                      start_gfn, count);
2960efe406cSJason J. Herne         return -EINVAL;
2970efe406cSJason J. Herne     }
2980efe406cSJason J. Herne 
2990efe406cSJason J. Herne     for (i = 0; i < count; i++) {
3000efe406cSJason J. Herne         keys[i] = skeydev->keydata[start_gfn + i];
3010efe406cSJason J. Herne     }
3020efe406cSJason J. Herne     return 0;
3030efe406cSJason J. Herne }
3040efe406cSJason J. Herne 
qemu_s390_skeys_class_init(ObjectClass * oc,void * data)3050efe406cSJason J. Herne static void qemu_s390_skeys_class_init(ObjectClass *oc, void *data)
3060efe406cSJason J. Herne {
3070efe406cSJason J. Herne     S390SKeysClass *skeyclass = S390_SKEYS_CLASS(oc);
308574ee06dSThomas Huth     DeviceClass *dc = DEVICE_CLASS(oc);
3090efe406cSJason J. Herne 
3105227b326SDavid Hildenbrand     skeyclass->skeys_are_enabled = qemu_s390_skeys_are_enabled;
311c3562238SDavid Hildenbrand     skeyclass->enable_skeys = qemu_s390_enable_skeys;
3120efe406cSJason J. Herne     skeyclass->get_skeys = qemu_s390_skeys_get;
3130efe406cSJason J. Herne     skeyclass->set_skeys = qemu_s390_skeys_set;
314574ee06dSThomas Huth 
315574ee06dSThomas Huth     /* Reason: Internal device (only one skeys device for the whole memory) */
316574ee06dSThomas Huth     dc->user_creatable = false;
3170efe406cSJason J. Herne }
3180efe406cSJason J. Herne 
3190efe406cSJason J. Herne static const TypeInfo qemu_s390_skeys_info = {
3200efe406cSJason J. Herne     .name          = TYPE_QEMU_S390_SKEYS,
3210efe406cSJason J. Herne     .parent        = TYPE_S390_SKEYS,
3220efe406cSJason J. Herne     .instance_size = sizeof(QEMUS390SKeysState),
3230efe406cSJason J. Herne     .class_init    = qemu_s390_skeys_class_init,
3242f2b0c66SChristian Borntraeger     .class_size    = sizeof(S390SKeysClass),
3250efe406cSJason J. Herne };
3260efe406cSJason J. Herne 
s390_storage_keys_save(QEMUFile * f,void * opaque)327186208faSJason J. Herne static void s390_storage_keys_save(QEMUFile *f, void *opaque)
328186208faSJason J. Herne {
329186208faSJason J. Herne     S390SKeysState *ss = S390_SKEYS(opaque);
330186208faSJason J. Herne     S390SKeysClass *skeyclass = S390_SKEYS_GET_CLASS(ss);
33167db1306SDavid Hildenbrand     GuestPhysBlockList guest_phys_blocks;
33267db1306SDavid Hildenbrand     GuestPhysBlock *block;
33367db1306SDavid Hildenbrand     uint64_t pages, gfn;
334186208faSJason J. Herne     int error = 0;
335186208faSJason J. Herne     uint8_t *buf;
336186208faSJason J. Herne 
3375227b326SDavid Hildenbrand     if (!skeyclass->skeys_are_enabled(ss)) {
338186208faSJason J. Herne         goto end_stream;
339186208faSJason J. Herne     }
340186208faSJason J. Herne 
341186208faSJason J. Herne     buf = g_try_malloc(S390_SKEYS_BUFFER_SIZE);
342186208faSJason J. Herne     if (!buf) {
3439af9e0feSMarkus Armbruster         error_report("storage key save could not allocate memory");
344186208faSJason J. Herne         goto end_stream;
345186208faSJason J. Herne     }
346186208faSJason J. Herne 
34767db1306SDavid Hildenbrand     guest_phys_blocks_init(&guest_phys_blocks);
34867db1306SDavid Hildenbrand     guest_phys_blocks_append(&guest_phys_blocks);
349186208faSJason J. Herne 
35067db1306SDavid Hildenbrand     /* Send each contiguous physical memory range separately. */
35167db1306SDavid Hildenbrand     QTAILQ_FOREACH(block, &guest_phys_blocks.head, next) {
35267db1306SDavid Hildenbrand         assert(QEMU_IS_ALIGNED(block->target_start, TARGET_PAGE_SIZE));
35367db1306SDavid Hildenbrand         assert(QEMU_IS_ALIGNED(block->target_end, TARGET_PAGE_SIZE));
35467db1306SDavid Hildenbrand 
35567db1306SDavid Hildenbrand         gfn = block->target_start / TARGET_PAGE_SIZE;
35667db1306SDavid Hildenbrand         pages = (block->target_end - block->target_start) / TARGET_PAGE_SIZE;
35767db1306SDavid Hildenbrand         qemu_put_be64(f, block->target_start | S390_SKEYS_SAVE_FLAG_SKEYS);
35867db1306SDavid Hildenbrand         qemu_put_be64(f, pages);
35967db1306SDavid Hildenbrand 
36067db1306SDavid Hildenbrand         while (pages) {
36167db1306SDavid Hildenbrand             const uint64_t cur_pages = MIN(pages, S390_SKEYS_BUFFER_SIZE);
362186208faSJason J. Herne 
363186208faSJason J. Herne             if (!error) {
36467db1306SDavid Hildenbrand                 error = skeyclass->get_skeys(ss, gfn, cur_pages, buf);
365186208faSJason J. Herne                 if (error) {
366186208faSJason J. Herne                     /*
36767db1306SDavid Hildenbrand                      * Create a valid stream with all 0x00 and indicate
36867db1306SDavid Hildenbrand                      * S390_SKEYS_SAVE_FLAG_ERROR to the destination.
369186208faSJason J. Herne                      */
3709af9e0feSMarkus Armbruster                     error_report("S390_GET_KEYS error %d", error);
371186208faSJason J. Herne                     memset(buf, 0, S390_SKEYS_BUFFER_SIZE);
372186208faSJason J. Herne                 }
373186208faSJason J. Herne             }
374186208faSJason J. Herne 
37567db1306SDavid Hildenbrand             qemu_put_buffer(f, buf, cur_pages);
37667db1306SDavid Hildenbrand             gfn += cur_pages;
37767db1306SDavid Hildenbrand             pages -= cur_pages;
378186208faSJason J. Herne         }
379186208faSJason J. Herne 
38067db1306SDavid Hildenbrand         if (error) {
38167db1306SDavid Hildenbrand             break;
38267db1306SDavid Hildenbrand         }
38367db1306SDavid Hildenbrand     }
38467db1306SDavid Hildenbrand 
38567db1306SDavid Hildenbrand     guest_phys_blocks_free(&guest_phys_blocks);
386186208faSJason J. Herne     g_free(buf);
387186208faSJason J. Herne end_stream:
38867db1306SDavid Hildenbrand     if (error) {
38967db1306SDavid Hildenbrand         qemu_put_be64(f, S390_SKEYS_SAVE_FLAG_ERROR);
39067db1306SDavid Hildenbrand     } else {
39167db1306SDavid Hildenbrand         qemu_put_be64(f, S390_SKEYS_SAVE_FLAG_EOS);
39267db1306SDavid Hildenbrand     }
393186208faSJason J. Herne }
394186208faSJason J. Herne 
s390_storage_keys_load(QEMUFile * f,void * opaque,int version_id)395186208faSJason J. Herne static int s390_storage_keys_load(QEMUFile *f, void *opaque, int version_id)
396186208faSJason J. Herne {
397186208faSJason J. Herne     S390SKeysState *ss = S390_SKEYS(opaque);
398186208faSJason J. Herne     S390SKeysClass *skeyclass = S390_SKEYS_GET_CLASS(ss);
399186208faSJason J. Herne     int ret = 0;
400186208faSJason J. Herne 
401c3562238SDavid Hildenbrand     /*
402c3562238SDavid Hildenbrand      * Make sure to lazy-enable if required to be done explicitly. No need to
403c3562238SDavid Hildenbrand      * flush any TLB as the VM is not running yet.
404c3562238SDavid Hildenbrand      */
405c3562238SDavid Hildenbrand     if (skeyclass->enable_skeys) {
406c3562238SDavid Hildenbrand         skeyclass->enable_skeys(ss);
407c3562238SDavid Hildenbrand     }
408c3562238SDavid Hildenbrand 
409186208faSJason J. Herne     while (!ret) {
410186208faSJason J. Herne         ram_addr_t addr;
411186208faSJason J. Herne         int flags;
412186208faSJason J. Herne 
413186208faSJason J. Herne         addr = qemu_get_be64(f);
414186208faSJason J. Herne         flags = addr & ~TARGET_PAGE_MASK;
415186208faSJason J. Herne         addr &= TARGET_PAGE_MASK;
416186208faSJason J. Herne 
417186208faSJason J. Herne         switch (flags) {
418186208faSJason J. Herne         case S390_SKEYS_SAVE_FLAG_SKEYS: {
419186208faSJason J. Herne             const uint64_t total_count = qemu_get_be64(f);
420186208faSJason J. Herne             uint64_t handled_count = 0, cur_count;
421186208faSJason J. Herne             uint64_t cur_gfn = addr / TARGET_PAGE_SIZE;
422186208faSJason J. Herne             uint8_t *buf = g_try_malloc(S390_SKEYS_BUFFER_SIZE);
423186208faSJason J. Herne 
424186208faSJason J. Herne             if (!buf) {
4259af9e0feSMarkus Armbruster                 error_report("storage key load could not allocate memory");
426186208faSJason J. Herne                 ret = -ENOMEM;
427186208faSJason J. Herne                 break;
428186208faSJason J. Herne             }
429186208faSJason J. Herne 
430186208faSJason J. Herne             while (handled_count < total_count) {
431186208faSJason J. Herne                 cur_count = MIN(total_count - handled_count,
432186208faSJason J. Herne                                 S390_SKEYS_BUFFER_SIZE);
433186208faSJason J. Herne                 qemu_get_buffer(f, buf, cur_count);
434186208faSJason J. Herne 
435186208faSJason J. Herne                 ret = skeyclass->set_skeys(ss, cur_gfn, cur_count, buf);
436186208faSJason J. Herne                 if (ret < 0) {
4379af9e0feSMarkus Armbruster                     error_report("S390_SET_KEYS error %d", ret);
438186208faSJason J. Herne                     break;
439186208faSJason J. Herne                 }
440186208faSJason J. Herne                 handled_count += cur_count;
441186208faSJason J. Herne                 cur_gfn += cur_count;
442186208faSJason J. Herne             }
443186208faSJason J. Herne             g_free(buf);
444186208faSJason J. Herne             break;
445186208faSJason J. Herne         }
446186208faSJason J. Herne         case S390_SKEYS_SAVE_FLAG_ERROR: {
447186208faSJason J. Herne             error_report("Storage key data is incomplete");
448186208faSJason J. Herne             ret = -EINVAL;
449186208faSJason J. Herne             break;
450186208faSJason J. Herne         }
451186208faSJason J. Herne         case S390_SKEYS_SAVE_FLAG_EOS:
452186208faSJason J. Herne             /* normal exit */
453186208faSJason J. Herne             return 0;
454186208faSJason J. Herne         default:
455186208faSJason J. Herne             error_report("Unexpected storage key flag data: %#x", flags);
456186208faSJason J. Herne             ret = -EINVAL;
457186208faSJason J. Herne         }
458186208faSJason J. Herne     }
459186208faSJason J. Herne 
460186208faSJason J. Herne     return ret;
461186208faSJason J. Herne }
462186208faSJason J. Herne 
4631b6e7482SLaurent Vivier static SaveVMHandlers savevm_s390_storage_keys = {
4641b6e7482SLaurent Vivier     .save_state = s390_storage_keys_save,
4651b6e7482SLaurent Vivier     .load_state = s390_storage_keys_load,
4661b6e7482SLaurent Vivier };
4671b6e7482SLaurent Vivier 
s390_skeys_realize(DeviceState * dev,Error ** errp)4682fb40d1bSThomas Huth static void s390_skeys_realize(DeviceState *dev, Error **errp)
4699ef40173SJason J. Herne {
4702fb40d1bSThomas Huth     S390SKeysState *ss = S390_SKEYS(dev);
4719ef40173SJason J. Herne 
4729ef40173SJason J. Herne     if (ss->migration_enabled) {
473ce62df53SDr. David Alan Gilbert         register_savevm_live(TYPE_S390_SKEYS, 0, 1,
4741b6e7482SLaurent Vivier                              &savevm_s390_storage_keys, ss);
4759ef40173SJason J. Herne     }
4769ef40173SJason J. Herne }
4779ef40173SJason J. Herne 
4782fb40d1bSThomas Huth static Property s390_skeys_props[] = {
4792fb40d1bSThomas Huth     DEFINE_PROP_BOOL("migration-enabled", S390SKeysState, migration_enabled, true),
4802fb40d1bSThomas Huth     DEFINE_PROP_END_OF_LIST(),
4812fb40d1bSThomas Huth };
482186208faSJason J. Herne 
s390_skeys_class_init(ObjectClass * oc,void * data)4830efe406cSJason J. Herne static void s390_skeys_class_init(ObjectClass *oc, void *data)
4840efe406cSJason J. Herne {
4850efe406cSJason J. Herne     DeviceClass *dc = DEVICE_CLASS(oc);
4860efe406cSJason J. Herne 
4870efe406cSJason J. Herne     dc->hotpluggable = false;
4882fb40d1bSThomas Huth     dc->realize = s390_skeys_realize;
4892fb40d1bSThomas Huth     device_class_set_props(dc, s390_skeys_props);
4900efe406cSJason J. Herne     set_bit(DEVICE_CATEGORY_MISC, dc->categories);
4910efe406cSJason J. Herne }
4920efe406cSJason J. Herne 
4930efe406cSJason J. Herne static const TypeInfo s390_skeys_info = {
4940efe406cSJason J. Herne     .name          = TYPE_S390_SKEYS,
4950efe406cSJason J. Herne     .parent        = TYPE_DEVICE,
4960efe406cSJason J. Herne     .instance_size = sizeof(S390SKeysState),
4970efe406cSJason J. Herne     .class_init    = s390_skeys_class_init,
4980efe406cSJason J. Herne     .class_size    = sizeof(S390SKeysClass),
4990efe406cSJason J. Herne     .abstract = true,
5000efe406cSJason J. Herne };
5010efe406cSJason J. Herne 
qemu_s390_skeys_register_types(void)5020efe406cSJason J. Herne static void qemu_s390_skeys_register_types(void)
5030efe406cSJason J. Herne {
5040efe406cSJason J. Herne     type_register_static(&s390_skeys_info);
5050efe406cSJason J. Herne     type_register_static(&qemu_s390_skeys_info);
5060efe406cSJason J. Herne }
5070efe406cSJason J. Herne 
5080efe406cSJason J. Herne type_init(qemu_s390_skeys_register_types)
509