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