xref: /openbmc/qemu/hw/s390x/s390-skeys.c (revision effd60c8)
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/qdev-properties.h"
16 #include "hw/s390x/storage-keys.h"
17 #include "qapi/error.h"
18 #include "qapi/qapi-commands-misc-target.h"
19 #include "qapi/qmp/qdict.h"
20 #include "qemu/error-report.h"
21 #include "sysemu/memory_mapping.h"
22 #include "exec/address-spaces.h"
23 #include "sysemu/kvm.h"
24 #include "migration/qemu-file-types.h"
25 #include "migration/register.h"
26 
27 #define S390_SKEYS_BUFFER_SIZE (128 * KiB)  /* Room for 128k storage keys */
28 #define S390_SKEYS_SAVE_FLAG_EOS 0x01
29 #define S390_SKEYS_SAVE_FLAG_SKEYS 0x02
30 #define S390_SKEYS_SAVE_FLAG_ERROR 0x04
31 
32 S390SKeysState *s390_get_skeys_device(void)
33 {
34     S390SKeysState *ss;
35 
36     ss = S390_SKEYS(object_resolve_path_type("", TYPE_S390_SKEYS, NULL));
37     assert(ss);
38     return ss;
39 }
40 
41 void s390_skeys_init(void)
42 {
43     Object *obj;
44 
45     if (kvm_enabled()) {
46         obj = object_new(TYPE_KVM_S390_SKEYS);
47     } else {
48         obj = object_new(TYPE_QEMU_S390_SKEYS);
49     }
50     object_property_add_child(qdev_get_machine(), TYPE_S390_SKEYS,
51                               obj);
52     object_unref(obj);
53 
54     qdev_realize(DEVICE(obj), NULL, &error_fatal);
55 }
56 
57 static void write_keys(FILE *f, uint8_t *keys, uint64_t startgfn,
58                        uint64_t count, Error **errp)
59 {
60     uint64_t curpage = startgfn;
61     uint64_t maxpage = curpage + count - 1;
62 
63     for (; curpage <= maxpage; curpage++) {
64         uint8_t acc = (*keys & 0xF0) >> 4;
65         int fp =  (*keys & 0x08);
66         int ref = (*keys & 0x04);
67         int ch = (*keys & 0x02);
68         int res = (*keys & 0x01);
69 
70         fprintf(f, "page=%03" PRIx64 ": key(%d) => ACC=%X, FP=%d, REF=%d,"
71                 " ch=%d, reserved=%d\n",
72                 curpage, *keys, acc, fp, ref, ch, res);
73         keys++;
74     }
75 }
76 
77 void hmp_info_skeys(Monitor *mon, const QDict *qdict)
78 {
79     S390SKeysState *ss = s390_get_skeys_device();
80     S390SKeysClass *skeyclass = S390_SKEYS_GET_CLASS(ss);
81     uint64_t addr = qdict_get_int(qdict, "addr");
82     uint8_t key;
83     int r;
84 
85     /* Quick check to see if guest is using storage keys*/
86     if (!skeyclass->skeys_are_enabled(ss)) {
87         monitor_printf(mon, "Error: This guest is not using storage keys\n");
88         return;
89     }
90 
91     if (!address_space_access_valid(&address_space_memory,
92                                     addr & TARGET_PAGE_MASK, TARGET_PAGE_SIZE,
93                                     false, MEMTXATTRS_UNSPECIFIED)) {
94         monitor_printf(mon, "Error: The given address is not valid\n");
95         return;
96     }
97 
98     r = skeyclass->get_skeys(ss, addr / TARGET_PAGE_SIZE, 1, &key);
99     if (r < 0) {
100         monitor_printf(mon, "Error: %s\n", strerror(-r));
101         return;
102     }
103 
104     monitor_printf(mon, "  key: 0x%X\n", key);
105 }
106 
107 void hmp_dump_skeys(Monitor *mon, const QDict *qdict)
108 {
109     const char *filename = qdict_get_str(qdict, "filename");
110     Error *err = NULL;
111 
112     qmp_dump_skeys(filename, &err);
113     if (err) {
114         error_report_err(err);
115     }
116 }
117 
118 void qmp_dump_skeys(const char *filename, Error **errp)
119 {
120     S390SKeysState *ss = s390_get_skeys_device();
121     S390SKeysClass *skeyclass = S390_SKEYS_GET_CLASS(ss);
122     GuestPhysBlockList guest_phys_blocks;
123     GuestPhysBlock *block;
124     uint64_t pages, gfn;
125     Error *lerr = NULL;
126     uint8_t *buf;
127     int ret;
128     int fd;
129     FILE *f;
130 
131     /* Quick check to see if guest is using storage keys*/
132     if (!skeyclass->skeys_are_enabled(ss)) {
133         error_setg(errp, "This guest is not using storage keys - "
134                          "nothing to dump");
135         return;
136     }
137 
138     fd = qemu_open_old(filename, O_WRONLY | O_CREAT | O_TRUNC, 0600);
139     if (fd < 0) {
140         error_setg_file_open(errp, errno, filename);
141         return;
142     }
143     f = fdopen(fd, "wb");
144     if (!f) {
145         close(fd);
146         error_setg_file_open(errp, errno, filename);
147         return;
148     }
149 
150     buf = g_try_malloc(S390_SKEYS_BUFFER_SIZE);
151     if (!buf) {
152         error_setg(errp, "Could not allocate memory");
153         goto out;
154     }
155 
156     assert(bql_locked());
157     guest_phys_blocks_init(&guest_phys_blocks);
158     guest_phys_blocks_append(&guest_phys_blocks);
159 
160     QTAILQ_FOREACH(block, &guest_phys_blocks.head, next) {
161         assert(QEMU_IS_ALIGNED(block->target_start, TARGET_PAGE_SIZE));
162         assert(QEMU_IS_ALIGNED(block->target_end, TARGET_PAGE_SIZE));
163 
164         gfn = block->target_start / TARGET_PAGE_SIZE;
165         pages = (block->target_end - block->target_start) / TARGET_PAGE_SIZE;
166 
167         while (pages) {
168             const uint64_t cur_pages = MIN(pages, S390_SKEYS_BUFFER_SIZE);
169 
170             ret = skeyclass->get_skeys(ss, gfn, cur_pages, buf);
171             if (ret < 0) {
172                 error_setg_errno(errp, -ret, "get_keys error");
173                 goto out_free;
174             }
175 
176             /* write keys to stream */
177             write_keys(f, buf, gfn, cur_pages, &lerr);
178             if (lerr) {
179                 goto out_free;
180             }
181 
182             gfn += cur_pages;
183             pages -= cur_pages;
184         }
185     }
186 
187 out_free:
188     guest_phys_blocks_free(&guest_phys_blocks);
189     error_propagate(errp, lerr);
190     g_free(buf);
191 out:
192     fclose(f);
193 }
194 
195 static bool qemu_s390_skeys_are_enabled(S390SKeysState *ss)
196 {
197     QEMUS390SKeysState *skeys = QEMU_S390_SKEYS(ss);
198 
199     /* Lockless check is sufficient. */
200     return !!skeys->keydata;
201 }
202 
203 static bool qemu_s390_enable_skeys(S390SKeysState *ss)
204 {
205     QEMUS390SKeysState *skeys = QEMU_S390_SKEYS(ss);
206     static gsize initialized;
207 
208     if (likely(skeys->keydata)) {
209         return true;
210     }
211 
212     /*
213      * TODO: Modern Linux doesn't use storage keys unless running KVM guests
214      *       that use storage keys. Therefore, we keep it simple for now.
215      *
216      * 1) We should initialize to "referenced+changed" for an initial
217      *    over-indication. Let's avoid touching megabytes of data for now and
218      *    assume that any sane user will issue a storage key instruction before
219      *    actually relying on this data.
220      * 2) Relying on ram_size and allocating a big array is ugly. We should
221      *    allocate and manage storage key data per RAMBlock or optimally using
222      *    some sparse data structure.
223      * 3) We only ever have a single S390SKeysState, so relying on
224      *    g_once_init_enter() is good enough.
225      */
226     if (g_once_init_enter(&initialized)) {
227         MachineState *machine = MACHINE(qdev_get_machine());
228 
229         skeys->key_count = machine->ram_size / TARGET_PAGE_SIZE;
230         skeys->keydata = g_malloc0(skeys->key_count);
231         g_once_init_leave(&initialized, 1);
232     }
233     return false;
234 }
235 
236 static int qemu_s390_skeys_set(S390SKeysState *ss, uint64_t start_gfn,
237                               uint64_t count, uint8_t *keys)
238 {
239     QEMUS390SKeysState *skeydev = QEMU_S390_SKEYS(ss);
240     int i;
241 
242     /* Check for uint64 overflow and access beyond end of key data */
243     if (unlikely(!skeydev->keydata || start_gfn + count > skeydev->key_count ||
244                   start_gfn + count < count)) {
245         error_report("Error: Setting storage keys for pages with unallocated "
246                      "storage key memory: gfn=%" PRIx64 " count=%" PRId64,
247                      start_gfn, count);
248         return -EINVAL;
249     }
250 
251     for (i = 0; i < count; i++) {
252         skeydev->keydata[start_gfn + i] = keys[i];
253     }
254     return 0;
255 }
256 
257 static int qemu_s390_skeys_get(S390SKeysState *ss, uint64_t start_gfn,
258                                uint64_t count, uint8_t *keys)
259 {
260     QEMUS390SKeysState *skeydev = QEMU_S390_SKEYS(ss);
261     int i;
262 
263     /* Check for uint64 overflow and access beyond end of key data */
264     if (unlikely(!skeydev->keydata || start_gfn + count > skeydev->key_count ||
265                   start_gfn + count < count)) {
266         error_report("Error: Getting storage keys for pages with unallocated "
267                      "storage key memory: gfn=%" PRIx64 " count=%" PRId64,
268                      start_gfn, count);
269         return -EINVAL;
270     }
271 
272     for (i = 0; i < count; i++) {
273         keys[i] = skeydev->keydata[start_gfn + i];
274     }
275     return 0;
276 }
277 
278 static void qemu_s390_skeys_class_init(ObjectClass *oc, void *data)
279 {
280     S390SKeysClass *skeyclass = S390_SKEYS_CLASS(oc);
281     DeviceClass *dc = DEVICE_CLASS(oc);
282 
283     skeyclass->skeys_are_enabled = qemu_s390_skeys_are_enabled;
284     skeyclass->enable_skeys = qemu_s390_enable_skeys;
285     skeyclass->get_skeys = qemu_s390_skeys_get;
286     skeyclass->set_skeys = qemu_s390_skeys_set;
287 
288     /* Reason: Internal device (only one skeys device for the whole memory) */
289     dc->user_creatable = false;
290 }
291 
292 static const TypeInfo qemu_s390_skeys_info = {
293     .name          = TYPE_QEMU_S390_SKEYS,
294     .parent        = TYPE_S390_SKEYS,
295     .instance_size = sizeof(QEMUS390SKeysState),
296     .class_init    = qemu_s390_skeys_class_init,
297     .class_size    = sizeof(S390SKeysClass),
298 };
299 
300 static void s390_storage_keys_save(QEMUFile *f, void *opaque)
301 {
302     S390SKeysState *ss = S390_SKEYS(opaque);
303     S390SKeysClass *skeyclass = S390_SKEYS_GET_CLASS(ss);
304     GuestPhysBlockList guest_phys_blocks;
305     GuestPhysBlock *block;
306     uint64_t pages, gfn;
307     int error = 0;
308     uint8_t *buf;
309 
310     if (!skeyclass->skeys_are_enabled(ss)) {
311         goto end_stream;
312     }
313 
314     buf = g_try_malloc(S390_SKEYS_BUFFER_SIZE);
315     if (!buf) {
316         error_report("storage key save could not allocate memory");
317         goto end_stream;
318     }
319 
320     guest_phys_blocks_init(&guest_phys_blocks);
321     guest_phys_blocks_append(&guest_phys_blocks);
322 
323     /* Send each contiguous physical memory range separately. */
324     QTAILQ_FOREACH(block, &guest_phys_blocks.head, next) {
325         assert(QEMU_IS_ALIGNED(block->target_start, TARGET_PAGE_SIZE));
326         assert(QEMU_IS_ALIGNED(block->target_end, TARGET_PAGE_SIZE));
327 
328         gfn = block->target_start / TARGET_PAGE_SIZE;
329         pages = (block->target_end - block->target_start) / TARGET_PAGE_SIZE;
330         qemu_put_be64(f, block->target_start | S390_SKEYS_SAVE_FLAG_SKEYS);
331         qemu_put_be64(f, pages);
332 
333         while (pages) {
334             const uint64_t cur_pages = MIN(pages, S390_SKEYS_BUFFER_SIZE);
335 
336             if (!error) {
337                 error = skeyclass->get_skeys(ss, gfn, cur_pages, buf);
338                 if (error) {
339                     /*
340                      * Create a valid stream with all 0x00 and indicate
341                      * S390_SKEYS_SAVE_FLAG_ERROR to the destination.
342                      */
343                     error_report("S390_GET_KEYS error %d", error);
344                     memset(buf, 0, S390_SKEYS_BUFFER_SIZE);
345                 }
346             }
347 
348             qemu_put_buffer(f, buf, cur_pages);
349             gfn += cur_pages;
350             pages -= cur_pages;
351         }
352 
353         if (error) {
354             break;
355         }
356     }
357 
358     guest_phys_blocks_free(&guest_phys_blocks);
359     g_free(buf);
360 end_stream:
361     if (error) {
362         qemu_put_be64(f, S390_SKEYS_SAVE_FLAG_ERROR);
363     } else {
364         qemu_put_be64(f, S390_SKEYS_SAVE_FLAG_EOS);
365     }
366 }
367 
368 static int s390_storage_keys_load(QEMUFile *f, void *opaque, int version_id)
369 {
370     S390SKeysState *ss = S390_SKEYS(opaque);
371     S390SKeysClass *skeyclass = S390_SKEYS_GET_CLASS(ss);
372     int ret = 0;
373 
374     /*
375      * Make sure to lazy-enable if required to be done explicitly. No need to
376      * flush any TLB as the VM is not running yet.
377      */
378     if (skeyclass->enable_skeys) {
379         skeyclass->enable_skeys(ss);
380     }
381 
382     while (!ret) {
383         ram_addr_t addr;
384         int flags;
385 
386         addr = qemu_get_be64(f);
387         flags = addr & ~TARGET_PAGE_MASK;
388         addr &= TARGET_PAGE_MASK;
389 
390         switch (flags) {
391         case S390_SKEYS_SAVE_FLAG_SKEYS: {
392             const uint64_t total_count = qemu_get_be64(f);
393             uint64_t handled_count = 0, cur_count;
394             uint64_t cur_gfn = addr / TARGET_PAGE_SIZE;
395             uint8_t *buf = g_try_malloc(S390_SKEYS_BUFFER_SIZE);
396 
397             if (!buf) {
398                 error_report("storage key load could not allocate memory");
399                 ret = -ENOMEM;
400                 break;
401             }
402 
403             while (handled_count < total_count) {
404                 cur_count = MIN(total_count - handled_count,
405                                 S390_SKEYS_BUFFER_SIZE);
406                 qemu_get_buffer(f, buf, cur_count);
407 
408                 ret = skeyclass->set_skeys(ss, cur_gfn, cur_count, buf);
409                 if (ret < 0) {
410                     error_report("S390_SET_KEYS error %d", ret);
411                     break;
412                 }
413                 handled_count += cur_count;
414                 cur_gfn += cur_count;
415             }
416             g_free(buf);
417             break;
418         }
419         case S390_SKEYS_SAVE_FLAG_ERROR: {
420             error_report("Storage key data is incomplete");
421             ret = -EINVAL;
422             break;
423         }
424         case S390_SKEYS_SAVE_FLAG_EOS:
425             /* normal exit */
426             return 0;
427         default:
428             error_report("Unexpected storage key flag data: %#x", flags);
429             ret = -EINVAL;
430         }
431     }
432 
433     return ret;
434 }
435 
436 static SaveVMHandlers savevm_s390_storage_keys = {
437     .save_state = s390_storage_keys_save,
438     .load_state = s390_storage_keys_load,
439 };
440 
441 static void s390_skeys_realize(DeviceState *dev, Error **errp)
442 {
443     S390SKeysState *ss = S390_SKEYS(dev);
444 
445     if (ss->migration_enabled) {
446         register_savevm_live(TYPE_S390_SKEYS, 0, 1,
447                              &savevm_s390_storage_keys, ss);
448     }
449 }
450 
451 static Property s390_skeys_props[] = {
452     DEFINE_PROP_BOOL("migration-enabled", S390SKeysState, migration_enabled, true),
453     DEFINE_PROP_END_OF_LIST(),
454 };
455 
456 static void s390_skeys_class_init(ObjectClass *oc, void *data)
457 {
458     DeviceClass *dc = DEVICE_CLASS(oc);
459 
460     dc->hotpluggable = false;
461     dc->realize = s390_skeys_realize;
462     device_class_set_props(dc, s390_skeys_props);
463     set_bit(DEVICE_CATEGORY_MISC, dc->categories);
464 }
465 
466 static const TypeInfo s390_skeys_info = {
467     .name          = TYPE_S390_SKEYS,
468     .parent        = TYPE_DEVICE,
469     .instance_size = sizeof(S390SKeysState),
470     .class_init    = s390_skeys_class_init,
471     .class_size    = sizeof(S390SKeysClass),
472     .abstract = true,
473 };
474 
475 static void qemu_s390_skeys_register_types(void)
476 {
477     type_register_static(&s390_skeys_info);
478     type_register_static(&qemu_s390_skeys_info);
479 }
480 
481 type_init(qemu_s390_skeys_register_types)
482