xref: /openbmc/qemu/hw/s390x/s390-skeys.c (revision 6016b7b4)
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 
31 S390SKeysState *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 
40 void 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 
56 static 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 
76 void 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 
106 void 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 
117 void 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 
186 out_free:
187     guest_phys_blocks_free(&guest_phys_blocks);
188     error_propagate(errp, lerr);
189     g_free(buf);
190 out:
191     fclose(f);
192 }
193 
194 static 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 
202 static 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 
235 static 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 
256 static 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 
277 static 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 
291 static 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 
299 static 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);
359 end_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 
367 static 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 
435 static 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 
442 static SaveVMHandlers savevm_s390_storage_keys = {
443     .save_state = s390_storage_keys_save,
444     .load_state = s390_storage_keys_load,
445 };
446 
447 static 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 
467 static 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 
475 static 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 
483 static 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 
493 static 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 
499 type_init(qemu_s390_skeys_register_types)
500