@@ -10,9 +10,12 @@
*/
#include "hw/boards.h"
+#include "qmp-commands.h"
#include "hw/s390x/storage-keys.h"
#include "qemu/error-report.h"
+#define S390_SKEYS_BUFFER_SIZE 131072 /* Room for 128k storage keys */
+
S390SKeysState *s390_get_skeys_device(void)
{
S390SKeysState *ss;
@@ -38,6 +41,94 @@ void s390_skeys_init(void)
qdev_init_nofail(DEVICE(obj));
}
+static void write_keys(FILE *f, uint8_t *keys, uint64_t startgfn,
+ uint64_t count, Error **errp)
+{
+ uint64_t curpage = startgfn;
+ uint64_t maxpage = curpage + count - 1;
+ int r;
+
+ for (; curpage <= maxpage; curpage++) {
+ uint8_t acc = (*keys & 0xF0) >> 4;
+ int fp = (*keys & 0x08);
+ int ref = (*keys & 0x04);
+ int ch = (*keys & 0x02);
+ int reserved = (*keys & 0x01);
+
+ r = fprintf(f, "page=%03" PRIx64 ": key(%d) => ACC=%X, FP=%d, REF=%d,"
+ " ch=%d, reserved=%d\n", curpage, *keys, acc, fp, ref,
+ ch, reserved);
+ if (r < 0) {
+ error_setg(errp, "I/O error");
+ return;
+ }
+ keys++;
+ }
+}
+
+void qmp_dump_skeys(const char *filename, Error **errp)
+{
+ S390SKeysState *ss = s390_get_skeys_device();
+ S390SKeysClass *skeyclass = S390_SKEYS_GET_CLASS(ss);
+ const uint64_t total_count = ram_size / TARGET_PAGE_SIZE;
+ uint64_t handled_count = 0, cur_count;
+ Error *lerr = NULL;
+ vaddr cur_gfn = 0;
+ uint8_t *buf;
+ int ret;
+ FILE *f;
+
+ /* Quick check to see if guest is using storage keys*/
+ if (!skeyclass->skeys_enabled(ss)) {
+ error_setg(&lerr, "This guest is not using storage keys. "
+ "Nothing to dump.");
+ error_propagate(errp, lerr);
+ return;
+ }
+
+ f = fopen(filename, "wb");
+ if (!f) {
+ error_setg(&lerr, "Could not open file");
+ error_propagate(errp, lerr);
+ return;
+ }
+
+ buf = g_try_malloc(S390_SKEYS_BUFFER_SIZE);
+ if (!buf) {
+ error_setg(&lerr, "Could not allocate memory");
+ error_propagate(errp, lerr);
+ goto out;
+ }
+
+ /* we'll only dump initial memory for now */
+ while (handled_count < total_count) {
+ /* Calculate how many keys to ask for & handle overflow case */
+ cur_count = MIN(total_count - handled_count, S390_SKEYS_BUFFER_SIZE);
+
+ ret = skeyclass->get_skeys(ss, cur_gfn, cur_count, buf);
+ if (ret < 0) {
+ error_setg(&lerr, "get_keys error %d", ret);
+ error_propagate(errp, lerr);
+ goto out_free;
+ }
+
+ /* write keys to stream */
+ write_keys(f, buf, cur_gfn, cur_count, &lerr);
+ if (lerr) {
+ error_propagate(errp, lerr);
+ goto out_free;
+ }
+
+ cur_gfn += cur_count;
+ handled_count += cur_count;
+ }
+
+out_free:
+ g_free(buf);
+out:
+ fclose(f);
+}
+
static void qemu_s390_skeys_init(Object *obj)
{
QEMUS390SKeysState *skeys = QEMU_S390_SKEYS(obj);
@@ -5361,3 +5361,10 @@ void qmp_rtc_reset_reinjection(Error **errp)
error_setg(errp, QERR_FEATURE_DISABLED, "rtc-reset-reinjection");
}
#endif
+
+#ifndef TARGET_S390X
+void qmp_dump_skeys(const char *filename, Error **errp)
+{
+ error_setg(errp, QERR_FEATURE_DISABLED, "dump-skeys");
+}
+#endif
@@ -2058,6 +2058,19 @@
'returns': 'DumpGuestMemoryCapability' }
##
+# @dump-skeys
+#
+# Dump guest's storage keys. @filename: the path to the file to dump to.
+# This command is only supported on s390 architecture.
+#
+# Returns: nothing on success
+#
+# Since: 2.5
+##
+{ 'command': 'dump-skeys',
+ 'data': { 'filename': 'str' } }
+
+##
# @netdev_add:
#
# Add a network backend.
@@ -872,6 +872,31 @@ Example:
EQMP
+#if defined TARGET_S390X
+ {
+ .name = "dump-skeys",
+ .args_type = "filename:F",
+ .mhandler.cmd_new = qmp_marshal_input_dump_skeys,
+ },
+#endif
+
+SQMP
+dump-skeys
+----------
+
+Save guest storage keys to file.
+
+Arguments:
+
+- "filename": file path (json-string)
+
+Example:
+
+-> { "execute": "dump-skeys", "arguments": { "filename": "/tmp/skeys" } }
+<- { "return": {} }
+
+EQMP
+
{
.name = "netdev_add",
.args_type = "netdev:O",