xref: /openbmc/qemu/hw/s390x/s390-skeys.c (revision c63ca4ff)
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/kvm.h"
21 #include "migration/qemu-file-types.h"
22 #include "migration/register.h"
23 
24 #define S390_SKEYS_BUFFER_SIZE (128 * KiB)  /* Room for 128k storage keys */
25 #define S390_SKEYS_SAVE_FLAG_EOS 0x01
26 #define S390_SKEYS_SAVE_FLAG_SKEYS 0x02
27 #define S390_SKEYS_SAVE_FLAG_ERROR 0x04
28 
29 S390SKeysState *s390_get_skeys_device(void)
30 {
31     S390SKeysState *ss;
32 
33     ss = S390_SKEYS(object_resolve_path_type("", TYPE_S390_SKEYS, NULL));
34     assert(ss);
35     return ss;
36 }
37 
38 void s390_skeys_init(void)
39 {
40     Object *obj;
41 
42     if (kvm_enabled()) {
43         obj = object_new(TYPE_KVM_S390_SKEYS);
44     } else {
45         obj = object_new(TYPE_QEMU_S390_SKEYS);
46     }
47     object_property_add_child(qdev_get_machine(), TYPE_S390_SKEYS,
48                               obj);
49     object_unref(obj);
50 
51     qdev_realize(DEVICE(obj), NULL, &error_fatal);
52 }
53 
54 static void write_keys(FILE *f, uint8_t *keys, uint64_t startgfn,
55                        uint64_t count, Error **errp)
56 {
57     uint64_t curpage = startgfn;
58     uint64_t maxpage = curpage + count - 1;
59 
60     for (; curpage <= maxpage; curpage++) {
61         uint8_t acc = (*keys & 0xF0) >> 4;
62         int fp =  (*keys & 0x08);
63         int ref = (*keys & 0x04);
64         int ch = (*keys & 0x02);
65         int res = (*keys & 0x01);
66 
67         fprintf(f, "page=%03" PRIx64 ": key(%d) => ACC=%X, FP=%d, REF=%d,"
68                 " ch=%d, reserved=%d\n",
69                 curpage, *keys, acc, fp, ref, ch, res);
70         keys++;
71     }
72 }
73 
74 void hmp_info_skeys(Monitor *mon, const QDict *qdict)
75 {
76     S390SKeysState *ss = s390_get_skeys_device();
77     S390SKeysClass *skeyclass = S390_SKEYS_GET_CLASS(ss);
78     uint64_t addr = qdict_get_int(qdict, "addr");
79     uint8_t key;
80     int r;
81 
82     /* Quick check to see if guest is using storage keys*/
83     if (!skeyclass->skeys_enabled(ss)) {
84         monitor_printf(mon, "Error: This guest is not using storage keys\n");
85         return;
86     }
87 
88     r = skeyclass->get_skeys(ss, addr / TARGET_PAGE_SIZE, 1, &key);
89     if (r < 0) {
90         monitor_printf(mon, "Error: %s\n", strerror(-r));
91         return;
92     }
93 
94     monitor_printf(mon, "  key: 0x%X\n", key);
95 }
96 
97 void hmp_dump_skeys(Monitor *mon, const QDict *qdict)
98 {
99     const char *filename = qdict_get_str(qdict, "filename");
100     Error *err = NULL;
101 
102     qmp_dump_skeys(filename, &err);
103     if (err) {
104         error_report_err(err);
105     }
106 }
107 
108 void qmp_dump_skeys(const char *filename, Error **errp)
109 {
110     S390SKeysState *ss = s390_get_skeys_device();
111     S390SKeysClass *skeyclass = S390_SKEYS_GET_CLASS(ss);
112     MachineState *ms = MACHINE(qdev_get_machine());
113     const uint64_t total_count = ms->ram_size / TARGET_PAGE_SIZE;
114     uint64_t handled_count = 0, cur_count;
115     Error *lerr = NULL;
116     vaddr cur_gfn = 0;
117     uint8_t *buf;
118     int ret;
119     int fd;
120     FILE *f;
121 
122     /* Quick check to see if guest is using storage keys*/
123     if (!skeyclass->skeys_enabled(ss)) {
124         error_setg(errp, "This guest is not using storage keys - "
125                          "nothing to dump");
126         return;
127     }
128 
129     fd = qemu_open_old(filename, O_WRONLY | O_CREAT | O_TRUNC, 0600);
130     if (fd < 0) {
131         error_setg_file_open(errp, errno, filename);
132         return;
133     }
134     f = fdopen(fd, "wb");
135     if (!f) {
136         close(fd);
137         error_setg_file_open(errp, errno, filename);
138         return;
139     }
140 
141     buf = g_try_malloc(S390_SKEYS_BUFFER_SIZE);
142     if (!buf) {
143         error_setg(errp, "Could not allocate memory");
144         goto out;
145     }
146 
147     /* we'll only dump initial memory for now */
148     while (handled_count < total_count) {
149         /* Calculate how many keys to ask for & handle overflow case */
150         cur_count = MIN(total_count - handled_count, S390_SKEYS_BUFFER_SIZE);
151 
152         ret = skeyclass->get_skeys(ss, cur_gfn, cur_count, buf);
153         if (ret < 0) {
154             error_setg(errp, "get_keys error %d", ret);
155             goto out_free;
156         }
157 
158         /* write keys to stream */
159         write_keys(f, buf, cur_gfn, cur_count, &lerr);
160         if (lerr) {
161             goto out_free;
162         }
163 
164         cur_gfn += cur_count;
165         handled_count += cur_count;
166     }
167 
168 out_free:
169     error_propagate(errp, lerr);
170     g_free(buf);
171 out:
172     fclose(f);
173 }
174 
175 static void qemu_s390_skeys_init(Object *obj)
176 {
177     QEMUS390SKeysState *skeys = QEMU_S390_SKEYS(obj);
178     MachineState *machine = MACHINE(qdev_get_machine());
179 
180     skeys->key_count = machine->ram_size / TARGET_PAGE_SIZE;
181     skeys->keydata = g_malloc0(skeys->key_count);
182 }
183 
184 static int qemu_s390_skeys_enabled(S390SKeysState *ss)
185 {
186     return 1;
187 }
188 
189 /*
190  * TODO: for memory hotplug support qemu_s390_skeys_set and qemu_s390_skeys_get
191  * will have to make sure that the given gfn belongs to a memory region and not
192  * a memory hole.
193  */
194 static int qemu_s390_skeys_set(S390SKeysState *ss, uint64_t start_gfn,
195                               uint64_t count, uint8_t *keys)
196 {
197     QEMUS390SKeysState *skeydev = QEMU_S390_SKEYS(ss);
198     int i;
199 
200     /* Check for uint64 overflow and access beyond end of key data */
201     if (start_gfn + count > skeydev->key_count || start_gfn + count < count) {
202         error_report("Error: Setting storage keys for page beyond the end "
203                      "of memory: gfn=%" PRIx64 " count=%" PRId64,
204                      start_gfn, count);
205         return -EINVAL;
206     }
207 
208     for (i = 0; i < count; i++) {
209         skeydev->keydata[start_gfn + i] = keys[i];
210     }
211     return 0;
212 }
213 
214 static int qemu_s390_skeys_get(S390SKeysState *ss, uint64_t start_gfn,
215                                uint64_t count, uint8_t *keys)
216 {
217     QEMUS390SKeysState *skeydev = QEMU_S390_SKEYS(ss);
218     int i;
219 
220     /* Check for uint64 overflow and access beyond end of key data */
221     if (start_gfn + count > skeydev->key_count || start_gfn + count < count) {
222         error_report("Error: Getting storage keys for page beyond the end "
223                      "of memory: gfn=%" PRIx64 " count=%" PRId64,
224                      start_gfn, count);
225         return -EINVAL;
226     }
227 
228     for (i = 0; i < count; i++) {
229         keys[i] = skeydev->keydata[start_gfn + i];
230     }
231     return 0;
232 }
233 
234 static void qemu_s390_skeys_class_init(ObjectClass *oc, void *data)
235 {
236     S390SKeysClass *skeyclass = S390_SKEYS_CLASS(oc);
237     DeviceClass *dc = DEVICE_CLASS(oc);
238 
239     skeyclass->skeys_enabled = qemu_s390_skeys_enabled;
240     skeyclass->get_skeys = qemu_s390_skeys_get;
241     skeyclass->set_skeys = qemu_s390_skeys_set;
242 
243     /* Reason: Internal device (only one skeys device for the whole memory) */
244     dc->user_creatable = false;
245 }
246 
247 static const TypeInfo qemu_s390_skeys_info = {
248     .name          = TYPE_QEMU_S390_SKEYS,
249     .parent        = TYPE_S390_SKEYS,
250     .instance_init = qemu_s390_skeys_init,
251     .instance_size = sizeof(QEMUS390SKeysState),
252     .class_init    = qemu_s390_skeys_class_init,
253     .class_size    = sizeof(S390SKeysClass),
254 };
255 
256 static void s390_storage_keys_save(QEMUFile *f, void *opaque)
257 {
258     S390SKeysState *ss = S390_SKEYS(opaque);
259     S390SKeysClass *skeyclass = S390_SKEYS_GET_CLASS(ss);
260     MachineState *ms = MACHINE(qdev_get_machine());
261     uint64_t pages_left = ms->ram_size / TARGET_PAGE_SIZE;
262     uint64_t read_count, eos = S390_SKEYS_SAVE_FLAG_EOS;
263     vaddr cur_gfn = 0;
264     int error = 0;
265     uint8_t *buf;
266 
267     if (!skeyclass->skeys_enabled(ss)) {
268         goto end_stream;
269     }
270 
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;
275     }
276 
277     /* We only support initial memory. Standby memory is not handled yet. */
278     qemu_put_be64(f, (cur_gfn * TARGET_PAGE_SIZE) | S390_SKEYS_SAVE_FLAG_SKEYS);
279     qemu_put_be64(f, pages_left);
280 
281     while (pages_left) {
282         read_count = MIN(pages_left, S390_SKEYS_BUFFER_SIZE);
283 
284         if (!error) {
285             error = skeyclass->get_skeys(ss, cur_gfn, read_count, buf);
286             if (error) {
287                 /*
288                  * If error: we want to fill the stream with valid data instead
289                  * of stopping early so we pad the stream with 0x00 values and
290                  * use S390_SKEYS_SAVE_FLAG_ERROR to indicate failure to the
291                  * reading side.
292                  */
293                 error_report("S390_GET_KEYS error %d", error);
294                 memset(buf, 0, S390_SKEYS_BUFFER_SIZE);
295                 eos = S390_SKEYS_SAVE_FLAG_ERROR;
296             }
297         }
298 
299         qemu_put_buffer(f, buf, read_count);
300         cur_gfn += read_count;
301         pages_left -= read_count;
302     }
303 
304     g_free(buf);
305 end_stream:
306     qemu_put_be64(f, eos);
307 }
308 
309 static int s390_storage_keys_load(QEMUFile *f, void *opaque, int version_id)
310 {
311     S390SKeysState *ss = S390_SKEYS(opaque);
312     S390SKeysClass *skeyclass = S390_SKEYS_GET_CLASS(ss);
313     int ret = 0;
314 
315     while (!ret) {
316         ram_addr_t addr;
317         int flags;
318 
319         addr = qemu_get_be64(f);
320         flags = addr & ~TARGET_PAGE_MASK;
321         addr &= TARGET_PAGE_MASK;
322 
323         switch (flags) {
324         case S390_SKEYS_SAVE_FLAG_SKEYS: {
325             const uint64_t total_count = qemu_get_be64(f);
326             uint64_t handled_count = 0, cur_count;
327             uint64_t cur_gfn = addr / TARGET_PAGE_SIZE;
328             uint8_t *buf = g_try_malloc(S390_SKEYS_BUFFER_SIZE);
329 
330             if (!buf) {
331                 error_report("storage key load could not allocate memory");
332                 ret = -ENOMEM;
333                 break;
334             }
335 
336             while (handled_count < total_count) {
337                 cur_count = MIN(total_count - handled_count,
338                                 S390_SKEYS_BUFFER_SIZE);
339                 qemu_get_buffer(f, buf, cur_count);
340 
341                 ret = skeyclass->set_skeys(ss, cur_gfn, cur_count, buf);
342                 if (ret < 0) {
343                     error_report("S390_SET_KEYS error %d", ret);
344                     break;
345                 }
346                 handled_count += cur_count;
347                 cur_gfn += cur_count;
348             }
349             g_free(buf);
350             break;
351         }
352         case S390_SKEYS_SAVE_FLAG_ERROR: {
353             error_report("Storage key data is incomplete");
354             ret = -EINVAL;
355             break;
356         }
357         case S390_SKEYS_SAVE_FLAG_EOS:
358             /* normal exit */
359             return 0;
360         default:
361             error_report("Unexpected storage key flag data: %#x", flags);
362             ret = -EINVAL;
363         }
364     }
365 
366     return ret;
367 }
368 
369 static inline bool s390_skeys_get_migration_enabled(Object *obj, Error **errp)
370 {
371     S390SKeysState *ss = S390_SKEYS(obj);
372 
373     return ss->migration_enabled;
374 }
375 
376 static SaveVMHandlers savevm_s390_storage_keys = {
377     .save_state = s390_storage_keys_save,
378     .load_state = s390_storage_keys_load,
379 };
380 
381 static inline void s390_skeys_set_migration_enabled(Object *obj, bool value,
382                                             Error **errp)
383 {
384     S390SKeysState *ss = S390_SKEYS(obj);
385 
386     /* Prevent double registration of savevm handler */
387     if (ss->migration_enabled == value) {
388         return;
389     }
390 
391     ss->migration_enabled = value;
392 
393     if (ss->migration_enabled) {
394         register_savevm_live(TYPE_S390_SKEYS, 0, 1,
395                              &savevm_s390_storage_keys, ss);
396     } else {
397         unregister_savevm(VMSTATE_IF(ss), TYPE_S390_SKEYS, ss);
398     }
399 }
400 
401 static void s390_skeys_instance_init(Object *obj)
402 {
403     object_property_add_bool(obj, "migration-enabled",
404                              s390_skeys_get_migration_enabled,
405                              s390_skeys_set_migration_enabled);
406     object_property_set_bool(obj, "migration-enabled", true, NULL);
407 }
408 
409 static void s390_skeys_class_init(ObjectClass *oc, void *data)
410 {
411     DeviceClass *dc = DEVICE_CLASS(oc);
412 
413     dc->hotpluggable = false;
414     set_bit(DEVICE_CATEGORY_MISC, dc->categories);
415 }
416 
417 static const TypeInfo s390_skeys_info = {
418     .name          = TYPE_S390_SKEYS,
419     .parent        = TYPE_DEVICE,
420     .instance_init = s390_skeys_instance_init,
421     .instance_size = sizeof(S390SKeysState),
422     .class_init    = s390_skeys_class_init,
423     .class_size    = sizeof(S390SKeysClass),
424     .abstract = true,
425 };
426 
427 static void qemu_s390_skeys_register_types(void)
428 {
429     type_register_static(&s390_skeys_info);
430     type_register_static(&qemu_s390_skeys_info);
431 }
432 
433 type_init(qemu_s390_skeys_register_types)
434