diff mbox series

[RFC,15/26] migration: Save confidential guest RAM using migration helper

Message ID 20210302204822.81901-16-dovmurik@linux.vnet.ibm.com
State New
Headers show
Series Confidential guest live migration | expand

Commit Message

Dov Murik March 2, 2021, 8:48 p.m. UTC
When saving RAM pages of a confidential guest, check whether a page is
encrypted.  If it is, ask the in-guest migration helper to encrypt the
page for transmission.

This relies on ability to track the encryption status of each page
according to guest's reports, and thus requires the relevant patches in
the guest OS and OVMF and the host KVM and QEMU.  This is all
encapsulated in is_page_encrypted; the implementation can be modified
according to the underlying implementation of page encryption status
tracking (bitmap / KVM shared regions list / user-side list)

Signed-off-by: Dov Murik <dovmurik@linux.vnet.ibm.com>
---
 migration/ram.c | 91 +++++++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 91 insertions(+)
diff mbox series

Patch

diff --git a/migration/ram.c b/migration/ram.c
index 997f90cc5b..8e55ed49fd 100644
--- a/migration/ram.c
+++ b/migration/ram.c
@@ -53,10 +53,13 @@ 
 #include "block.h"
 #include "sysemu/sysemu.h"
 #include "sysemu/cpu-throttle.h"
+#include "sysemu/kvm.h"
 #include "savevm.h"
 #include "qemu/iov.h"
 #include "multifd.h"
 #include "sysemu/runstate.h"
+#include "hw/boards.h"
+#include "confidential-ram.h"
 
 #if defined(__linux__)
 #include "qemu/userfaultfd.h"
@@ -81,6 +84,7 @@ 
 #define RAM_SAVE_FLAG_XBZRLE   0x40
 /* 0x80 is reserved in migration.h start with 0x100 next */
 #define RAM_SAVE_FLAG_COMPRESS_PAGE    0x100
+#define RAM_SAVE_FLAG_GUEST_ENCRYPTED_PAGE    0x200
 
 static inline bool memcrypt_enabled(void)
 {
@@ -94,6 +98,13 @@  static inline bool is_zero_range(uint8_t *p, uint64_t size)
     return buffer_is_zero(p, size);
 }
 
+static inline bool confidential_guest(void)
+{
+    MachineState *ms = MACHINE(qdev_get_machine());
+
+    return ms->cgs;
+}
+
 XBZRLECacheStats xbzrle_counters;
 
 /* struct contains XBZRLE cache and a static page
@@ -660,6 +671,23 @@  static void mig_throttle_guest_down(uint64_t bytes_dirty_period,
     }
 }
 
+/**
+ * is_page_encrypted: check if the page is encrypted
+ *
+ * Returns a bool indicating whether the page is encrypted.
+ */
+static bool is_page_encrypted(RAMState *rs, RAMBlock *block, unsigned long page)
+{
+    /* ROM devices contain unencrypted data */
+    if (memory_region_is_romd(block->mr) ||
+        memory_region_is_rom(block->mr) ||
+        !memory_region_is_ram(block->mr)) {
+        return false;
+    }
+
+    return test_bit(page, block->encbmap);
+}
+
 /**
  * xbzrle_cache_zero_page: insert a zero page in the XBZRLE cache
  *
@@ -1928,6 +1956,45 @@  static bool save_compress_page(RAMState *rs, RAMBlock *block, ram_addr_t offset)
     return false;
 }
 
+/**
+ * ram_save_encrypted_page - send the given encrypted page to the stream
+ *
+ * Return the number of pages written (=1).
+ */
+static int ram_save_encrypted_page(RAMState *rs, PageSearchStatus *pss,
+                                   bool last_stage)
+{
+    int ret;
+    uint8_t *p;
+    RAMBlock *block = pss->block;
+    ram_addr_t offset = pss->page << TARGET_PAGE_BITS;
+    ram_addr_t gpa;
+    uint64_t bytes_sent;
+
+    p = block->host + offset;
+
+    /* Find the GPA of the page */
+    if (!kvm_physical_memory_addr_from_host(kvm_state, p, &gpa)) {
+        error_report("%s failed to get gpa for offset %" PRIu64 " block %s",
+                     __func__, offset, memory_region_name(block->mr));
+        return -1;
+    }
+
+    ram_counters.transferred +=
+        save_page_header(rs, rs->f, block,
+                    offset | RAM_SAVE_FLAG_GUEST_ENCRYPTED_PAGE);
+
+    ret = cgs_mh_save_encrypted_page(rs->f, gpa, TARGET_PAGE_SIZE, &bytes_sent);
+    if (ret) {
+        return -1;
+    }
+
+    ram_counters.transferred += bytes_sent;
+    ram_counters.normal++;
+
+    return 1;
+}
+
 /**
  * ram_save_target_page: save one target page
  *
@@ -1948,6 +2015,26 @@  static int ram_save_target_page(RAMState *rs, PageSearchStatus *pss,
         return res;
     }
 
+    /*
+     * If memory encryption is enabled then skip saving the data pages used by
+     * the migration handler.
+     */
+    if (confidential_guest() &&
+        gpa_inside_migration_helper_shared_area(offset)) {
+        return 0;
+    }
+
+    /*
+     * If memory encryption is enabled then use memory encryption APIs
+     * to write the outgoing buffer to the wire. The encryption APIs
+     * will take care of accessing the guest memory and re-encrypt it
+     * for the transport purposes.
+     */
+    if (confidential_guest() &&
+        is_page_encrypted(rs, pss->block, pss->page)) {
+        return ram_save_encrypted_page(rs, pss, last_stage);
+    }
+
     if (save_compress_page(rs, block, offset)) {
         return 1;
     }
@@ -2776,6 +2863,10 @@  static int ram_save_setup(QEMUFile *f, void *opaque)
         return -1;
     }
 
+    if (confidential_guest()) {
+        cgs_mh_init();
+    }
+
     /* migration has already setup the bitmap, reuse it. */
     if (!migration_in_colo_state()) {
         if (ram_init_all(rsp) != 0) {