diff --git a/hw-misc-psp-support-live-migrate-for-vpsp-device.patch b/hw-misc-psp-support-live-migrate-for-vpsp-device.patch new file mode 100644 index 0000000000000000000000000000000000000000..6db15aeae9deea4afcf77594349323e82a18b6c3 --- /dev/null +++ b/hw-misc-psp-support-live-migrate-for-vpsp-device.patch @@ -0,0 +1,401 @@ +From 17f20fb58ad96d04680f0b4f9f031041b1aceaf8 Mon Sep 17 00:00:00 2001 +From: xiongmengbiao +Date: Thu, 7 Aug 2025 17:48:54 +0800 +Subject: [PATCH] hw/misc/psp: support live migrate for vpsp device + +This patch adds live migration support by: +1. Introducing VMStateDescription for migration +2. Implementing pre-save to backup device state: + - Key images via VPSP_OP_BACKUP_KEY + - Command context via VPSP_OP_BACKUP_CTX +3. Implementing post-load to restore state: + - Command context via VPSP_OP_RESTORE_CTX + - Key images via VPSP_OP_RESTORE_KEY + +Signed-off-by: xiongmengbiao +--- + hw/i386/pc.c | 1 + + hw/misc/psp.c | 296 ++++++++++++++++++++++++++++++++++++++++++++++++++ + 2 files changed, 297 insertions(+) + +diff --git a/hw/i386/pc.c b/hw/i386/pc.c +index 527f4d295d..49d168a0ad 100644 +--- a/hw/i386/pc.c ++++ b/hw/i386/pc.c +@@ -890,6 +890,7 @@ static void mem2_init(MachineState *ms, MemoryRegion *system_memory) + + sprintf(mr_name, "mem2-%d", i); + memory_region_init_ram_ptr(mem2_mr[i], NULL, mr_name, HUGEPAGE_SIZE, ram); ++ vmstate_register_ram_global(mem2_mr[i]); + memory_region_add_subregion(system_memory, ms->ram2_base + (i * HUGEPAGE_SIZE), mem2_mr[i]); + } + +diff --git a/hw/misc/psp.c b/hw/misc/psp.c +index f760206a58..e999cbb2cb 100644 +--- a/hw/misc/psp.c ++++ b/hw/misc/psp.c +@@ -19,17 +19,27 @@ + #include "exec/address-spaces.h" + #include "exec/ramblock.h" + #include "hw/i386/e820_memory_layout.h" ++#include "migration/migration.h" ++#include "migration/misc.h" + #include + + #define TYPE_PSP_DEV "psp" + OBJECT_DECLARE_SIMPLE_TYPE(PSPDevState, PSP_DEV) + ++#define VPSP_MIGRATE_VERSION 1 ++#define HUGEPAGE_SIZE (1024*1024*2) ++ ++static int vpsp_dev_pre_save(void *opaque); ++static int vpsp_dev_post_load(void *opaque, int version_id); ++ + struct PSPDevState { + /* Private */ + DeviceState pdev; + + /* Public */ + Notifier shutdown_notifier; ++ NotifierWithReturn precopy_notifier; ++ + int dev_fd; + uint8_t enabled; + +@@ -41,6 +51,30 @@ struct PSPDevState { + uint32_t vid; + /* pinned hugepage numbers */ + int hp_num; ++ ++ uint32_t img_len; ++ uint8_t *key_img; ++ uint32_t ctx_len; ++ uint8_t *cmd_ctx; ++}; ++ ++static const VMStateDescription vmstate_vpsp_dev = { ++ .name = "vpsp-dev", ++ .version_id = VPSP_MIGRATE_VERSION, ++ .minimum_version_id = VPSP_MIGRATE_VERSION, ++ .pre_save = vpsp_dev_pre_save, ++ .post_load = vpsp_dev_post_load, ++ .fields = (VMStateField[]) { ++ VMSTATE_UINT32(img_len, PSPDevState), ++ VMSTATE_VBUFFER_ALLOC_UINT32(key_img, ++ PSPDevState, 0, 0, ++ img_len), ++ VMSTATE_UINT32(ctx_len, PSPDevState), ++ VMSTATE_VBUFFER_ALLOC_UINT32(cmd_ctx, ++ PSPDevState, 0, 0, ++ ctx_len), ++ VMSTATE_END_OF_LIST() ++ } + }; + + #define PSP_DEV_PATH "/dev/hygon_psp_config" +@@ -57,8 +91,22 @@ enum VPSP_DEV_CTRL_OPCODE { + VPSP_OP_SET_DEFAULT_VID_PERMISSION, + VPSP_OP_GET_DEFAULT_VID_PERMISSION, + VPSP_OP_SET_GPA, ++ VPSP_OP_BACKUP_KEY, ++ VPSP_OP_RESTORE_KEY, ++ VPSP_OP_BACKUP_CTX, ++ VPSP_OP_RESTORE_CTX, + }; + ++typedef struct key_img_ctl { ++ unsigned int img_len; ++ void *key_img_ptr; ++} __attribute__ ((packed)) key_img_ctl_t; ++ ++typedef struct cmd_ctx_ctl { ++ unsigned int buffer_len; ++ void *cmd_ctx_ptr; ++} __attribute__ ((packed)) cmd_ctx_ctl_t; ++ + struct psp_dev_ctrl { + unsigned char op; + unsigned char resv[3]; +@@ -70,10 +118,212 @@ struct psp_dev_ctrl { + uint64_t gpa_start; + uint64_t gpa_end; + } gpa; ++ key_img_ctl_t key_img_ctl; ++ cmd_ctx_ctl_t cmd_ctx_ctl; + unsigned char reserved[128]; + } __attribute__ ((packed)) data; + }; + ++static int vpsp_dev_backup_key_img(struct PSPDevState *state) ++{ ++ int ret = 0; ++ uint32_t img_buffer_len = 0; ++ struct psp_dev_ctrl ctrl = { 0 }; ++ ++ if (state && state->dev_fd) { ++ if (state->enabled && state->vid) { ++ ctrl.op = VPSP_OP_BACKUP_KEY; ++ ++ // get actual key image buffer length ++ ctrl.data.key_img_ctl.img_len = 0; ++ if (ioctl(state->dev_fd, PSP_IOC_VPSP_OPT, &ctrl) < 0) { ++ error_report("ioctl VPSP_OP_BACKUP_KEY: %d", -errno); ++ return -1; ++ } ++ ++ img_buffer_len = ctrl.data.key_img_ctl.img_len; ++ // no key images need to migrate, but it unlikely ++ if (img_buffer_len == 0) ++ return 0; ++ ++ // free last key images, it's unlikely ++ if (state->key_img) ++ g_free(state->key_img); ++ ++ state->key_img = g_malloc0(img_buffer_len); ++ if (!state->key_img) { ++ error_report("g_malloc0 failed: %d", -errno); ++ return -1; ++ } ++ ++ // get key images backup buffer ++ ctrl.data.key_img_ctl.img_len = img_buffer_len; ++ ctrl.data.key_img_ctl.key_img_ptr = state->key_img; ++ ret = ioctl(state->dev_fd, PSP_IOC_VPSP_OPT, &ctrl); ++ if (ret < 0) { ++ error_report("ioctl VPSP_OP_BACKUP_KEY: %d", -errno); ++ goto end; ++ } ++ ++ state->img_len = ctrl.data.key_img_ctl.img_len; ++ } ++ } ++ ++ ret = 0; ++end: ++ return ret; ++} ++ ++static int vpsp_dev_restore_key_img(struct PSPDevState *state) ++{ ++ int ret = 0; ++ struct psp_dev_ctrl ctrl = { 0 }; ++ ++ if (state && state->dev_fd) { ++ if (state->enabled && state->vid && state->img_len) { ++ if (!state->key_img) { ++ error_report("PSPDevState load invalid, key_img is null\n"); ++ return -1; ++ } ++ ++ ctrl.op = VPSP_OP_RESTORE_KEY; ++ ctrl.data.key_img_ctl.img_len = state->img_len; ++ ctrl.data.key_img_ctl.key_img_ptr = state->key_img; ++ if (ioctl(state->dev_fd, PSP_IOC_VPSP_OPT, &ctrl) < 0) { ++ error_report("ioctl PSP_IOC_VPSP_OPT: %d", -errno); ++ return -1; ++ } ++ ++ // release key_img buffer, for migrate again ++ g_free(state->key_img); ++ state->key_img = NULL; ++ state->img_len = 0; ++ } ++ } ++ ++ return ret; ++} ++ ++static int vpsp_dev_backup_cmd_ctx(struct PSPDevState *state) ++{ ++ int ret = 0; ++ uint32_t buffer_len = 0; ++ struct psp_dev_ctrl ctrl = { 0 }; ++ ++ if (state && state->dev_fd) { ++ if (state->enabled && state->vid) { ++ ctrl.op = VPSP_OP_BACKUP_CTX; ++ ++ // get actual cmd context serialization buffer length ++ ctrl.data.cmd_ctx_ctl.buffer_len = 0; ++ if (ioctl(state->dev_fd, PSP_IOC_VPSP_OPT, &ctrl) < 0) { ++ error_report("ioctl VPSP_OP_BACKUP_CTX: %d", -errno); ++ return -1; ++ } ++ ++ buffer_len = ctrl.data.cmd_ctx_ctl.buffer_len; ++ // no cmd ctx need to migrate ++ if (buffer_len == 0) ++ return 0; ++ ++ // free last cmd_ctx buffer, it's unlikely ++ if (state->cmd_ctx) ++ g_free(state->cmd_ctx); ++ ++ state->cmd_ctx = g_malloc0(buffer_len); ++ if (!state->cmd_ctx) { ++ error_report("g_malloc0 failed: %d", -errno); ++ return -1; ++ } ++ ++ ctrl.data.cmd_ctx_ctl.buffer_len = buffer_len; ++ ctrl.data.cmd_ctx_ctl.cmd_ctx_ptr = state->cmd_ctx; ++ ret = ioctl(state->dev_fd, PSP_IOC_VPSP_OPT, &ctrl); ++ if (ret < 0) { ++ error_report("ioctl VPSP_OP_BACKUP_CTX: %d", -errno); ++ goto end; ++ } ++ state->ctx_len = ctrl.data.cmd_ctx_ctl.buffer_len; ++ } ++ } ++ ++ ret = 0; ++end: ++ return ret; ++} ++ ++static int vpsp_dev_restore_cmd_ctx(struct PSPDevState *state) ++{ ++ int ret = 0; ++ struct psp_dev_ctrl ctrl = { 0 }; ++ ++ if (state && state->dev_fd) { ++ if (state->enabled && state->vid && state->ctx_len) { ++ if (!state->cmd_ctx) { ++ error_report("PSPDevState load invalid, cmd_ctx is null\n"); ++ return -1; ++ } ++ ++ ctrl.op = VPSP_OP_RESTORE_CTX; ++ ctrl.data.cmd_ctx_ctl.buffer_len = state->ctx_len; ++ ctrl.data.cmd_ctx_ctl.cmd_ctx_ptr = state->cmd_ctx; ++ ++ if (ioctl(state->dev_fd, PSP_IOC_VPSP_OPT, &ctrl) < 0) { ++ error_report("ioctl PSP_IOC_VPSP_OPT: %d", -errno); ++ return -1; ++ } ++ ++ // release cmd ctx buffer, for migrate again ++ g_free(state->cmd_ctx); ++ state->cmd_ctx = NULL; ++ state->ctx_len = 0; ++ } ++ } ++ ++ return ret; ++} ++ ++static int vpsp_dev_pre_save(void *opaque) ++{ ++ int ret = 0; ++ struct PSPDevState *state = opaque; ++ ++ /** ++ * Back up the key image in the final stage ++ * to ensure the key image is up-to-date ++ */ ++ ret = vpsp_dev_backup_key_img(opaque); ++ if (ret) ++ goto end; ++ ++end: ++ if (ret && state->key_img) { ++ g_free(state->key_img); ++ state->key_img = NULL; ++ state->img_len = 0; ++ } ++ return ret; ++} ++ ++static int vpsp_dev_post_load(void *opaque, int version_id) ++{ ++ int ret = 0; ++ ++ /** ++ * During load, there are no sequencing requirements ++ * between restore of the key image and cmd_ctx ++ */ ++ ret = vpsp_dev_restore_cmd_ctx(opaque); ++ if (ret) ++ return ret; ++ ++ ret = vpsp_dev_restore_key_img(opaque); ++ if (ret) ++ return ret; ++ ++ return ret; ++} ++ + static MemoryRegion *find_memory_region_by_name(MemoryRegion *root, const char *name) { + MemoryRegion *subregion; + MemoryRegion *result; +@@ -91,6 +341,47 @@ static MemoryRegion *find_memory_region_by_name(MemoryRegion *root, const char * + return NULL; + } + ++static int precopy_state_notifier(NotifierWithReturn *notifier, void *data) ++{ ++ int ret = 0, i; ++ PrecopyNotifyData *pnd = data; ++ char mr_name[128] = {0}; ++ MemoryRegion *find_mr = NULL; ++ PSPDevState *state = container_of(notifier, PSPDevState, precopy_notifier); ++ ++ if (pnd->reason != PRECOPY_NOTIFY_COMPLETE) ++ goto end; ++ ++ /** ++ * The host kernel will then check each cmd_ctx ++ * to confirm all cmd_ctx are completed. ++ */ ++ ret = vpsp_dev_backup_cmd_ctx(state); ++ if (ret) ++ goto end; ++ ++ for (i = 0 ; i < state->hp_num; ++i) { ++ sprintf(mr_name, "mem2-%d", i); ++ find_mr = find_memory_region_by_name(get_system_memory(), mr_name); ++ if (!find_mr) { ++ error_report("fail to find memory region by name %s.", mr_name); ++ ret = -ENOMEM; ++ goto end; ++ } ++ ++ /* ensure mem2 memoryregion is migrated during downtime */ ++ memory_region_set_dirty(find_mr, 0, HUGEPAGE_SIZE); ++ } ++ ++end: ++ if (ret && state->cmd_ctx) { ++ g_free(state->cmd_ctx); ++ state->cmd_ctx = NULL; ++ state->ctx_len = 0; ++ } ++ return ret; ++} ++ + static int pin_user_hugepage(int fd, uint64_t vaddr) + { + int ret; +@@ -269,6 +560,9 @@ static void psp_dev_realize(DeviceState *dev, Error **errp) + state->shutdown_notifier.notify = psp_dev_shutdown_notify; + qemu_register_shutdown_notifier(&state->shutdown_notifier); + ++ state->precopy_notifier.notify = precopy_state_notifier; ++ precopy_add_notifier(&state->precopy_notifier); ++ + return; + del_vid: + ctrl.op = VPSP_OP_VID_DEL; +@@ -288,6 +582,8 @@ static void psp_dev_class_init(ObjectClass *klass, void *data) + + dc->desc = "PSP Device"; + dc->realize = psp_dev_realize; ++ dc->vmsd = &vmstate_vpsp_dev; ++ + set_bit(DEVICE_CATEGORY_MISC, dc->categories); + device_class_set_props(dc, psp_dev_properties); + } +-- +2.36.6 + diff --git a/qemu-kvm.spec b/qemu-kvm.spec index fa0ce4809eaab7724a8c989d3cd4fb1a73dd2db8..b011ee184230080f9f95eb3e5919ebb2d93c3d45 100644 --- a/qemu-kvm.spec +++ b/qemu-kvm.spec @@ -92,7 +92,7 @@ Obsoletes: %1-rhev <= %{epoch}:%{version}-%{release} Summary: QEMU is a machine emulator and virtualizer Name: qemu-kvm Version: 6.2.0 -Release: 53%{?rcrel}%{anolis_release}%{?dist}.6 +Release: 53%{?rcrel}%{anolis_release}%{?dist}.7 # Epoch because we pushed a qemu-1.0 package. AIUI this can't ever be dropped Epoch: 15 License: GPLv2 and GPLv2+ and CC-BY @@ -1089,6 +1089,9 @@ Patch1166: 1166-target-i386-Add-new-CPU-model-ClearwaterForest.patch Patch1167: 1167-docs-Add-GNR-SRF-and-CWF-CPU-models.patch Patch1168: 1168-target-i386-add-sha512-sm3-sm4-feature-bits.patch +# Support Hygon TKM (Trusted Key Management) Guest live migration +Patch1169: hw-misc-psp-support-live-migrate-for-vpsp-device.patch + BuildRequires: wget BuildRequires: rpm-build BuildRequires: ninja-build @@ -2326,6 +2329,10 @@ sh %{_sysconfdir}/sysconfig/modules/kvm.modules &> /dev/null || : %endif %changelog +* Thu Oct 09 2025 Mengbiao Xiong +- Support Hygon TKM (Trusted Key Management) Guest live migration +- hw-misc-psp-support-live-migrate-for-vpsp-device.patch + * Tue Sep 16 2025 Quanxian Wang - 6.2.0-53.0.8.6 - Clearwater Forrest(CWF) Support - 1164-target-i386-Introduce-SierraForest-v2-model.patch