hw/s390x/s390-skeys: use memory mapping to detect which storage keys to migrate
[qemu/kevin.git] / hw / s390x / s390-skeys.c
blob250685a95a4ebab87a6160f29fb8189173eebd09
1 /*
2 * s390 storage key device
4 * Copyright 2015 IBM Corp.
5 * Author(s): Jason J. Herne <jjherne@linux.vnet.ibm.com>
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.
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 "sysemu/kvm.h"
22 #include "migration/qemu-file-types.h"
23 #include "migration/register.h"
25 #define S390_SKEYS_BUFFER_SIZE (128 * KiB) /* Room for 128k storage keys */
26 #define S390_SKEYS_SAVE_FLAG_EOS 0x01
27 #define S390_SKEYS_SAVE_FLAG_SKEYS 0x02
28 #define S390_SKEYS_SAVE_FLAG_ERROR 0x04
30 S390SKeysState *s390_get_skeys_device(void)
32 S390SKeysState *ss;
34 ss = S390_SKEYS(object_resolve_path_type("", TYPE_S390_SKEYS, NULL));
35 assert(ss);
36 return ss;
39 void s390_skeys_init(void)
41 Object *obj;
43 if (kvm_enabled()) {
44 obj = object_new(TYPE_KVM_S390_SKEYS);
45 } else {
46 obj = object_new(TYPE_QEMU_S390_SKEYS);
48 object_property_add_child(qdev_get_machine(), TYPE_S390_SKEYS,
49 obj);
50 object_unref(obj);
52 qdev_realize(DEVICE(obj), NULL, &error_fatal);
55 static void write_keys(FILE *f, uint8_t *keys, uint64_t startgfn,
56 uint64_t count, Error **errp)
58 uint64_t curpage = startgfn;
59 uint64_t maxpage = curpage + count - 1;
61 for (; curpage <= maxpage; curpage++) {
62 uint8_t acc = (*keys & 0xF0) >> 4;
63 int fp = (*keys & 0x08);
64 int ref = (*keys & 0x04);
65 int ch = (*keys & 0x02);
66 int res = (*keys & 0x01);
68 fprintf(f, "page=%03" PRIx64 ": key(%d) => ACC=%X, FP=%d, REF=%d,"
69 " ch=%d, reserved=%d\n",
70 curpage, *keys, acc, fp, ref, ch, res);
71 keys++;
75 void hmp_info_skeys(Monitor *mon, const QDict *qdict)
77 S390SKeysState *ss = s390_get_skeys_device();
78 S390SKeysClass *skeyclass = S390_SKEYS_GET_CLASS(ss);
79 uint64_t addr = qdict_get_int(qdict, "addr");
80 uint8_t key;
81 int r;
83 /* Quick check to see if guest is using storage keys*/
84 if (!skeyclass->skeys_enabled(ss)) {
85 monitor_printf(mon, "Error: This guest is not using storage keys\n");
86 return;
89 r = skeyclass->get_skeys(ss, addr / TARGET_PAGE_SIZE, 1, &key);
90 if (r < 0) {
91 monitor_printf(mon, "Error: %s\n", strerror(-r));
92 return;
95 monitor_printf(mon, " key: 0x%X\n", key);
98 void hmp_dump_skeys(Monitor *mon, const QDict *qdict)
100 const char *filename = qdict_get_str(qdict, "filename");
101 Error *err = NULL;
103 qmp_dump_skeys(filename, &err);
104 if (err) {
105 error_report_err(err);
109 void qmp_dump_skeys(const char *filename, Error **errp)
111 S390SKeysState *ss = s390_get_skeys_device();
112 S390SKeysClass *skeyclass = S390_SKEYS_GET_CLASS(ss);
113 MachineState *ms = MACHINE(qdev_get_machine());
114 const uint64_t total_count = ms->ram_size / TARGET_PAGE_SIZE;
115 uint64_t handled_count = 0, cur_count;
116 Error *lerr = NULL;
117 vaddr cur_gfn = 0;
118 uint8_t *buf;
119 int ret;
120 int fd;
121 FILE *f;
123 /* Quick check to see if guest is using storage keys*/
124 if (!skeyclass->skeys_enabled(ss)) {
125 error_setg(errp, "This guest is not using storage keys - "
126 "nothing to dump");
127 return;
130 fd = qemu_open_old(filename, O_WRONLY | O_CREAT | O_TRUNC, 0600);
131 if (fd < 0) {
132 error_setg_file_open(errp, errno, filename);
133 return;
135 f = fdopen(fd, "wb");
136 if (!f) {
137 close(fd);
138 error_setg_file_open(errp, errno, filename);
139 return;
142 buf = g_try_malloc(S390_SKEYS_BUFFER_SIZE);
143 if (!buf) {
144 error_setg(errp, "Could not allocate memory");
145 goto out;
148 /* we'll only dump initial memory for now */
149 while (handled_count < total_count) {
150 /* Calculate how many keys to ask for & handle overflow case */
151 cur_count = MIN(total_count - handled_count, S390_SKEYS_BUFFER_SIZE);
153 ret = skeyclass->get_skeys(ss, cur_gfn, cur_count, buf);
154 if (ret < 0) {
155 error_setg(errp, "get_keys error %d", ret);
156 goto out_free;
159 /* write keys to stream */
160 write_keys(f, buf, cur_gfn, cur_count, &lerr);
161 if (lerr) {
162 goto out_free;
165 cur_gfn += cur_count;
166 handled_count += cur_count;
169 out_free:
170 error_propagate(errp, lerr);
171 g_free(buf);
172 out:
173 fclose(f);
176 static void qemu_s390_skeys_init(Object *obj)
178 QEMUS390SKeysState *skeys = QEMU_S390_SKEYS(obj);
179 MachineState *machine = MACHINE(qdev_get_machine());
181 skeys->key_count = machine->ram_size / TARGET_PAGE_SIZE;
182 skeys->keydata = g_malloc0(skeys->key_count);
185 static int qemu_s390_skeys_enabled(S390SKeysState *ss)
187 return 1;
191 * TODO: for memory hotplug support qemu_s390_skeys_set and qemu_s390_skeys_get
192 * will have to make sure that the given gfn belongs to a memory region and not
193 * a memory hole.
195 static int qemu_s390_skeys_set(S390SKeysState *ss, uint64_t start_gfn,
196 uint64_t count, uint8_t *keys)
198 QEMUS390SKeysState *skeydev = QEMU_S390_SKEYS(ss);
199 int i;
201 /* Check for uint64 overflow and access beyond end of key data */
202 if (start_gfn + count > skeydev->key_count || start_gfn + count < count) {
203 error_report("Error: Setting storage keys for page beyond the end "
204 "of memory: gfn=%" PRIx64 " count=%" PRId64,
205 start_gfn, count);
206 return -EINVAL;
209 for (i = 0; i < count; i++) {
210 skeydev->keydata[start_gfn + i] = keys[i];
212 return 0;
215 static int qemu_s390_skeys_get(S390SKeysState *ss, uint64_t start_gfn,
216 uint64_t count, uint8_t *keys)
218 QEMUS390SKeysState *skeydev = QEMU_S390_SKEYS(ss);
219 int i;
221 /* Check for uint64 overflow and access beyond end of key data */
222 if (start_gfn + count > skeydev->key_count || start_gfn + count < count) {
223 error_report("Error: Getting storage keys for page beyond the end "
224 "of memory: gfn=%" PRIx64 " count=%" PRId64,
225 start_gfn, count);
226 return -EINVAL;
229 for (i = 0; i < count; i++) {
230 keys[i] = skeydev->keydata[start_gfn + i];
232 return 0;
235 static void qemu_s390_skeys_class_init(ObjectClass *oc, void *data)
237 S390SKeysClass *skeyclass = S390_SKEYS_CLASS(oc);
238 DeviceClass *dc = DEVICE_CLASS(oc);
240 skeyclass->skeys_enabled = qemu_s390_skeys_enabled;
241 skeyclass->get_skeys = qemu_s390_skeys_get;
242 skeyclass->set_skeys = qemu_s390_skeys_set;
244 /* Reason: Internal device (only one skeys device for the whole memory) */
245 dc->user_creatable = false;
248 static const TypeInfo qemu_s390_skeys_info = {
249 .name = TYPE_QEMU_S390_SKEYS,
250 .parent = TYPE_S390_SKEYS,
251 .instance_init = qemu_s390_skeys_init,
252 .instance_size = sizeof(QEMUS390SKeysState),
253 .class_init = qemu_s390_skeys_class_init,
254 .class_size = sizeof(S390SKeysClass),
257 static void s390_storage_keys_save(QEMUFile *f, void *opaque)
259 S390SKeysState *ss = S390_SKEYS(opaque);
260 S390SKeysClass *skeyclass = S390_SKEYS_GET_CLASS(ss);
261 GuestPhysBlockList guest_phys_blocks;
262 GuestPhysBlock *block;
263 uint64_t pages, gfn;
264 int error = 0;
265 uint8_t *buf;
267 if (!skeyclass->skeys_enabled(ss)) {
268 goto end_stream;
271 buf = g_try_malloc(S390_SKEYS_BUFFER_SIZE);
272 if (!buf) {
273 error_report("storage key save could not allocate memory");
274 goto end_stream;
277 guest_phys_blocks_init(&guest_phys_blocks);
278 guest_phys_blocks_append(&guest_phys_blocks);
280 /* Send each contiguous physical memory range separately. */
281 QTAILQ_FOREACH(block, &guest_phys_blocks.head, next) {
282 assert(QEMU_IS_ALIGNED(block->target_start, TARGET_PAGE_SIZE));
283 assert(QEMU_IS_ALIGNED(block->target_end, TARGET_PAGE_SIZE));
285 gfn = block->target_start / TARGET_PAGE_SIZE;
286 pages = (block->target_end - block->target_start) / TARGET_PAGE_SIZE;
287 qemu_put_be64(f, block->target_start | S390_SKEYS_SAVE_FLAG_SKEYS);
288 qemu_put_be64(f, pages);
290 while (pages) {
291 const uint64_t cur_pages = MIN(pages, S390_SKEYS_BUFFER_SIZE);
293 if (!error) {
294 error = skeyclass->get_skeys(ss, gfn, cur_pages, buf);
295 if (error) {
297 * Create a valid stream with all 0x00 and indicate
298 * S390_SKEYS_SAVE_FLAG_ERROR to the destination.
300 error_report("S390_GET_KEYS error %d", error);
301 memset(buf, 0, S390_SKEYS_BUFFER_SIZE);
305 qemu_put_buffer(f, buf, cur_pages);
306 gfn += cur_pages;
307 pages -= cur_pages;
310 if (error) {
311 break;
315 guest_phys_blocks_free(&guest_phys_blocks);
316 g_free(buf);
317 end_stream:
318 if (error) {
319 qemu_put_be64(f, S390_SKEYS_SAVE_FLAG_ERROR);
320 } else {
321 qemu_put_be64(f, S390_SKEYS_SAVE_FLAG_EOS);
325 static int s390_storage_keys_load(QEMUFile *f, void *opaque, int version_id)
327 S390SKeysState *ss = S390_SKEYS(opaque);
328 S390SKeysClass *skeyclass = S390_SKEYS_GET_CLASS(ss);
329 int ret = 0;
331 while (!ret) {
332 ram_addr_t addr;
333 int flags;
335 addr = qemu_get_be64(f);
336 flags = addr & ~TARGET_PAGE_MASK;
337 addr &= TARGET_PAGE_MASK;
339 switch (flags) {
340 case S390_SKEYS_SAVE_FLAG_SKEYS: {
341 const uint64_t total_count = qemu_get_be64(f);
342 uint64_t handled_count = 0, cur_count;
343 uint64_t cur_gfn = addr / TARGET_PAGE_SIZE;
344 uint8_t *buf = g_try_malloc(S390_SKEYS_BUFFER_SIZE);
346 if (!buf) {
347 error_report("storage key load could not allocate memory");
348 ret = -ENOMEM;
349 break;
352 while (handled_count < total_count) {
353 cur_count = MIN(total_count - handled_count,
354 S390_SKEYS_BUFFER_SIZE);
355 qemu_get_buffer(f, buf, cur_count);
357 ret = skeyclass->set_skeys(ss, cur_gfn, cur_count, buf);
358 if (ret < 0) {
359 error_report("S390_SET_KEYS error %d", ret);
360 break;
362 handled_count += cur_count;
363 cur_gfn += cur_count;
365 g_free(buf);
366 break;
368 case S390_SKEYS_SAVE_FLAG_ERROR: {
369 error_report("Storage key data is incomplete");
370 ret = -EINVAL;
371 break;
373 case S390_SKEYS_SAVE_FLAG_EOS:
374 /* normal exit */
375 return 0;
376 default:
377 error_report("Unexpected storage key flag data: %#x", flags);
378 ret = -EINVAL;
382 return ret;
385 static inline bool s390_skeys_get_migration_enabled(Object *obj, Error **errp)
387 S390SKeysState *ss = S390_SKEYS(obj);
389 return ss->migration_enabled;
392 static SaveVMHandlers savevm_s390_storage_keys = {
393 .save_state = s390_storage_keys_save,
394 .load_state = s390_storage_keys_load,
397 static inline void s390_skeys_set_migration_enabled(Object *obj, bool value,
398 Error **errp)
400 S390SKeysState *ss = S390_SKEYS(obj);
402 /* Prevent double registration of savevm handler */
403 if (ss->migration_enabled == value) {
404 return;
407 ss->migration_enabled = value;
409 if (ss->migration_enabled) {
410 register_savevm_live(TYPE_S390_SKEYS, 0, 1,
411 &savevm_s390_storage_keys, ss);
412 } else {
413 unregister_savevm(VMSTATE_IF(ss), TYPE_S390_SKEYS, ss);
417 static void s390_skeys_instance_init(Object *obj)
419 object_property_add_bool(obj, "migration-enabled",
420 s390_skeys_get_migration_enabled,
421 s390_skeys_set_migration_enabled);
422 object_property_set_bool(obj, "migration-enabled", true, NULL);
425 static void s390_skeys_class_init(ObjectClass *oc, void *data)
427 DeviceClass *dc = DEVICE_CLASS(oc);
429 dc->hotpluggable = false;
430 set_bit(DEVICE_CATEGORY_MISC, dc->categories);
433 static const TypeInfo s390_skeys_info = {
434 .name = TYPE_S390_SKEYS,
435 .parent = TYPE_DEVICE,
436 .instance_init = s390_skeys_instance_init,
437 .instance_size = sizeof(S390SKeysState),
438 .class_init = s390_skeys_class_init,
439 .class_size = sizeof(S390SKeysClass),
440 .abstract = true,
443 static void qemu_s390_skeys_register_types(void)
445 type_register_static(&s390_skeys_info);
446 type_register_static(&qemu_s390_skeys_info);
449 type_init(qemu_s390_skeys_register_types)