* Tue Dec 09 2025 Jon Maloy <jmaloy@redhat.com> - 10.1.0-9

- kvm-pcie_sriov-make-pcie_sriov_pf_exit-safe-on-non-SR-IO.patch [RHEL-131144]
- kvm-accel-Add-Meson-and-config-support-for-MSHV-accelera.patch [RHEL-132193]
- kvm-target-i386-emulate-Allow-instruction-decoding-from-.patch [RHEL-132193]
- kvm-target-i386-mshv-Add-x86-decoder-emu-implementation.patch [RHEL-132193]
- kvm-hw-intc-Generalize-APIC-helper-names-from-kvm_-to-ac.patch [RHEL-132193]
- kvm-include-hw-hyperv-Add-MSHV-ABI-header-definitions.patch [RHEL-132193]
- kvm-linux-headers-linux-Add-mshv.h-headers.patch [RHEL-132193]
- kvm-accel-mshv-Add-accelerator-skeleton.patch [RHEL-132193]
- kvm-accel-mshv-Register-memory-region-listeners.patch [RHEL-132193]
- kvm-accel-mshv-Initialize-VM-partition.patch [RHEL-132193]
- kvm-accel-mshv-Add-vCPU-creation-and-execution-loop.patch [RHEL-132193]
- kvm-treewide-rename-qemu_wait_io_event-qemu_wait_io_even.patch [RHEL-132193]
- kvm-accel-mshv-Add-vCPU-signal-handling.patch [RHEL-132193]
- kvm-target-i386-mshv-Add-CPU-create-and-remove-logic.patch [RHEL-132193]
- kvm-target-i386-mshv-Implement-mshv_store_regs.patch [RHEL-132193]
- kvm-target-i386-mshv-Implement-mshv_get_standard_regs.patch [RHEL-132193]
- kvm-target-i386-mshv-Implement-mshv_get_special_regs.patch [RHEL-132193]
- kvm-target-i386-mshv-Implement-mshv_arch_put_registers.patch [RHEL-132193]
- kvm-target-i386-mshv-Set-local-interrupt-controller-stat.patch [RHEL-132193]
- kvm-target-i386-mshv-Register-CPUID-entries-with-MSHV.patch [RHEL-132193]
- kvm-target-i386-mshv-Register-MSRs-with-MSHV.patch [RHEL-132193]
- kvm-target-i386-mshv-Integrate-x86-instruction-decoder-e.patch [RHEL-132193]
- kvm-target-i386-mshv-Write-MSRs-to-the-hypervisor.patch [RHEL-132193]
- kvm-target-i386-mshv-Implement-mshv_vcpu_run.patch [RHEL-132193]
- kvm-accel-mshv-Handle-overlapping-mem-mappings.patch [RHEL-132193]
- kvm-qapi-accel-Allow-to-query-mshv-capabilities.patch [RHEL-132193]
- kvm-target-i386-mshv-Use-preallocated-page-for-hvcall.patch [RHEL-132193]
- kvm-docs-Add-mshv-to-documentation.patch [RHEL-132193]
- kvm-MAINTAINERS-Add-maintainers-for-mshv-accelerator.patch [RHEL-132193]
- kvm-accel-mshv-initialize-thread-name.patch [RHEL-132193]
- kvm-accel-mshv-use-return-value-of-handle_pio_str_read.patch [RHEL-132193]
- Resolves: RHEL-131144
  (qemu crash after hot-unplug disk from the multifunction enabled bus [RHEL.9.8])
- Resolves: RHEL-132193
  ([rhel 9.8]L1VH qemu downstream initial merge RHEL9)
This commit is contained in:
Jon Maloy 2025-12-09 23:04:44 -05:00
parent 4de7fac8a3
commit 4ba0b5c287
32 changed files with 9462 additions and 1 deletions

View File

@ -0,0 +1,54 @@
From 0fdaad5ececfa48df564eaf8ee08fdb76a08e5c5 Mon Sep 17 00:00:00 2001
From: Magnus Kulke <magnuskulke@linux.microsoft.com>
Date: Tue, 16 Sep 2025 18:48:47 +0200
Subject: [PATCH 29/31] MAINTAINERS: Add maintainers for mshv accelerator
RH-Author: Igor Mammedov <imammedo@redhat.com>
RH-MergeRequest: 435: x86: enablement for Azure L1VH OCP readiness
RH-Jira: RHEL-132193
RH-Acked-by: Vitaly Kuznetsov <vkuznets@redhat.com>
RH-Acked-by: Miroslav Rezanina <mrezanin@redhat.com>
RH-Commit: [28/30] 8ddf4160575605b6d6e4d8490f61dffe826d5acc
Adding Magnus Kulke and Wei Liu to the maintainers file for the
respective folders/files.
Signed-off-by: Magnus Kulke <magnuskulke@linux.microsoft.com>
Link: https://lore.kernel.org/r/20250916164847.77883-28-magnuskulke@linux.microsoft.com
[Rename "MAHV CPUs" to mention x86. - Paolo]
Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
(cherry picked from commit 1872bd9f2dad5113b0c27d352ec49683e0953c7f)
Signed-off-by: Igor Mammedov <imammedo@redhat.com>
---
MAINTAINERS | 15 +++++++++++++++
1 file changed, 15 insertions(+)
diff --git a/MAINTAINERS b/MAINTAINERS
index a07086ed76..d4696f00d7 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -546,6 +546,21 @@ F: target/i386/whpx/
F: accel/stubs/whpx-stub.c
F: include/system/whpx.h
+MSHV
+M: Magnus Kulke <magnus.kulke@linux.microsoft.com>
+R: Wei Liu <wei.liu@kernel.org>
+S: Supported
+F: accel/mshv/
+F: include/system/mshv.h
+F: include/hw/hyperv/hvgdk*.h
+F: include/hw/hyperv/hvhdk*.h
+
+X86 MSHV CPUs
+M: Magnus Kulke <magnus.kulke@linux.microsoft.com>
+R: Wei Liu <wei.liu@kernel.org>
+S: Supported
+F: target/i386/mshv/
+
X86 Instruction Emulator
M: Cameron Esfahani <dirty@apple.com>
M: Roman Bolshakov <rbolshakov@ddn.com>
--
2.51.1

View File

@ -0,0 +1,131 @@
From 566ceb746665ec2729791f979f9a4771f299c564 Mon Sep 17 00:00:00 2001
From: Magnus Kulke <magnuskulke@linux.microsoft.com>
Date: Tue, 16 Sep 2025 18:48:21 +0200
Subject: [PATCH 02/31] accel: Add Meson and config support for MSHV
accelerator
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
RH-Author: Igor Mammedov <imammedo@redhat.com>
RH-MergeRequest: 435: x86: enablement for Azure L1VH OCP readiness
RH-Jira: RHEL-132193
RH-Acked-by: Vitaly Kuznetsov <vkuznets@redhat.com>
RH-Acked-by: Miroslav Rezanina <mrezanin@redhat.com>
RH-Commit: [1/30] f47b1d42871811bead1e3430b18694284d70a534
Introduce a Meson feature option and default-config entry to allow
building QEMU with MSHV (Microsoft Hypervisor) acceleration support.
This is the first step toward implementing an MSHV backend in QEMU.
Signed-off-by: Magnus Kulke <magnuskulke@linux.microsoft.com>
Reviewed-by: Daniel P. Berrangé <berrange@redhat.com>
Link: https://lore.kernel.org/r/20250916164847.77883-2-magnuskulke@linux.microsoft.com
[Add error for unavailable accelerator. - Paolo]
Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
(cherry picked from commit 37e12da5df8eb74042f11e9e7bec8a50b8090adb)
Signed-off-by: Igor Mammedov <imammedo@redhat.com>
---
accel/Kconfig | 3 +++
meson.build | 13 +++++++++++++
meson_options.txt | 2 ++
scripts/meson-buildoptions.sh | 3 +++
4 files changed, 21 insertions(+)
diff --git a/accel/Kconfig b/accel/Kconfig
index 4263cab722..a60f114923 100644
--- a/accel/Kconfig
+++ b/accel/Kconfig
@@ -13,6 +13,9 @@ config TCG
config KVM
bool
+config MSHV
+ bool
+
config XEN
bool
select FSDEV_9P if VIRTFS
diff --git a/meson.build b/meson.build
index ef2e5be6e2..96254f8075 100644
--- a/meson.build
+++ b/meson.build
@@ -334,6 +334,7 @@ elif cpu == 'x86_64'
'CONFIG_HVF': ['x86_64-softmmu'],
'CONFIG_NVMM': ['i386-softmmu', 'x86_64-softmmu'],
'CONFIG_WHPX': ['i386-softmmu', 'x86_64-softmmu'],
+ 'CONFIG_MSHV': ['x86_64-softmmu'],
}
endif
@@ -884,6 +885,14 @@ accelerators = []
if get_option('kvm').allowed() and host_os == 'linux'
accelerators += 'CONFIG_KVM'
endif
+
+if get_option('mshv').allowed() and host_os == 'linux'
+ if get_option('mshv').enabled() and host_machine.cpu() != 'x86_64'
+ error('mshv accelerator requires x64_64 host')
+ endif
+ accelerators += 'CONFIG_MSHV'
+endif
+
if get_option('whpx').allowed() and host_os == 'windows'
if get_option('whpx').enabled() and host_machine.cpu() != 'x86_64'
error('WHPX requires 64-bit host')
@@ -953,6 +962,9 @@ endif
if 'CONFIG_WHPX' not in accelerators and get_option('whpx').enabled()
error('WHPX not available on this platform')
endif
+if 'CONFIG_MSHV' not in accelerators and get_option('mshv').enabled()
+ error('mshv not available on this platform')
+endif
xen = not_found
if get_option('xen').enabled() or (get_option('xen').auto() and have_system)
@@ -4821,6 +4833,7 @@ if have_system
summary_info += {'HVF support': config_all_accel.has_key('CONFIG_HVF')}
summary_info += {'WHPX support': config_all_accel.has_key('CONFIG_WHPX')}
summary_info += {'NVMM support': config_all_accel.has_key('CONFIG_NVMM')}
+ summary_info += {'MSHV support': config_all_accel.has_key('CONFIG_MSHV')}
summary_info += {'Xen support': xen.found()}
if xen.found()
summary_info += {'xen ctrl version': xen.version()}
diff --git a/meson_options.txt b/meson_options.txt
index f45d7ded45..2267dee8a0 100644
--- a/meson_options.txt
+++ b/meson_options.txt
@@ -73,6 +73,8 @@ option('malloc', type : 'combo', choices : ['system', 'tcmalloc', 'jemalloc'],
option('kvm', type: 'feature', value: 'auto',
description: 'KVM acceleration support')
+option('mshv', type: 'feature', value: 'auto',
+ description: 'MSHV acceleration support')
option('whpx', type: 'feature', value: 'auto',
description: 'WHPX acceleration support')
option('hvf', type: 'feature', value: 'auto',
diff --git a/scripts/meson-buildoptions.sh b/scripts/meson-buildoptions.sh
index 4146dbc88d..9aff126c28 100644
--- a/scripts/meson-buildoptions.sh
+++ b/scripts/meson-buildoptions.sh
@@ -155,6 +155,7 @@ meson_options_help() {
printf "%s\n" ' membarrier membarrier system call (for Linux 4.14+ or Windows'
printf "%s\n" ' modules modules support (non Windows)'
printf "%s\n" ' mpath Multipath persistent reservation passthrough'
+ printf "%s\n" ' mshv MSHV acceleration support'
printf "%s\n" ' multiprocess Out of process device emulation support'
printf "%s\n" ' netmap netmap network backend support'
printf "%s\n" ' nettle nettle cryptography support'
@@ -409,6 +410,8 @@ _meson_option_parse() {
--disable-modules) printf "%s" -Dmodules=disabled ;;
--enable-mpath) printf "%s" -Dmpath=enabled ;;
--disable-mpath) printf "%s" -Dmpath=disabled ;;
+ --enable-mshv) printf "%s" -Dmshv=enabled ;;
+ --disable-mshv) printf "%s" -Dmshv=disabled ;;
--enable-multiprocess) printf "%s" -Dmultiprocess=enabled ;;
--disable-multiprocess) printf "%s" -Dmultiprocess=disabled ;;
--enable-netmap) printf "%s" -Dnetmap=enabled ;;
--
2.51.1

View File

@ -0,0 +1,291 @@
From 185d76c5267fb97eb51df160b212cded1fb72e86 Mon Sep 17 00:00:00 2001
From: Magnus Kulke <magnuskulke@linux.microsoft.com>
Date: Thu, 2 Oct 2025 18:25:02 +0200
Subject: [PATCH 08/31] accel/mshv: Add accelerator skeleton
RH-Author: Igor Mammedov <imammedo@redhat.com>
RH-MergeRequest: 435: x86: enablement for Azure L1VH OCP readiness
RH-Jira: RHEL-132193
RH-Acked-by: Vitaly Kuznetsov <vkuznets@redhat.com>
RH-Acked-by: Miroslav Rezanina <mrezanin@redhat.com>
RH-Commit: [7/30] 3490738c5fd68ed2d66f1ab5599252cbbc76ef5f
Introduce the initial scaffold for the MSHV (Microsoft Hypervisor)
accelerator backend. This includes the basic directory structure and
stub implementations needed to integrate with QEMU's accelerator
framework.
Signed-off-by: Magnus Kulke <magnuskulke@linux.microsoft.com>
Link: https://lore.kernel.org/r/20250916164847.77883-8-magnuskulke@linux.microsoft.com
[Move include of linux/mshv.h in the per-target section; create
include/system/mshv_int.h. - Paolo]
Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
(cherry picked from commit d0d2918f968c55628e17e2733b799fcefb50f16b)
Signed-off-by: Igor Mammedov <imammedo@redhat.com>
---
accel/meson.build | 1 +
accel/mshv/meson.build | 6 ++
accel/mshv/mshv-all.c | 144 ++++++++++++++++++++++++++++++++++++++
include/system/mshv.h | 12 ++++
include/system/mshv_int.h | 41 +++++++++++
5 files changed, 204 insertions(+)
create mode 100644 accel/mshv/meson.build
create mode 100644 accel/mshv/mshv-all.c
create mode 100644 include/system/mshv_int.h
diff --git a/accel/meson.build b/accel/meson.build
index 6349efe682..983dfd0bd5 100644
--- a/accel/meson.build
+++ b/accel/meson.build
@@ -10,6 +10,7 @@ if have_system
subdir('kvm')
subdir('xen')
subdir('stubs')
+ subdir('mshv')
endif
# qtest
diff --git a/accel/mshv/meson.build b/accel/mshv/meson.build
new file mode 100644
index 0000000000..4c03ac7921
--- /dev/null
+++ b/accel/mshv/meson.build
@@ -0,0 +1,6 @@
+mshv_ss = ss.source_set()
+mshv_ss.add(if_true: files(
+ 'mshv-all.c'
+))
+
+specific_ss.add_all(when: 'CONFIG_MSHV', if_true: mshv_ss)
diff --git a/accel/mshv/mshv-all.c b/accel/mshv/mshv-all.c
new file mode 100644
index 0000000000..ae12f0f58b
--- /dev/null
+++ b/accel/mshv/mshv-all.c
@@ -0,0 +1,144 @@
+/*
+ * QEMU MSHV support
+ *
+ * Copyright Microsoft, Corp. 2025
+ *
+ * Authors:
+ * Ziqiao Zhou <ziqiaozhou@microsoft.com>
+ * Magnus Kulke <magnuskulke@microsoft.com>
+ * Jinank Jain <jinankjain@microsoft.com>
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ *
+ */
+
+#include "qemu/osdep.h"
+#include "qapi/error.h"
+#include "qemu/error-report.h"
+#include "qemu/event_notifier.h"
+#include "qemu/module.h"
+#include "qemu/main-loop.h"
+#include "hw/boards.h"
+
+#include "hw/hyperv/hvhdk.h"
+#include "hw/hyperv/hvhdk_mini.h"
+#include "hw/hyperv/hvgdk.h"
+#include "linux/mshv.h"
+
+#include "qemu/accel.h"
+#include "qemu/guest-random.h"
+#include "accel/accel-ops.h"
+#include "accel/accel-cpu-ops.h"
+#include "system/cpus.h"
+#include "system/runstate.h"
+#include "system/accel-blocker.h"
+#include "system/address-spaces.h"
+#include "system/mshv.h"
+#include "system/mshv_int.h"
+#include "system/reset.h"
+#include "trace.h"
+#include <err.h>
+#include <stdint.h>
+#include <sys/ioctl.h>
+
+#define TYPE_MSHV_ACCEL ACCEL_CLASS_NAME("mshv")
+
+DECLARE_INSTANCE_CHECKER(MshvState, MSHV_STATE, TYPE_MSHV_ACCEL)
+
+bool mshv_allowed;
+
+MshvState *mshv_state;
+
+static int mshv_init(AccelState *as, MachineState *ms)
+{
+ error_report("unimplemented");
+ abort();
+}
+
+static void mshv_start_vcpu_thread(CPUState *cpu)
+{
+ error_report("unimplemented");
+ abort();
+}
+
+static void mshv_cpu_synchronize_post_init(CPUState *cpu)
+{
+ error_report("unimplemented");
+ abort();
+}
+
+static void mshv_cpu_synchronize_post_reset(CPUState *cpu)
+{
+ error_report("unimplemented");
+ abort();
+}
+
+static void mshv_cpu_synchronize_pre_loadvm(CPUState *cpu)
+{
+ error_report("unimplemented");
+ abort();
+}
+
+static void mshv_cpu_synchronize(CPUState *cpu)
+{
+ error_report("unimplemented");
+ abort();
+}
+
+static bool mshv_cpus_are_resettable(void)
+{
+ error_report("unimplemented");
+ abort();
+}
+
+static void mshv_accel_class_init(ObjectClass *oc, const void *data)
+{
+ AccelClass *ac = ACCEL_CLASS(oc);
+
+ ac->name = "MSHV";
+ ac->init_machine = mshv_init;
+ ac->allowed = &mshv_allowed;
+}
+
+static void mshv_accel_instance_init(Object *obj)
+{
+ MshvState *s = MSHV_STATE(obj);
+
+ s->vm = 0;
+}
+
+static const TypeInfo mshv_accel_type = {
+ .name = TYPE_MSHV_ACCEL,
+ .parent = TYPE_ACCEL,
+ .instance_init = mshv_accel_instance_init,
+ .class_init = mshv_accel_class_init,
+ .instance_size = sizeof(MshvState),
+};
+
+static void mshv_accel_ops_class_init(ObjectClass *oc, const void *data)
+{
+ AccelOpsClass *ops = ACCEL_OPS_CLASS(oc);
+
+ ops->create_vcpu_thread = mshv_start_vcpu_thread;
+ ops->synchronize_post_init = mshv_cpu_synchronize_post_init;
+ ops->synchronize_post_reset = mshv_cpu_synchronize_post_reset;
+ ops->synchronize_state = mshv_cpu_synchronize;
+ ops->synchronize_pre_loadvm = mshv_cpu_synchronize_pre_loadvm;
+ ops->cpus_are_resettable = mshv_cpus_are_resettable;
+ ops->handle_interrupt = generic_handle_interrupt;
+}
+
+static const TypeInfo mshv_accel_ops_type = {
+ .name = ACCEL_OPS_NAME("mshv"),
+ .parent = TYPE_ACCEL_OPS,
+ .class_init = mshv_accel_ops_class_init,
+ .abstract = true,
+};
+
+static void mshv_type_init(void)
+{
+ type_register_static(&mshv_accel_type);
+ type_register_static(&mshv_accel_ops_type);
+}
+
+type_init(mshv_type_init);
diff --git a/include/system/mshv.h b/include/system/mshv.h
index 2a504ed81f..434ea9682e 100644
--- a/include/system/mshv.h
+++ b/include/system/mshv.h
@@ -14,8 +14,17 @@
#ifndef QEMU_MSHV_H
#define QEMU_MSHV_H
+#include "qemu/osdep.h"
+#include "qemu/accel.h"
+#include "hw/hyperv/hyperv-proto.h"
+#include "hw/hyperv/hvhdk.h"
+#include "qapi/qapi-types-common.h"
+#include "system/memory.h"
+#include "accel/accel-ops.h"
+
#ifdef COMPILING_PER_TARGET
#ifdef CONFIG_MSHV
+#include <linux/mshv.h>
#define CONFIG_MSHV_IS_POSSIBLE
#endif
#else
@@ -30,6 +39,9 @@ extern bool mshv_allowed;
#endif
#define mshv_msi_via_irqfd_enabled() false
+typedef struct MshvState MshvState;
+extern MshvState *mshv_state;
+
/* interrupt */
int mshv_irqchip_add_msi_route(int vector, PCIDevice *dev);
int mshv_irqchip_update_msi_route(int virq, MSIMessage msg, PCIDevice *dev);
diff --git a/include/system/mshv_int.h b/include/system/mshv_int.h
new file mode 100644
index 0000000000..132491b599
--- /dev/null
+++ b/include/system/mshv_int.h
@@ -0,0 +1,41 @@
+/*
+ * QEMU MSHV support
+ *
+ * Copyright Microsoft, Corp. 2025
+ *
+ * Authors: Ziqiao Zhou <ziqiaozhou@microsoft.com>
+ * Magnus Kulke <magnuskulke@microsoft.com>
+ * Jinank Jain <jinankjain@microsoft.com>
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ *
+ */
+
+#ifndef QEMU_MSHV_INT_H
+#define QEMU_MSHV_INT_H
+
+struct AccelCPUState {
+ int cpufd;
+ bool dirty;
+};
+
+typedef struct MshvMemoryListener {
+ MemoryListener listener;
+ int as_id;
+} MshvMemoryListener;
+
+typedef struct MshvAddressSpace {
+ MshvMemoryListener *ml;
+ AddressSpace *as;
+} MshvAddressSpace;
+
+struct MshvState {
+ AccelState parent_obj;
+ int vm;
+ MshvMemoryListener memory_listener;
+ /* number of listeners */
+ int nr_as;
+ MshvAddressSpace *as;
+};
+
+#endif
--
2.51.1

View File

@ -0,0 +1,417 @@
From aa8010ec2f64396baffff491b32f31a44ca1b117 Mon Sep 17 00:00:00 2001
From: Magnus Kulke <magnuskulke@linux.microsoft.com>
Date: Tue, 16 Sep 2025 18:48:30 +0200
Subject: [PATCH 11/31] accel/mshv: Add vCPU creation and execution loop
RH-Author: Igor Mammedov <imammedo@redhat.com>
RH-MergeRequest: 435: x86: enablement for Azure L1VH OCP readiness
RH-Jira: RHEL-132193
RH-Acked-by: Vitaly Kuznetsov <vkuznets@redhat.com>
RH-Acked-by: Miroslav Rezanina <mrezanin@redhat.com>
RH-Commit: [10/30] 91f9d846d568c41fc2b03ffa2e120034b1ab1439
Create MSHV vCPUs using MSHV_CREATE_VP and initialize their state.
Register the MSHV CPU execution loop loop with the QEMU accelerator
framework to enable guest code execution.
The target/i386 functionality is still mostly stubbed out and will be
populated in a later commit in this series.
Signed-off-by: Magnus Kulke <magnuskulke@linux.microsoft.com>
Link: https://lore.kernel.org/r/20250916164847.77883-11-magnuskulke@linux.microsoft.com
[Fix g_free/g_clear_pointer confusion; rename qemu_wait_io_event;
mshv.h/mshv_int.h split. - Paolo]
Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
(cherry picked from commit 4dc5d4257259764b6fcd035870517fc4140d8962)
Signed-off-by: Igor Mammedov <imammedo@redhat.com>
---
accel/mshv/mshv-all.c | 186 +++++++++++++++++++++++++++++++++---
accel/mshv/trace-events | 2 +
include/system/mshv.h | 2 +-
include/system/mshv_int.h | 20 ++++
target/i386/mshv/mshv-cpu.c | 63 ++++++++++++
5 files changed, 260 insertions(+), 13 deletions(-)
diff --git a/accel/mshv/mshv-all.c b/accel/mshv/mshv-all.c
index 653195c57c..e02421d79d 100644
--- a/accel/mshv/mshv-all.c
+++ b/accel/mshv/mshv-all.c
@@ -393,6 +393,24 @@ int mshv_hvcall(int fd, const struct mshv_root_hvcall *args)
return ret;
}
+static int mshv_init_vcpu(CPUState *cpu)
+{
+ int vm_fd = mshv_state->vm;
+ uint8_t vp_index = cpu->cpu_index;
+ int ret;
+
+ mshv_arch_init_vcpu(cpu);
+ cpu->accel = g_new0(AccelCPUState, 1);
+
+ ret = mshv_create_vcpu(vm_fd, vp_index, &cpu->accel->cpufd);
+ if (ret < 0) {
+ return -1;
+ }
+
+ cpu->accel->dirty = true;
+
+ return 0;
+}
static int mshv_init(AccelState *as, MachineState *ms)
{
@@ -415,6 +433,8 @@ static int mshv_init(AccelState *as, MachineState *ms)
return -1;
}
+ mshv_init_mmio_emu();
+
mshv_init_msicontrol();
ret = create_vm(mshv_fd, &vm_fd);
@@ -444,40 +464,182 @@ static int mshv_init(AccelState *as, MachineState *ms)
return 0;
}
+static int mshv_destroy_vcpu(CPUState *cpu)
+{
+ int cpu_fd = mshv_vcpufd(cpu);
+ int vm_fd = mshv_state->vm;
+
+ mshv_remove_vcpu(vm_fd, cpu_fd);
+ mshv_vcpufd(cpu) = 0;
+
+ mshv_arch_destroy_vcpu(cpu);
+ g_clear_pointer(&cpu->accel, g_free);
+ return 0;
+}
+
+static int mshv_cpu_exec(CPUState *cpu)
+{
+ hv_message mshv_msg;
+ enum MshvVmExit exit_reason;
+ int ret = 0;
+
+ bql_unlock();
+ cpu_exec_start(cpu);
+
+ do {
+ if (cpu->accel->dirty) {
+ ret = mshv_arch_put_registers(cpu);
+ if (ret) {
+ error_report("Failed to put registers after init: %s",
+ strerror(-ret));
+ ret = -1;
+ break;
+ }
+ cpu->accel->dirty = false;
+ }
+
+ ret = mshv_run_vcpu(mshv_state->vm, cpu, &mshv_msg, &exit_reason);
+ if (ret < 0) {
+ error_report("Failed to run on vcpu %d", cpu->cpu_index);
+ abort();
+ }
+
+ switch (exit_reason) {
+ case MshvVmExitIgnore:
+ break;
+ default:
+ ret = EXCP_INTERRUPT;
+ break;
+ }
+ } while (ret == 0);
+
+ cpu_exec_end(cpu);
+ bql_lock();
+
+ if (ret < 0) {
+ cpu_dump_state(cpu, stderr, CPU_DUMP_CODE);
+ vm_stop(RUN_STATE_INTERNAL_ERROR);
+ }
+
+ return ret;
+}
+
+static void *mshv_vcpu_thread(void *arg)
+{
+ CPUState *cpu = arg;
+ int ret;
+
+ rcu_register_thread();
+
+ bql_lock();
+ qemu_thread_get_self(cpu->thread);
+ cpu->thread_id = qemu_get_thread_id();
+ current_cpu = cpu;
+ ret = mshv_init_vcpu(cpu);
+ if (ret < 0) {
+ error_report("Failed to init vcpu %d", cpu->cpu_index);
+ goto cleanup;
+ }
+
+ /* signal CPU creation */
+ cpu_thread_signal_created(cpu);
+ qemu_guest_random_seed_thread_part2(cpu->random_seed);
+
+ do {
+ qemu_process_cpu_events(cpu);
+ if (cpu_can_run(cpu)) {
+ mshv_cpu_exec(cpu);
+ }
+ } while (!cpu->unplug || cpu_can_run(cpu));
+
+ mshv_destroy_vcpu(cpu);
+cleanup:
+ cpu_thread_signal_destroyed(cpu);
+ bql_unlock();
+ rcu_unregister_thread();
+ return NULL;
+}
+
static void mshv_start_vcpu_thread(CPUState *cpu)
{
- error_report("unimplemented");
- abort();
+ char thread_name[VCPU_THREAD_NAME_SIZE];
+
+ cpu->thread = g_malloc0(sizeof(QemuThread));
+ cpu->halt_cond = g_malloc0(sizeof(QemuCond));
+
+ qemu_cond_init(cpu->halt_cond);
+
+ trace_mshv_start_vcpu_thread(thread_name, cpu->cpu_index);
+ qemu_thread_create(cpu->thread, thread_name, mshv_vcpu_thread, cpu,
+ QEMU_THREAD_JOINABLE);
+}
+
+static void do_mshv_cpu_synchronize_post_init(CPUState *cpu,
+ run_on_cpu_data arg)
+{
+ int ret = mshv_arch_put_registers(cpu);
+ if (ret < 0) {
+ error_report("Failed to put registers after init: %s", strerror(-ret));
+ abort();
+ }
+
+ cpu->accel->dirty = false;
}
static void mshv_cpu_synchronize_post_init(CPUState *cpu)
{
- error_report("unimplemented");
- abort();
+ run_on_cpu(cpu, do_mshv_cpu_synchronize_post_init, RUN_ON_CPU_NULL);
}
static void mshv_cpu_synchronize_post_reset(CPUState *cpu)
{
- error_report("unimplemented");
- abort();
+ int ret = mshv_arch_put_registers(cpu);
+ if (ret) {
+ error_report("Failed to put registers after reset: %s",
+ strerror(-ret));
+ cpu_dump_state(cpu, stderr, CPU_DUMP_CODE);
+ vm_stop(RUN_STATE_INTERNAL_ERROR);
+ }
+ cpu->accel->dirty = false;
+}
+
+static void do_mshv_cpu_synchronize_pre_loadvm(CPUState *cpu,
+ run_on_cpu_data arg)
+{
+ cpu->accel->dirty = true;
}
static void mshv_cpu_synchronize_pre_loadvm(CPUState *cpu)
{
- error_report("unimplemented");
- abort();
+ run_on_cpu(cpu, do_mshv_cpu_synchronize_pre_loadvm, RUN_ON_CPU_NULL);
+}
+
+static void do_mshv_cpu_synchronize(CPUState *cpu, run_on_cpu_data arg)
+{
+ if (!cpu->accel->dirty) {
+ int ret = mshv_load_regs(cpu);
+ if (ret < 0) {
+ error_report("Failed to load registers for vcpu %d",
+ cpu->cpu_index);
+
+ cpu_dump_state(cpu, stderr, CPU_DUMP_CODE);
+ vm_stop(RUN_STATE_INTERNAL_ERROR);
+ }
+
+ cpu->accel->dirty = true;
+ }
}
static void mshv_cpu_synchronize(CPUState *cpu)
{
- error_report("unimplemented");
- abort();
+ if (!cpu->accel->dirty) {
+ run_on_cpu(cpu, do_mshv_cpu_synchronize, RUN_ON_CPU_NULL);
+ }
}
static bool mshv_cpus_are_resettable(void)
{
- error_report("unimplemented");
- abort();
+ return false;
}
static void mshv_accel_class_init(ObjectClass *oc, const void *data)
diff --git a/accel/mshv/trace-events b/accel/mshv/trace-events
index 6130c4abf8..a4dffeb24a 100644
--- a/accel/mshv/trace-events
+++ b/accel/mshv/trace-events
@@ -3,6 +3,8 @@
#
# SPDX-License-Identifier: GPL-2.0-or-later
+mshv_start_vcpu_thread(const char* thread, uint32_t cpu) "thread=%s cpu_index=%d"
+
mshv_set_memory(bool add, uint64_t gpa, uint64_t size, uint64_t user_addr, bool readonly, int ret) "add=%d gpa=0x%" PRIx64 " size=0x%" PRIx64 " user=0x%" PRIx64 " readonly=%d result=%d"
mshv_mem_ioeventfd_add(uint64_t addr, uint32_t size, uint32_t data) "addr=0x%" PRIx64 " size=%d data=0x%x"
mshv_mem_ioeventfd_del(uint64_t addr, uint32_t size, uint32_t data) "addr=0x%" PRIx64 " size=%d data=0x%x"
diff --git a/include/system/mshv.h b/include/system/mshv.h
index 1011e81df4..bbc42f4dc3 100644
--- a/include/system/mshv.h
+++ b/include/system/mshv.h
@@ -41,7 +41,7 @@ extern bool mshv_allowed;
#define mshv_msi_via_irqfd_enabled() mshv_enabled()
#else /* CONFIG_MSHV_IS_POSSIBLE */
#define mshv_enabled() false
-#define mshv_msi_via_irqfd_enabled() false
+#define mshv_msi_via_irqfd_enabled() mshv_enabled()
#endif
typedef struct MshvState MshvState;
diff --git a/include/system/mshv_int.h b/include/system/mshv_int.h
index b36124a0ea..fb80f69772 100644
--- a/include/system/mshv_int.h
+++ b/include/system/mshv_int.h
@@ -14,6 +14,8 @@
#ifndef QEMU_MSHV_INT_H
#define QEMU_MSHV_INT_H
+typedef struct hyperv_message hv_message;
+
struct AccelCPUState {
int cpufd;
bool dirty;
@@ -44,6 +46,24 @@ typedef struct MshvMsiControl {
GHashTable *gsi_routes;
} MshvMsiControl;
+#define mshv_vcpufd(cpu) (cpu->accel->cpufd)
+
+/* cpu */
+typedef enum MshvVmExit {
+ MshvVmExitIgnore = 0,
+ MshvVmExitShutdown = 1,
+ MshvVmExitSpecial = 2,
+} MshvVmExit;
+
+void mshv_init_mmio_emu(void);
+int mshv_create_vcpu(int vm_fd, uint8_t vp_index, int *cpu_fd);
+void mshv_remove_vcpu(int vm_fd, int cpu_fd);
+int mshv_run_vcpu(int vm_fd, CPUState *cpu, hv_message *msg, MshvVmExit *exit);
+int mshv_load_regs(CPUState *cpu);
+int mshv_store_regs(CPUState *cpu);
+int mshv_arch_put_registers(const CPUState *cpu);
+void mshv_arch_init_vcpu(CPUState *cpu);
+void mshv_arch_destroy_vcpu(CPUState *cpu);
void mshv_arch_amend_proc_features(
union hv_partition_synthetic_processor_features *features);
int mshv_arch_post_init_vm(int vm_fd);
diff --git a/target/i386/mshv/mshv-cpu.c b/target/i386/mshv/mshv-cpu.c
index de0c26bc6c..02d71ebc14 100644
--- a/target/i386/mshv/mshv-cpu.c
+++ b/target/i386/mshv/mshv-cpu.c
@@ -22,15 +22,78 @@
#include "hw/hyperv/hvgdk_mini.h"
#include "hw/hyperv/hvhdk_mini.h"
+#include "cpu.h"
+#include "emulate/x86_decode.h"
+#include "emulate/x86_emu.h"
+#include "emulate/x86_flags.h"
+
#include "trace-accel_mshv.h"
#include "trace.h"
+int mshv_store_regs(CPUState *cpu)
+{
+ error_report("unimplemented");
+ abort();
+}
+
+int mshv_load_regs(CPUState *cpu)
+{
+ error_report("unimplemented");
+ abort();
+}
+
+int mshv_arch_put_registers(const CPUState *cpu)
+{
+ error_report("unimplemented");
+ abort();
+}
+
void mshv_arch_amend_proc_features(
union hv_partition_synthetic_processor_features *features)
{
features->access_guest_idle_reg = 1;
}
+int mshv_run_vcpu(int vm_fd, CPUState *cpu, hv_message *msg, MshvVmExit *exit)
+{
+ error_report("unimplemented");
+ abort();
+}
+
+void mshv_remove_vcpu(int vm_fd, int cpu_fd)
+{
+ error_report("unimplemented");
+ abort();
+}
+
+int mshv_create_vcpu(int vm_fd, uint8_t vp_index, int *cpu_fd)
+{
+ error_report("unimplemented");
+ abort();
+}
+
+void mshv_init_mmio_emu(void)
+{
+ error_report("unimplemented");
+ abort();
+}
+
+void mshv_arch_init_vcpu(CPUState *cpu)
+{
+ X86CPU *x86_cpu = X86_CPU(cpu);
+ CPUX86State *env = &x86_cpu->env;
+
+ env->emu_mmio_buf = g_new(char, 4096);
+}
+
+void mshv_arch_destroy_vcpu(CPUState *cpu)
+{
+ X86CPU *x86_cpu = X86_CPU(cpu);
+ CPUX86State *env = &x86_cpu->env;
+
+ g_clear_pointer(&env->emu_mmio_buf, g_free);
+}
+
/*
* Default Microsoft Hypervisor behavior for unimplemented MSR is to send a
* fault to the guest if it tries to access it. It is possible to override
--
2.51.1

View File

@ -0,0 +1,75 @@
From d8f03b683f887433bccf15bf012d94e130290414 Mon Sep 17 00:00:00 2001
From: Magnus Kulke <magnuskulke@linux.microsoft.com>
Date: Tue, 16 Sep 2025 18:48:31 +0200
Subject: [PATCH 13/31] accel/mshv: Add vCPU signal handling
RH-Author: Igor Mammedov <imammedo@redhat.com>
RH-MergeRequest: 435: x86: enablement for Azure L1VH OCP readiness
RH-Jira: RHEL-132193
RH-Acked-by: Vitaly Kuznetsov <vkuznets@redhat.com>
RH-Acked-by: Miroslav Rezanina <mrezanin@redhat.com>
RH-Commit: [12/30] 87dd1e952aae5893b29c3f74b804b2935c347001
Implement signal handling for MSHV vCPUs to support asynchronous
interrupts from the main thread.
Signed-off-by: Magnus Kulke <magnuskulke@linux.microsoft.com>
Link: https://lore.kernel.org/r/20250916164847.77883-12-magnuskulke@linux.microsoft.com
Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
(cherry picked from commit 575df4df54e060661db45e6f80293b29ea8901c1)
Signed-off-by: Igor Mammedov <imammedo@redhat.com>
---
accel/mshv/mshv-all.c | 30 ++++++++++++++++++++++++++++++
1 file changed, 30 insertions(+)
diff --git a/accel/mshv/mshv-all.c b/accel/mshv/mshv-all.c
index e02421d79d..fa1f8f35bd 100644
--- a/accel/mshv/mshv-all.c
+++ b/accel/mshv/mshv-all.c
@@ -524,6 +524,35 @@ static int mshv_cpu_exec(CPUState *cpu)
return ret;
}
+/*
+ * The signal handler is triggered when QEMU's main thread receives a SIG_IPI
+ * (SIGUSR1). This signal causes the current CPU thread to be kicked, forcing a
+ * VM exit on the CPU. The VM exit generates an exit reason that breaks the loop
+ * (see mshv_cpu_exec). If the exit is due to a Ctrl+A+x command, the system
+ * will shut down. For other cases, the system will continue running.
+ */
+static void sa_ipi_handler(int sig)
+{
+ /* TODO: call IOCTL to set_immediate_exit, once implemented. */
+
+ qemu_cpu_kick_self();
+}
+
+static void init_signal(CPUState *cpu)
+{
+ /* init cpu signals */
+ struct sigaction sigact;
+ sigset_t set;
+
+ memset(&sigact, 0, sizeof(sigact));
+ sigact.sa_handler = sa_ipi_handler;
+ sigaction(SIG_IPI, &sigact, NULL);
+
+ pthread_sigmask(SIG_BLOCK, NULL, &set);
+ sigdelset(&set, SIG_IPI);
+ pthread_sigmask(SIG_SETMASK, &set, NULL);
+}
+
static void *mshv_vcpu_thread(void *arg)
{
CPUState *cpu = arg;
@@ -540,6 +569,7 @@ static void *mshv_vcpu_thread(void *arg)
error_report("Failed to init vcpu %d", cpu->cpu_index);
goto cleanup;
}
+ init_signal(cpu);
/* signal CPU creation */
cpu_thread_signal_created(cpu);
--
2.51.1

View File

@ -0,0 +1,695 @@
From 1bbcbcfd11a90719b7a7eb25e8547b9d6d35b9f3 Mon Sep 17 00:00:00 2001
From: Magnus Kulke <magnuskulke@linux.microsoft.com>
Date: Tue, 16 Sep 2025 18:48:43 +0200
Subject: [PATCH 25/31] accel/mshv: Handle overlapping mem mappings
RH-Author: Igor Mammedov <imammedo@redhat.com>
RH-MergeRequest: 435: x86: enablement for Azure L1VH OCP readiness
RH-Jira: RHEL-132193
RH-Acked-by: Vitaly Kuznetsov <vkuznets@redhat.com>
RH-Acked-by: Miroslav Rezanina <mrezanin@redhat.com>
RH-Commit: [24/30] 957ca3735cd46783d0ca044b37ddbfa5303158f7
QEMU maps certain regions into the guest multiple times, as seen in the
trace below. Currently the MSHV kernel driver will reject those
mappings. To workaround this, a record is kept (a static global list of
"slots", inspired by what the HVF accelerator has implemented). An
overlapping region is not registered at the hypervisor, and marked as
mapped=false. If there is an UNMAPPED_GPA exit, we can look for a slot
that is unmapped and would cover the GPA. In this case we map out the
conflicting slot and map in the requested region.
mshv_set_phys_mem add=1 name=pc.bios
mshv_map_memory => u_a=7ffff4e00000 gpa=00fffc0000 size=00040000
mshv_set_phys_mem add=1 name=ioapic
mshv_set_phys_mem add=1 name=hpet
mshv_set_phys_mem add=0 name=pc.ram
mshv_unmap_memory u_a=7fff67e00000 gpa=0000000000 size=80000000
mshv_set_phys_mem add=1 name=pc.ram
mshv_map_memory u_a=7fff67e00000 gpa=0000000000 size=000c0000
mshv_set_phys_mem add=1 name=pc.rom
mshv_map_memory u_a=7ffff4c00000 gpa=00000c0000 size=00020000
mshv_set_phys_mem add=1 name=pc.bios
mshv_remap_attempt => u_a=7ffff4e20000 gpa=00000e0000 size=00020000
The mapping table is guarded by a mutex for concurrent modification and
RCU mechanisms for concurrent reads. Writes occur rarely, but we'll have
to verify whether an unmapped region exist for each UNMAPPED_GPA exit,
which happens frequently.
Signed-off-by: Magnus Kulke <magnuskulke@linux.microsoft.com>
Link: https://lore.kernel.org/r/20250916164847.77883-24-magnuskulke@linux.microsoft.com
[Fix format strings for trace-events; mshv.h/mshv_int.h split. - Paolo]
Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
(cherry picked from commit efc4093358511a58846a409b965213aa1bb9f31a)
Signed-off-by: Igor Mammedov <imammedo@redhat.com>
---
accel/mshv/mem.c | 406 +++++++++++++++++++++++++++++++++---
accel/mshv/mshv-all.c | 2 +
accel/mshv/trace-events | 5 +
include/system/mshv_int.h | 22 +-
target/i386/mshv/mshv-cpu.c | 43 ++++
5 files changed, 448 insertions(+), 30 deletions(-)
diff --git a/accel/mshv/mem.c b/accel/mshv/mem.c
index e55c38d4db..0e2164af3e 100644
--- a/accel/mshv/mem.c
+++ b/accel/mshv/mem.c
@@ -11,7 +11,9 @@
*/
#include "qemu/osdep.h"
+#include "qemu/lockable.h"
#include "qemu/error-report.h"
+#include "qemu/rcu.h"
#include "linux/mshv.h"
#include "system/address-spaces.h"
#include "system/mshv.h"
@@ -20,6 +22,137 @@
#include <sys/ioctl.h>
#include "trace.h"
+typedef struct SlotsRCUReclaim {
+ struct rcu_head rcu;
+ GList *old_head;
+ MshvMemorySlot *removed_slot;
+} SlotsRCUReclaim;
+
+static void rcu_reclaim_slotlist(struct rcu_head *rcu)
+{
+ SlotsRCUReclaim *r = container_of(rcu, SlotsRCUReclaim, rcu);
+ g_list_free(r->old_head);
+ g_free(r->removed_slot);
+ g_free(r);
+}
+
+static void publish_slots(GList *new_head, GList *old_head,
+ MshvMemorySlot *removed_slot)
+{
+ MshvMemorySlotManager *manager = &mshv_state->msm;
+
+ assert(manager);
+ qatomic_store_release(&manager->slots, new_head);
+
+ SlotsRCUReclaim *r = g_new(SlotsRCUReclaim, 1);
+ r->old_head = old_head;
+ r->removed_slot = removed_slot;
+
+ call_rcu1(&r->rcu, rcu_reclaim_slotlist);
+}
+
+/* Needs to be called with mshv_state->msm.mutex held */
+static int remove_slot(MshvMemorySlot *slot)
+{
+ GList *old_head, *new_head;
+ MshvMemorySlotManager *manager = &mshv_state->msm;
+
+ assert(manager);
+ old_head = qatomic_load_acquire(&manager->slots);
+
+ if (!g_list_find(old_head, slot)) {
+ error_report("slot requested for removal not found");
+ return -1;
+ }
+
+ new_head = g_list_copy(old_head);
+ new_head = g_list_remove(new_head, slot);
+ manager->n_slots--;
+
+ publish_slots(new_head, old_head, slot);
+
+ return 0;
+}
+
+/* Needs to be called with mshv_state->msm.mutex held */
+static MshvMemorySlot *append_slot(uint64_t gpa, uint64_t userspace_addr,
+ uint64_t size, bool readonly)
+{
+ GList *old_head, *new_head;
+ MshvMemorySlot *slot;
+ MshvMemorySlotManager *manager = &mshv_state->msm;
+
+ assert(manager);
+
+ old_head = qatomic_load_acquire(&manager->slots);
+
+ if (manager->n_slots >= MSHV_MAX_MEM_SLOTS) {
+ error_report("no free memory slots available");
+ return NULL;
+ }
+
+ slot = g_new0(MshvMemorySlot, 1);
+ slot->guest_phys_addr = gpa;
+ slot->userspace_addr = userspace_addr;
+ slot->memory_size = size;
+ slot->readonly = readonly;
+
+ new_head = g_list_copy(old_head);
+ new_head = g_list_append(new_head, slot);
+ manager->n_slots++;
+
+ publish_slots(new_head, old_head, NULL);
+
+ return slot;
+}
+
+static int slot_overlaps(const MshvMemorySlot *slot1,
+ const MshvMemorySlot *slot2)
+{
+ uint64_t start_1 = slot1->userspace_addr,
+ start_2 = slot2->userspace_addr;
+ size_t len_1 = slot1->memory_size,
+ len_2 = slot2->memory_size;
+
+ if (slot1 == slot2) {
+ return -1;
+ }
+
+ return ranges_overlap(start_1, len_1, start_2, len_2) ? 0 : -1;
+}
+
+static bool is_mapped(MshvMemorySlot *slot)
+{
+ /* Subsequent reads of mapped field see a fully-initialized slot */
+ return qatomic_load_acquire(&slot->mapped);
+}
+
+/*
+ * Find slot that is:
+ * - overlapping in userspace
+ * - currently mapped in the guest
+ *
+ * Needs to be called with mshv_state->msm.mutex or RCU read lock held.
+ */
+static MshvMemorySlot *find_overlap_mem_slot(GList *head, MshvMemorySlot *slot)
+{
+ GList *found;
+ MshvMemorySlot *overlap_slot;
+
+ found = g_list_find_custom(head, slot, (GCompareFunc) slot_overlaps);
+
+ if (!found) {
+ return NULL;
+ }
+
+ overlap_slot = found->data;
+ if (!overlap_slot || !is_mapped(overlap_slot)) {
+ return NULL;
+ }
+
+ return overlap_slot;
+}
+
static int set_guest_memory(int vm_fd,
const struct mshv_user_mem_region *region)
{
@@ -27,38 +160,169 @@ static int set_guest_memory(int vm_fd,
ret = ioctl(vm_fd, MSHV_SET_GUEST_MEMORY, region);
if (ret < 0) {
- error_report("failed to set guest memory");
- return -errno;
+ error_report("failed to set guest memory: %s", strerror(errno));
+ return -1;
}
return 0;
}
-static int map_or_unmap(int vm_fd, const MshvMemoryRegion *mr, bool map)
+static int map_or_unmap(int vm_fd, const MshvMemorySlot *slot, bool map)
{
struct mshv_user_mem_region region = {0};
- region.guest_pfn = mr->guest_phys_addr >> MSHV_PAGE_SHIFT;
- region.size = mr->memory_size;
- region.userspace_addr = mr->userspace_addr;
+ region.guest_pfn = slot->guest_phys_addr >> MSHV_PAGE_SHIFT;
+ region.size = slot->memory_size;
+ region.userspace_addr = slot->userspace_addr;
if (!map) {
region.flags |= (1 << MSHV_SET_MEM_BIT_UNMAP);
- trace_mshv_unmap_memory(mr->userspace_addr, mr->guest_phys_addr,
- mr->memory_size);
+ trace_mshv_unmap_memory(slot->userspace_addr, slot->guest_phys_addr,
+ slot->memory_size);
return set_guest_memory(vm_fd, &region);
}
region.flags = BIT(MSHV_SET_MEM_BIT_EXECUTABLE);
- if (!mr->readonly) {
+ if (!slot->readonly) {
region.flags |= BIT(MSHV_SET_MEM_BIT_WRITABLE);
}
- trace_mshv_map_memory(mr->userspace_addr, mr->guest_phys_addr,
- mr->memory_size);
+ trace_mshv_map_memory(slot->userspace_addr, slot->guest_phys_addr,
+ slot->memory_size);
return set_guest_memory(vm_fd, &region);
}
+static int slot_matches_region(const MshvMemorySlot *slot1,
+ const MshvMemorySlot *slot2)
+{
+ return (slot1->guest_phys_addr == slot2->guest_phys_addr &&
+ slot1->userspace_addr == slot2->userspace_addr &&
+ slot1->memory_size == slot2->memory_size) ? 0 : -1;
+}
+
+/* Needs to be called with mshv_state->msm.mutex held */
+static MshvMemorySlot *find_mem_slot_by_region(uint64_t gpa, uint64_t size,
+ uint64_t userspace_addr)
+{
+ MshvMemorySlot ref_slot = {
+ .guest_phys_addr = gpa,
+ .userspace_addr = userspace_addr,
+ .memory_size = size,
+ };
+ GList *found;
+ MshvMemorySlotManager *manager = &mshv_state->msm;
+
+ assert(manager);
+ found = g_list_find_custom(manager->slots, &ref_slot,
+ (GCompareFunc) slot_matches_region);
+
+ return found ? found->data : NULL;
+}
+
+static int slot_covers_gpa(const MshvMemorySlot *slot, uint64_t *gpa_p)
+{
+ uint64_t gpa_offset, gpa = *gpa_p;
+
+ gpa_offset = gpa - slot->guest_phys_addr;
+ return (slot->guest_phys_addr <= gpa && gpa_offset < slot->memory_size)
+ ? 0 : -1;
+}
+
+/* Needs to be called with mshv_state->msm.mutex or RCU read lock held */
+static MshvMemorySlot *find_mem_slot_by_gpa(GList *head, uint64_t gpa)
+{
+ GList *found;
+ MshvMemorySlot *slot;
+
+ trace_mshv_find_slot_by_gpa(gpa);
+
+ found = g_list_find_custom(head, &gpa, (GCompareFunc) slot_covers_gpa);
+ if (found) {
+ slot = found->data;
+ trace_mshv_found_slot(slot->userspace_addr, slot->guest_phys_addr,
+ slot->memory_size);
+ return slot;
+ }
+
+ return NULL;
+}
+
+/* Needs to be called with mshv_state->msm.mutex held */
+static void set_mapped(MshvMemorySlot *slot, bool mapped)
+{
+ /* prior writes to mapped field becomes visible before readers see slot */
+ qatomic_store_release(&slot->mapped, mapped);
+}
+
+MshvRemapResult mshv_remap_overlap_region(int vm_fd, uint64_t gpa)
+{
+ MshvMemorySlot *gpa_slot, *overlap_slot;
+ GList *head;
+ int ret;
+ MshvMemorySlotManager *manager = &mshv_state->msm;
+
+ /* fast path, called often by unmapped_gpa vm exit */
+ WITH_RCU_READ_LOCK_GUARD() {
+ assert(manager);
+ head = qatomic_load_acquire(&manager->slots);
+ /* return early if no slot is found */
+ gpa_slot = find_mem_slot_by_gpa(head, gpa);
+ if (gpa_slot == NULL) {
+ return MshvRemapNoMapping;
+ }
+
+ /* return early if no overlapping slot is found */
+ overlap_slot = find_overlap_mem_slot(head, gpa_slot);
+ if (overlap_slot == NULL) {
+ return MshvRemapNoOverlap;
+ }
+ }
+
+ /*
+ * We'll modify the mapping list, so we need to upgrade to mutex and
+ * recheck.
+ */
+ assert(manager);
+ QEMU_LOCK_GUARD(&manager->mutex);
+
+ /* return early if no slot is found */
+ gpa_slot = find_mem_slot_by_gpa(manager->slots, gpa);
+ if (gpa_slot == NULL) {
+ return MshvRemapNoMapping;
+ }
+
+ /* return early if no overlapping slot is found */
+ overlap_slot = find_overlap_mem_slot(manager->slots, gpa_slot);
+ if (overlap_slot == NULL) {
+ return MshvRemapNoOverlap;
+ }
+
+ /* unmap overlapping slot */
+ ret = map_or_unmap(vm_fd, overlap_slot, false);
+ if (ret < 0) {
+ error_report("failed to unmap overlap region");
+ abort();
+ }
+ set_mapped(overlap_slot, false);
+ warn_report("mapped out userspace_addr=0x%016lx gpa=0x%010lx size=0x%lx",
+ overlap_slot->userspace_addr,
+ overlap_slot->guest_phys_addr,
+ overlap_slot->memory_size);
+
+ /* map region for gpa */
+ ret = map_or_unmap(vm_fd, gpa_slot, true);
+ if (ret < 0) {
+ error_report("failed to map new region");
+ abort();
+ }
+ set_mapped(gpa_slot, true);
+ warn_report("mapped in userspace_addr=0x%016lx gpa=0x%010lx size=0x%lx",
+ gpa_slot->userspace_addr, gpa_slot->guest_phys_addr,
+ gpa_slot->memory_size);
+
+ return MshvRemapOk;
+}
+
static int handle_unmapped_mmio_region_read(uint64_t gpa, uint64_t size,
uint8_t *data)
{
@@ -124,20 +388,97 @@ int mshv_guest_mem_write(uint64_t gpa, const uint8_t *data, uintptr_t size,
return -1;
}
-static int set_memory(const MshvMemoryRegion *mshv_mr, bool add)
+static int tracked_unmap(int vm_fd, uint64_t gpa, uint64_t size,
+ uint64_t userspace_addr)
{
- int ret = 0;
+ int ret;
+ MshvMemorySlot *slot;
+ MshvMemorySlotManager *manager = &mshv_state->msm;
+
+ assert(manager);
+
+ QEMU_LOCK_GUARD(&manager->mutex);
+
+ slot = find_mem_slot_by_region(gpa, size, userspace_addr);
+ if (!slot) {
+ trace_mshv_skip_unset_mem(userspace_addr, gpa, size);
+ /* no work to do */
+ return 0;
+ }
+
+ if (!is_mapped(slot)) {
+ /* remove slot, no need to unmap */
+ return remove_slot(slot);
+ }
- if (!mshv_mr) {
- error_report("Invalid mshv_mr");
+ ret = map_or_unmap(vm_fd, slot, false);
+ if (ret < 0) {
+ error_report("failed to unmap memory region");
+ return ret;
+ }
+ return remove_slot(slot);
+}
+
+static int tracked_map(int vm_fd, uint64_t gpa, uint64_t size, bool readonly,
+ uint64_t userspace_addr)
+{
+ MshvMemorySlot *slot, *overlap_slot;
+ int ret;
+ MshvMemorySlotManager *manager = &mshv_state->msm;
+
+ assert(manager);
+
+ QEMU_LOCK_GUARD(&manager->mutex);
+
+ slot = find_mem_slot_by_region(gpa, size, userspace_addr);
+ if (slot) {
+ error_report("memory region already mapped at gpa=0x%lx, "
+ "userspace_addr=0x%lx, size=0x%lx",
+ slot->guest_phys_addr, slot->userspace_addr,
+ slot->memory_size);
return -1;
}
- trace_mshv_set_memory(add, mshv_mr->guest_phys_addr,
- mshv_mr->memory_size,
- mshv_mr->userspace_addr, mshv_mr->readonly,
- ret);
- return map_or_unmap(mshv_state->vm, mshv_mr, add);
+ slot = append_slot(gpa, userspace_addr, size, readonly);
+
+ overlap_slot = find_overlap_mem_slot(manager->slots, slot);
+ if (overlap_slot) {
+ trace_mshv_remap_attempt(slot->userspace_addr,
+ slot->guest_phys_addr,
+ slot->memory_size);
+ warn_report("attempt to map region [0x%lx-0x%lx], while "
+ "[0x%lx-0x%lx] is already mapped in the guest",
+ userspace_addr, userspace_addr + size - 1,
+ overlap_slot->userspace_addr,
+ overlap_slot->userspace_addr +
+ overlap_slot->memory_size - 1);
+
+ /* do not register mem slot in hv, but record for later swap-in */
+ set_mapped(slot, false);
+
+ return 0;
+ }
+
+ ret = map_or_unmap(vm_fd, slot, true);
+ if (ret < 0) {
+ error_report("failed to map memory region");
+ return -1;
+ }
+ set_mapped(slot, true);
+
+ return 0;
+}
+
+static int set_memory(uint64_t gpa, uint64_t size, bool readonly,
+ uint64_t userspace_addr, bool add)
+{
+ int vm_fd = mshv_state->vm;
+
+ if (add) {
+ return tracked_map(vm_fd, gpa, size, readonly, userspace_addr);
+ }
+
+ return tracked_unmap(vm_fd, gpa, size, userspace_addr);
}
/*
@@ -173,7 +514,9 @@ void mshv_set_phys_mem(MshvMemoryListener *mml, MemoryRegionSection *section,
bool writable = !area->readonly && !area->rom_device;
hwaddr start_addr, mr_offset, size;
void *ram;
- MshvMemoryRegion mshv_mr = {0};
+
+ size = align_section(section, &start_addr);
+ trace_mshv_set_phys_mem(add, section->mr->name, start_addr);
size = align_section(section, &start_addr);
trace_mshv_set_phys_mem(add, section->mr->name, start_addr);
@@ -200,14 +543,21 @@ void mshv_set_phys_mem(MshvMemoryListener *mml, MemoryRegionSection *section,
ram = memory_region_get_ram_ptr(area) + mr_offset;
- mshv_mr.guest_phys_addr = start_addr;
- mshv_mr.memory_size = size;
- mshv_mr.readonly = !writable;
- mshv_mr.userspace_addr = (uint64_t)ram;
-
- ret = set_memory(&mshv_mr, add);
+ ret = set_memory(start_addr, size, !writable, (uint64_t)ram, add);
if (ret < 0) {
- error_report("Failed to set memory region");
+ error_report("failed to set memory region");
abort();
}
}
+
+void mshv_init_memory_slot_manager(MshvState *mshv_state)
+{
+ MshvMemorySlotManager *manager;
+
+ assert(mshv_state);
+ manager = &mshv_state->msm;
+
+ manager->n_slots = 0;
+ manager->slots = NULL;
+ qemu_mutex_init(&manager->mutex);
+}
diff --git a/accel/mshv/mshv-all.c b/accel/mshv/mshv-all.c
index fa1f8f35bd..5edfcbad9d 100644
--- a/accel/mshv/mshv-all.c
+++ b/accel/mshv/mshv-all.c
@@ -437,6 +437,8 @@ static int mshv_init(AccelState *as, MachineState *ms)
mshv_init_msicontrol();
+ mshv_init_memory_slot_manager(s);
+
ret = create_vm(mshv_fd, &vm_fd);
if (ret < 0) {
close(mshv_fd);
diff --git a/accel/mshv/trace-events b/accel/mshv/trace-events
index a4dffeb24a..36f0d59b38 100644
--- a/accel/mshv/trace-events
+++ b/accel/mshv/trace-events
@@ -26,3 +26,8 @@ mshv_map_memory(uint64_t userspace_addr, uint64_t gpa, uint64_t size) "\tu_a=0x%
mshv_unmap_memory(uint64_t userspace_addr, uint64_t gpa, uint64_t size) "\tu_a=0x%" PRIx64 " gpa=0x%010" PRIx64 " size=0x%08" PRIx64
mshv_set_phys_mem(bool add, const char *name, uint64_t gpa) "\tadd=%d name=%s gpa=0x%010" PRIx64
mshv_handle_mmio(uint64_t gva, uint64_t gpa, uint64_t size, uint8_t access_type) "\tgva=0x%" PRIx64 " gpa=0x%010" PRIx64 " size=0x%" PRIx64 " access_type=%d"
+
+mshv_found_slot(uint64_t userspace_addr, uint64_t gpa, uint64_t size) "\tu_a=0x%" PRIx64 " gpa=0x%010" PRIx64 " size=0x%08" PRIx64
+mshv_skip_unset_mem(uint64_t userspace_addr, uint64_t gpa, uint64_t size) "\tu_a=0x%" PRIx64 " gpa=0x%010" PRIx64 " size=0x%08" PRIx64
+mshv_remap_attempt(uint64_t userspace_addr, uint64_t gpa, uint64_t size) "\tu_a=0x%" PRIx64 " gpa=0x%010" PRIx64 " size=0x%08" PRIx64
+mshv_find_slot_by_gpa(uint64_t gpa) "\tgpa=0x%010" PRIx64
diff --git a/include/system/mshv_int.h b/include/system/mshv_int.h
index b29d39911d..6350c69e9d 100644
--- a/include/system/mshv_int.h
+++ b/include/system/mshv_int.h
@@ -16,6 +16,8 @@
#define MSHV_MSR_ENTRIES_COUNT 64
+#define MSHV_MAX_MEM_SLOTS 32
+
typedef struct hyperv_message hv_message;
struct AccelCPUState {
@@ -33,6 +35,12 @@ typedef struct MshvAddressSpace {
AddressSpace *as;
} MshvAddressSpace;
+typedef struct MshvMemorySlotManager {
+ size_t n_slots;
+ GList *slots;
+ QemuMutex mutex;
+} MshvMemorySlotManager;
+
struct MshvState {
AccelState parent_obj;
int vm;
@@ -41,6 +49,7 @@ struct MshvState {
int nr_as;
MshvAddressSpace *as;
int fd;
+ MshvMemorySlotManager msm;
};
typedef struct MshvMsiControl {
@@ -71,6 +80,12 @@ typedef enum MshvVmExit {
MshvVmExitSpecial = 2,
} MshvVmExit;
+typedef enum MshvRemapResult {
+ MshvRemapOk = 0,
+ MshvRemapNoMapping = 1,
+ MshvRemapNoOverlap = 2,
+} MshvRemapResult;
+
void mshv_init_mmio_emu(void);
int mshv_create_vcpu(int vm_fd, uint8_t vp_index, int *cpu_fd);
void mshv_remove_vcpu(int vm_fd, int cpu_fd);
@@ -94,19 +109,22 @@ int mshv_hvcall(int fd, const struct mshv_root_hvcall *args);
#endif
/* memory */
-typedef struct MshvMemoryRegion {
+typedef struct MshvMemorySlot {
uint64_t guest_phys_addr;
uint64_t memory_size;
uint64_t userspace_addr;
bool readonly;
-} MshvMemoryRegion;
+ bool mapped;
+} MshvMemorySlot;
+MshvRemapResult mshv_remap_overlap_region(int vm_fd, uint64_t gpa);
int mshv_guest_mem_read(uint64_t gpa, uint8_t *data, uintptr_t size,
bool is_secure_mode, bool instruction_fetch);
int mshv_guest_mem_write(uint64_t gpa, const uint8_t *data, uintptr_t size,
bool is_secure_mode);
void mshv_set_phys_mem(MshvMemoryListener *mml, MemoryRegionSection *section,
bool add);
+void mshv_init_memory_slot_manager(MshvState *mshv_state);
/* msr */
typedef struct MshvMsrEntry {
diff --git a/target/i386/mshv/mshv-cpu.c b/target/i386/mshv/mshv-cpu.c
index 7edc032cea..de87142bff 100644
--- a/target/i386/mshv/mshv-cpu.c
+++ b/target/i386/mshv/mshv-cpu.c
@@ -1170,6 +1170,43 @@ static int handle_mmio(CPUState *cpu, const struct hyperv_message *msg,
return 0;
}
+static int handle_unmapped_mem(int vm_fd, CPUState *cpu,
+ const struct hyperv_message *msg,
+ MshvVmExit *exit_reason)
+{
+ struct hv_x64_memory_intercept_message info = { 0 };
+ uint64_t gpa;
+ int ret;
+ enum MshvRemapResult remap_result;
+
+ ret = set_memory_info(msg, &info);
+ if (ret < 0) {
+ error_report("failed to convert message to memory info");
+ return -1;
+ }
+
+ gpa = info.guest_physical_address;
+
+ /* attempt to remap the region, in case of overlapping userspace mappings */
+ remap_result = mshv_remap_overlap_region(vm_fd, gpa);
+ *exit_reason = MshvVmExitIgnore;
+
+ switch (remap_result) {
+ case MshvRemapNoMapping:
+ /* if we didn't find a mapping, it is probably mmio */
+ return handle_mmio(cpu, msg, exit_reason);
+ case MshvRemapOk:
+ break;
+ case MshvRemapNoOverlap:
+ /* This should not happen, but we are forgiving it */
+ warn_report("found no overlap for unmapped region");
+ *exit_reason = MshvVmExitSpecial;
+ break;
+ }
+
+ return 0;
+}
+
static int set_ioport_info(const struct hyperv_message *msg,
hv_x64_io_port_intercept_message *info)
{
@@ -1507,6 +1544,12 @@ int mshv_run_vcpu(int vm_fd, CPUState *cpu, hv_message *msg, MshvVmExit *exit)
case HVMSG_UNRECOVERABLE_EXCEPTION:
return MshvVmExitShutdown;
case HVMSG_UNMAPPED_GPA:
+ ret = handle_unmapped_mem(vm_fd, cpu, msg, &exit_reason);
+ if (ret < 0) {
+ error_report("failed to handle unmapped memory");
+ return -1;
+ }
+ return exit_reason;
case HVMSG_GPA_INTERCEPT:
ret = handle_mmio(cpu, msg, &exit_reason);
if (ret < 0) {
--
2.51.1

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,172 @@
From bbcb9aabff22f694f88e518de0261fc28d5dc2c3 Mon Sep 17 00:00:00 2001
From: Magnus Kulke <magnuskulke@linux.microsoft.com>
Date: Tue, 16 Sep 2025 18:48:28 +0200
Subject: [PATCH 09/31] accel/mshv: Register memory region listeners
RH-Author: Igor Mammedov <imammedo@redhat.com>
RH-MergeRequest: 435: x86: enablement for Azure L1VH OCP readiness
RH-Jira: RHEL-132193
RH-Acked-by: Vitaly Kuznetsov <vkuznets@redhat.com>
RH-Acked-by: Miroslav Rezanina <mrezanin@redhat.com>
RH-Commit: [8/30] 4b7ab25a2146d964cf526067925157c706566040
Add memory listener hooks for the MSHV accelerator to track guest
memory regions. This enables the backend to respond to region
additions, removals and will be used to manage guest memory mappings
inside the hypervisor.
Actually registering physical memory in the hypervisor is still stubbed
out.
Signed-off-by: Magnus Kulke <magnuskulke@linux.microsoft.com>
Link: https://lore.kernel.org/r/20250916164847.77883-9-magnuskulke@linux.microsoft.com
[mshv.h/mshv_int.h split. - Paolo]
Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
(cherry picked from commit 5006ea1344d134356a9b2e1afd521cf8df0c6a85)
Signed-off-by: Igor Mammedov <imammedo@redhat.com>
---
accel/mshv/mem.c | 25 +++++++++++++++
accel/mshv/meson.build | 1 +
accel/mshv/mshv-all.c | 67 +++++++++++++++++++++++++++++++++++++--
include/system/mshv_int.h | 4 +++
4 files changed, 95 insertions(+), 2 deletions(-)
create mode 100644 accel/mshv/mem.c
diff --git a/accel/mshv/mem.c b/accel/mshv/mem.c
new file mode 100644
index 0000000000..9889918c31
--- /dev/null
+++ b/accel/mshv/mem.c
@@ -0,0 +1,25 @@
+/*
+ * QEMU MSHV support
+ *
+ * Copyright Microsoft, Corp. 2025
+ *
+ * Authors:
+ * Magnus Kulke <magnuskulke@microsoft.com>
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ *
+ */
+
+#include "qemu/osdep.h"
+#include "qemu/error-report.h"
+#include "system/address-spaces.h"
+#include "system/mshv.h"
+#include "system/mshv_int.h"
+
+void mshv_set_phys_mem(MshvMemoryListener *mml, MemoryRegionSection *section,
+ bool add)
+{
+ error_report("unimplemented");
+ abort();
+}
+
diff --git a/accel/mshv/meson.build b/accel/mshv/meson.build
index 4c03ac7921..8a6beb3fb1 100644
--- a/accel/mshv/meson.build
+++ b/accel/mshv/meson.build
@@ -1,5 +1,6 @@
mshv_ss = ss.source_set()
mshv_ss.add(if_true: files(
+ 'mem.c',
'mshv-all.c'
))
diff --git a/accel/mshv/mshv-all.c b/accel/mshv/mshv-all.c
index ae12f0f58b..a684a36677 100644
--- a/accel/mshv/mshv-all.c
+++ b/accel/mshv/mshv-all.c
@@ -49,10 +49,73 @@ bool mshv_allowed;
MshvState *mshv_state;
+static void mem_region_add(MemoryListener *listener,
+ MemoryRegionSection *section)
+{
+ MshvMemoryListener *mml;
+ mml = container_of(listener, MshvMemoryListener, listener);
+ memory_region_ref(section->mr);
+ mshv_set_phys_mem(mml, section, true);
+}
+
+static void mem_region_del(MemoryListener *listener,
+ MemoryRegionSection *section)
+{
+ MshvMemoryListener *mml;
+ mml = container_of(listener, MshvMemoryListener, listener);
+ mshv_set_phys_mem(mml, section, false);
+ memory_region_unref(section->mr);
+}
+
+static MemoryListener mshv_memory_listener = {
+ .name = "mshv",
+ .priority = MEMORY_LISTENER_PRIORITY_ACCEL,
+ .region_add = mem_region_add,
+ .region_del = mem_region_del,
+};
+
+static MemoryListener mshv_io_listener = {
+ .name = "mshv", .priority = MEMORY_LISTENER_PRIORITY_DEV_BACKEND,
+ /* MSHV does not support PIO eventfd */
+};
+
+static void register_mshv_memory_listener(MshvState *s, MshvMemoryListener *mml,
+ AddressSpace *as, int as_id,
+ const char *name)
+{
+ int i;
+
+ mml->listener = mshv_memory_listener;
+ mml->listener.name = name;
+ memory_listener_register(&mml->listener, as);
+ for (i = 0; i < s->nr_as; ++i) {
+ if (!s->as[i].as) {
+ s->as[i].as = as;
+ s->as[i].ml = mml;
+ break;
+ }
+ }
+}
+
static int mshv_init(AccelState *as, MachineState *ms)
{
- error_report("unimplemented");
- abort();
+ MshvState *s;
+ s = MSHV_STATE(as);
+
+ accel_blocker_init();
+
+ s->vm = 0;
+
+ s->nr_as = 1;
+ s->as = g_new0(MshvAddressSpace, s->nr_as);
+
+ mshv_state = s;
+
+ register_mshv_memory_listener(s, &s->memory_listener, &address_space_memory,
+ 0, "mshv-memory");
+ memory_listener_register(&mshv_io_listener, &address_space_io);
+
+ return 0;
}
static void mshv_start_vcpu_thread(CPUState *cpu)
diff --git a/include/system/mshv_int.h b/include/system/mshv_int.h
index 132491b599..cfa177ff72 100644
--- a/include/system/mshv_int.h
+++ b/include/system/mshv_int.h
@@ -38,4 +38,8 @@ struct MshvState {
MshvAddressSpace *as;
};
+/* memory */
+void mshv_set_phys_mem(MshvMemoryListener *mml, MemoryRegionSection *section,
+ bool add);
+
#endif
--
2.51.1

View File

@ -0,0 +1,39 @@
From 193512539736d1cf0ef4e138b7c621a2f12ac167 Mon Sep 17 00:00:00 2001
From: Paolo Bonzini <pbonzini@redhat.com>
Date: Wed, 22 Oct 2025 14:52:48 +0200
Subject: [PATCH 30/31] accel/mshv: initialize thread name
RH-Author: Igor Mammedov <imammedo@redhat.com>
RH-MergeRequest: 435: x86: enablement for Azure L1VH OCP readiness
RH-Jira: RHEL-132193
RH-Acked-by: Vitaly Kuznetsov <vkuznets@redhat.com>
RH-Acked-by: Miroslav Rezanina <mrezanin@redhat.com>
RH-Commit: [29/30] 88e9d5fed01c8ab1fa4433a7b84d297b0879d20d
The initialization was dropped when the code was copied from existing
accelerators. Coverity knows (CID 1641400). Fix it.
Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
(cherry picked from commit 2cd3c1d35a06a62b25afa80c5baf381ce4b56805)
Signed-off-by: Igor Mammedov <imammedo@redhat.com>
---
accel/mshv/mshv-all.c | 3 +++
1 file changed, 3 insertions(+)
diff --git a/accel/mshv/mshv-all.c b/accel/mshv/mshv-all.c
index 45174f7c4e..80428d130d 100644
--- a/accel/mshv/mshv-all.c
+++ b/accel/mshv/mshv-all.c
@@ -596,6 +596,9 @@ static void mshv_start_vcpu_thread(CPUState *cpu)
{
char thread_name[VCPU_THREAD_NAME_SIZE];
+ snprintf(thread_name, VCPU_THREAD_NAME_SIZE, "CPU %d/MSHV",
+ cpu->cpu_index);
+
cpu->thread = g_malloc0(sizeof(QemuThread));
cpu->halt_cond = g_malloc0(sizeof(QemuCond));
--
2.51.1

View File

@ -0,0 +1,42 @@
From 3234aa5a584713cc4423bfb5215ef38d8789ff73 Mon Sep 17 00:00:00 2001
From: Paolo Bonzini <pbonzini@redhat.com>
Date: Wed, 22 Oct 2025 14:54:30 +0200
Subject: [PATCH 31/31] accel/mshv: use return value of handle_pio_str_read
RH-Author: Igor Mammedov <imammedo@redhat.com>
RH-MergeRequest: 435: x86: enablement for Azure L1VH OCP readiness
RH-Jira: RHEL-132193
RH-Acked-by: Vitaly Kuznetsov <vkuznets@redhat.com>
RH-Acked-by: Miroslav Rezanina <mrezanin@redhat.com>
RH-Commit: [30/30] 75d6f9f124f9e967b6a605241e1f725173bd1142
Coverity complains because we assign to ret here but
then never read it again before we overwrite it with
the call to set_x64_registers().
Analyzed-by: Peter Maydell <peter.maydell@linaro.org>
Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
(cherry picked from commit 1557adc82698416bc68033765cfffb0a0b91c6bf)
Signed-off-by: Igor Mammedov <imammedo@redhat.com>
---
target/i386/mshv/mshv-cpu.c | 4 ++++
1 file changed, 4 insertions(+)
diff --git a/target/i386/mshv/mshv-cpu.c b/target/i386/mshv/mshv-cpu.c
index 1f7b9cb37e..1c3db02188 100644
--- a/target/i386/mshv/mshv-cpu.c
+++ b/target/i386/mshv/mshv-cpu.c
@@ -1489,6 +1489,10 @@ static int handle_pio_str(CPUState *cpu, hv_x64_io_port_intercept_message *info)
reg_values[0] = info->rsi;
} else {
ret = handle_pio_str_read(cpu, info, repeat, port, direction_flag);
+ if (ret < 0) {
+ error_report("Failed to handle pio str read");
+ return -1;
+ }
reg_names[0] = HV_X64_REGISTER_RDI;
reg_values[0] = info->rdi;
}
--
2.51.1

View File

@ -0,0 +1,143 @@
From 8ff12da78a42d59fd4e3525d081634804debf008 Mon Sep 17 00:00:00 2001
From: Magnus Kulke <magnuskulke@linux.microsoft.com>
Date: Tue, 16 Sep 2025 18:48:46 +0200
Subject: [PATCH 28/31] docs: Add mshv to documentation
RH-Author: Igor Mammedov <imammedo@redhat.com>
RH-MergeRequest: 435: x86: enablement for Azure L1VH OCP readiness
RH-Jira: RHEL-132193
RH-Acked-by: Vitaly Kuznetsov <vkuznets@redhat.com>
RH-Acked-by: Miroslav Rezanina <mrezanin@redhat.com>
RH-Commit: [27/30] 2c46c1324ab9670aa133efcb9104d744746be32b
Added mshv to the list of accelerators in doc text.
Signed-off-by: Magnus Kulke <magnuskulke@linux.microsoft.com>
Link: https://lore.kernel.org/r/20250916164847.77883-27-magnuskulke@linux.microsoft.com
Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
(cherry picked from commit 3af71a1a6a7a497df7d3026239d6136b56e3d5ab)
Signed-off-by: Igor Mammedov <imammedo@redhat.com>
---
docs/about/build-platforms.rst | 2 +-
docs/devel/codebase.rst | 2 +-
docs/glossary.rst | 7 +++----
docs/system/introduction.rst | 3 +++
qemu-options.hx | 16 ++++++++--------
5 files changed, 16 insertions(+), 14 deletions(-)
diff --git a/docs/about/build-platforms.rst b/docs/about/build-platforms.rst
index 8671c3be9c..06ba0ddc9a 100644
--- a/docs/about/build-platforms.rst
+++ b/docs/about/build-platforms.rst
@@ -55,7 +55,7 @@ Those hosts are officially supported, with various accelerators:
* - SPARC
- tcg
* - x86
- - hvf (64 bit only), kvm, nvmm, tcg, whpx (64 bit only), xen
+ - hvf (64 bit only), mshv (64 bit only), kvm, nvmm, tcg, whpx (64 bit only), xen
Other host architectures are not supported. It is possible to build QEMU system
emulation on an unsupported host architecture using the configure
diff --git a/docs/devel/codebase.rst b/docs/devel/codebase.rst
index 2a3143787a..69d8827117 100644
--- a/docs/devel/codebase.rst
+++ b/docs/devel/codebase.rst
@@ -48,7 +48,7 @@ yet, so sometimes the source code is all you have.
* `accel <https://gitlab.com/qemu-project/qemu/-/tree/master/accel>`_:
Infrastructure and architecture agnostic code related to the various
`accelerators <Accelerators>` supported by QEMU
- (TCG, KVM, hvf, whpx, xen, nvmm).
+ (TCG, KVM, hvf, whpx, xen, nvmm, mshv).
Contains interfaces for operations that will be implemented per
`target <https://gitlab.com/qemu-project/qemu/-/tree/master/target>`_.
* `audio <https://gitlab.com/qemu-project/qemu/-/tree/master/audio>`_:
diff --git a/docs/glossary.rst b/docs/glossary.rst
index 4fa044bfb6..2857731bc4 100644
--- a/docs/glossary.rst
+++ b/docs/glossary.rst
@@ -12,7 +12,7 @@ Accelerator
A specific API used to accelerate execution of guest instructions. It can be
hardware-based, through a virtualization API provided by the host OS (kvm, hvf,
-whpx, ...), or software-based (tcg). See this description of `supported
+whpx, mshv, ...), or software-based (tcg). See this description of `supported
accelerators<Accelerators>`.
Board
@@ -101,9 +101,8 @@ manage a virtual machine. QEMU is a virtualizer, that interacts with various
hypervisors.
In the context of QEMU, an hypervisor is an API, provided by the Host OS,
-allowing to execute virtual machines. Linux implementation is KVM (and supports
-Xen as well). For MacOS, it's HVF. Windows defines WHPX. And NetBSD provides
-NVMM.
+allowing to execute virtual machines. Linux provides a choice of KVM, Xen
+or MSHV; MacOS provides HVF; Windows provides WHPX; NetBSD provides NVMM.
.. _machine:
diff --git a/docs/system/introduction.rst b/docs/system/introduction.rst
index 4cd46b5b8f..9c57523b6c 100644
--- a/docs/system/introduction.rst
+++ b/docs/system/introduction.rst
@@ -23,6 +23,9 @@ Tiny Code Generator (TCG) capable of emulating many CPUs.
* - Xen
- Linux (as dom0)
- Arm, x86
+ * - MSHV
+ - Linux (as dom0)
+ - x86
* - Hypervisor Framework (hvf)
- MacOS
- x86 (64 bit only), Arm (64 bit only)
diff --git a/qemu-options.hx b/qemu-options.hx
index 3837456a61..f802f1d6d2 100644
--- a/qemu-options.hx
+++ b/qemu-options.hx
@@ -28,7 +28,7 @@ DEF("machine", HAS_ARG, QEMU_OPTION_machine, \
"-machine [type=]name[,prop[=value][,...]]\n"
" selects emulated machine ('-machine help' for list)\n"
" property accel=accel1[:accel2[:...]] selects accelerator\n"
- " supported accelerators are kvm, xen, hvf, nvmm, whpx or tcg (default: tcg)\n"
+ " supported accelerators are kvm, xen, hvf, nvmm, whpx, mshv or tcg (default: tcg)\n"
" vmport=on|off|auto controls emulation of vmport (default: auto)\n"
" dump-guest-core=on|off include guest memory in a core dump (default=on)\n"
" mem-merge=on|off controls memory merge support (default: on)\n"
@@ -66,10 +66,10 @@ SRST
``accel=accels1[:accels2[:...]]``
This is used to enable an accelerator. Depending on the target
- architecture, kvm, xen, hvf, nvmm, whpx or tcg can be available.
- By default, tcg is used. If there is more than one accelerator
- specified, the next one is used if the previous one fails to
- initialize.
+ architecture, kvm, xen, hvf, nvmm, whpx, mshv or tcg can be
+ available. By default, tcg is used. If there is more than one
+ accelerator specified, the next one is used if the previous one
+ fails to initialize.
``vmport=on|off|auto``
Enables emulation of VMWare IO port, for vmmouse etc. auto says
@@ -226,7 +226,7 @@ ERST
DEF("accel", HAS_ARG, QEMU_OPTION_accel,
"-accel [accel=]accelerator[,prop[=value][,...]]\n"
- " select accelerator (kvm, xen, hvf, nvmm, whpx or tcg; use 'help' for a list)\n"
+ " select accelerator (kvm, xen, hvf, nvmm, whpx, mshv or tcg; use 'help' for a list)\n"
" igd-passthru=on|off (enable Xen integrated Intel graphics passthrough, default=off)\n"
" kernel-irqchip=on|off|split controls accelerated irqchip support (default=on)\n"
" kvm-shadow-mem=size of KVM shadow MMU in bytes\n"
@@ -241,8 +241,8 @@ DEF("accel", HAS_ARG, QEMU_OPTION_accel,
SRST
``-accel name[,prop=value[,...]]``
This is used to enable an accelerator. Depending on the target
- architecture, kvm, xen, hvf, nvmm, whpx or tcg can be available. By
- default, tcg is used. If there is more than one accelerator
+ architecture, kvm, xen, hvf, nvmm, whpx, mshv or tcg can be available.
+ By default, tcg is used. If there is more than one accelerator
specified, the next one is used if the previous one fails to
initialize.
--
2.51.1

View File

@ -0,0 +1,386 @@
From f68e2f259fc589c33e2fef16e488842824125a98 Mon Sep 17 00:00:00 2001
From: Magnus Kulke <magnuskulke@linux.microsoft.com>
Date: Tue, 16 Sep 2025 18:48:24 +0200
Subject: [PATCH 05/31] hw/intc: Generalize APIC helper names from kvm_* to
accel_*
RH-Author: Igor Mammedov <imammedo@redhat.com>
RH-MergeRequest: 435: x86: enablement for Azure L1VH OCP readiness
RH-Jira: RHEL-132193
RH-Acked-by: Vitaly Kuznetsov <vkuznets@redhat.com>
RH-Acked-by: Miroslav Rezanina <mrezanin@redhat.com>
RH-Commit: [4/30] dd44c736f2bef167511d2b60d456c04f12019882
Rename APIC helper functions to use an accel_* prefix instead of kvm_*
to support use by accelerators other than KVM. This is a preparatory
step for integrating MSHV support with common APIC logic.
Signed-off-by: Magnus Kulke <magnuskulke@linux.microsoft.com>
Link: https://lore.kernel.org/r/20250916164847.77883-5-magnuskulke@linux.microsoft.com
[Remove dead definition of mshv_msi_via_irqfd_enabled. - Paolo]
Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
(cherry picked from commit 638ac1c78457dd93ccd795b9c6c2673af8c7dd21)
Signed-off-by: Igor Mammedov <imammedo@redhat.com>
---
accel/accel-irq.c | 106 +++++++++++++++++++++++++++++++++++++
accel/meson.build | 2 +-
hw/intc/ioapic.c | 20 ++++---
hw/virtio/virtio-pci.c | 21 ++++----
include/system/accel-irq.h | 37 +++++++++++++
include/system/mshv.h | 17 ++++++
6 files changed, 185 insertions(+), 18 deletions(-)
create mode 100644 accel/accel-irq.c
create mode 100644 include/system/accel-irq.h
diff --git a/accel/accel-irq.c b/accel/accel-irq.c
new file mode 100644
index 0000000000..7f864e35c4
--- /dev/null
+++ b/accel/accel-irq.c
@@ -0,0 +1,106 @@
+/*
+ * Accelerated irqchip abstraction
+ *
+ * Copyright Microsoft, Corp. 2025
+ *
+ * Authors: Ziqiao Zhou <ziqiaozhou@microsoft.com>
+ * Magnus Kulke <magnuskulke@microsoft.com>
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+
+#include "qemu/osdep.h"
+#include "hw/pci/msi.h"
+
+#include "system/kvm.h"
+#include "system/mshv.h"
+#include "system/accel-irq.h"
+
+int accel_irqchip_add_msi_route(KVMRouteChange *c, int vector, PCIDevice *dev)
+{
+#ifdef CONFIG_MSHV_IS_POSSIBLE
+ if (mshv_msi_via_irqfd_enabled()) {
+ return mshv_irqchip_add_msi_route(vector, dev);
+ }
+#endif
+ if (kvm_enabled()) {
+ return kvm_irqchip_add_msi_route(c, vector, dev);
+ }
+ return -ENOSYS;
+}
+
+int accel_irqchip_update_msi_route(int vector, MSIMessage msg, PCIDevice *dev)
+{
+#ifdef CONFIG_MSHV_IS_POSSIBLE
+ if (mshv_msi_via_irqfd_enabled()) {
+ return mshv_irqchip_update_msi_route(vector, msg, dev);
+ }
+#endif
+ if (kvm_enabled()) {
+ return kvm_irqchip_update_msi_route(kvm_state, vector, msg, dev);
+ }
+ return -ENOSYS;
+}
+
+void accel_irqchip_commit_route_changes(KVMRouteChange *c)
+{
+#ifdef CONFIG_MSHV_IS_POSSIBLE
+ if (mshv_msi_via_irqfd_enabled()) {
+ mshv_irqchip_commit_routes();
+ }
+#endif
+ if (kvm_enabled()) {
+ kvm_irqchip_commit_route_changes(c);
+ }
+}
+
+void accel_irqchip_commit_routes(void)
+{
+#ifdef CONFIG_MSHV_IS_POSSIBLE
+ if (mshv_msi_via_irqfd_enabled()) {
+ mshv_irqchip_commit_routes();
+ }
+#endif
+ if (kvm_enabled()) {
+ kvm_irqchip_commit_routes(kvm_state);
+ }
+}
+
+void accel_irqchip_release_virq(int virq)
+{
+#ifdef CONFIG_MSHV_IS_POSSIBLE
+ if (mshv_msi_via_irqfd_enabled()) {
+ mshv_irqchip_release_virq(virq);
+ }
+#endif
+ if (kvm_enabled()) {
+ kvm_irqchip_release_virq(kvm_state, virq);
+ }
+}
+
+int accel_irqchip_add_irqfd_notifier_gsi(EventNotifier *n, EventNotifier *rn,
+ int virq)
+{
+#ifdef CONFIG_MSHV_IS_POSSIBLE
+ if (mshv_msi_via_irqfd_enabled()) {
+ return mshv_irqchip_add_irqfd_notifier_gsi(n, rn, virq);
+ }
+#endif
+ if (kvm_enabled()) {
+ return kvm_irqchip_add_irqfd_notifier_gsi(kvm_state, n, rn, virq);
+ }
+ return -ENOSYS;
+}
+
+int accel_irqchip_remove_irqfd_notifier_gsi(EventNotifier *n, int virq)
+{
+#ifdef CONFIG_MSHV_IS_POSSIBLE
+ if (mshv_msi_via_irqfd_enabled()) {
+ return mshv_irqchip_remove_irqfd_notifier_gsi(n, virq);
+ }
+#endif
+ if (kvm_enabled()) {
+ return kvm_irqchip_remove_irqfd_notifier_gsi(kvm_state, n, virq);
+ }
+ return -ENOSYS;
+}
diff --git a/accel/meson.build b/accel/meson.build
index 25b0f100b5..6349efe682 100644
--- a/accel/meson.build
+++ b/accel/meson.build
@@ -1,6 +1,6 @@
common_ss.add(files('accel-common.c'))
specific_ss.add(files('accel-target.c'))
-system_ss.add(files('accel-system.c', 'accel-blocker.c', 'accel-qmp.c'))
+system_ss.add(files('accel-system.c', 'accel-blocker.c', 'accel-qmp.c', 'accel-irq.c'))
user_ss.add(files('accel-user.c'))
subdir('tcg')
diff --git a/hw/intc/ioapic.c b/hw/intc/ioapic.c
index 133bef852d..e431d00311 100644
--- a/hw/intc/ioapic.c
+++ b/hw/intc/ioapic.c
@@ -30,12 +30,18 @@
#include "hw/intc/ioapic_internal.h"
#include "hw/pci/msi.h"
#include "hw/qdev-properties.h"
+#include "system/accel-irq.h"
#include "system/kvm.h"
#include "system/system.h"
#include "hw/i386/apic-msidef.h"
#include "hw/i386/x86-iommu.h"
#include "trace.h"
+
+#if defined(CONFIG_KVM) || defined(CONFIG_MSHV)
+#define ACCEL_GSI_IRQFD_POSSIBLE
+#endif
+
#define APIC_DELIVERY_MODE_SHIFT 8
#define APIC_POLARITY_SHIFT 14
#define APIC_TRIG_MODE_SHIFT 15
@@ -191,10 +197,10 @@ static void ioapic_set_irq(void *opaque, int vector, int level)
static void ioapic_update_kvm_routes(IOAPICCommonState *s)
{
-#ifdef CONFIG_KVM
+#ifdef ACCEL_GSI_IRQFD_POSSIBLE
int i;
- if (kvm_irqchip_is_split()) {
+ if (accel_irqchip_is_split()) {
for (i = 0; i < IOAPIC_NUM_PINS; i++) {
MSIMessage msg;
struct ioapic_entry_info info;
@@ -202,15 +208,15 @@ static void ioapic_update_kvm_routes(IOAPICCommonState *s)
if (!info.masked) {
msg.address = info.addr;
msg.data = info.data;
- kvm_irqchip_update_msi_route(kvm_state, i, msg, NULL);
+ accel_irqchip_update_msi_route(i, msg, NULL);
}
}
- kvm_irqchip_commit_routes(kvm_state);
+ accel_irqchip_commit_routes();
}
#endif
}
-#ifdef CONFIG_KVM
+#ifdef ACCEL_KERNEL_GSI_IRQFD_POSSIBLE
static void ioapic_iec_notifier(void *private, bool global,
uint32_t index, uint32_t mask)
{
@@ -428,11 +434,11 @@ static const MemoryRegionOps ioapic_io_ops = {
static void ioapic_machine_done_notify(Notifier *notifier, void *data)
{
-#ifdef CONFIG_KVM
+#ifdef ACCEL_KERNEL_GSI_IRQFD_POSSIBLE
IOAPICCommonState *s = container_of(notifier, IOAPICCommonState,
machine_done);
- if (kvm_irqchip_is_split()) {
+ if (accel_irqchip_is_split()) {
X86IOMMUState *iommu = x86_iommu_get_default();
if (iommu) {
/* Register this IOAPIC with IOMMU IEC notifier, so that
diff --git a/hw/virtio/virtio-pci.c b/hw/virtio/virtio-pci.c
index 767216d795..0cdc16217f 100644
--- a/hw/virtio/virtio-pci.c
+++ b/hw/virtio/virtio-pci.c
@@ -34,6 +34,7 @@
#include "hw/pci/msi.h"
#include "hw/pci/msix.h"
#include "hw/loader.h"
+#include "system/accel-irq.h"
#include "system/kvm.h"
#include "hw/virtio/virtio-pci.h"
#include "qemu/range.h"
@@ -825,11 +826,11 @@ static int kvm_virtio_pci_vq_vector_use(VirtIOPCIProxy *proxy,
if (irqfd->users == 0) {
KVMRouteChange c = kvm_irqchip_begin_route_changes(kvm_state);
- ret = kvm_irqchip_add_msi_route(&c, vector, &proxy->pci_dev);
+ ret = accel_irqchip_add_msi_route(&c, vector, &proxy->pci_dev);
if (ret < 0) {
return ret;
}
- kvm_irqchip_commit_route_changes(&c);
+ accel_irqchip_commit_route_changes(&c);
irqfd->virq = ret;
}
irqfd->users++;
@@ -841,7 +842,7 @@ static void kvm_virtio_pci_vq_vector_release(VirtIOPCIProxy *proxy,
{
VirtIOIRQFD *irqfd = &proxy->vector_irqfd[vector];
if (--irqfd->users == 0) {
- kvm_irqchip_release_virq(kvm_state, irqfd->virq);
+ accel_irqchip_release_virq(irqfd->virq);
}
}
@@ -850,7 +851,7 @@ static int kvm_virtio_pci_irqfd_use(VirtIOPCIProxy *proxy,
unsigned int vector)
{
VirtIOIRQFD *irqfd = &proxy->vector_irqfd[vector];
- return kvm_irqchip_add_irqfd_notifier_gsi(kvm_state, n, NULL, irqfd->virq);
+ return accel_irqchip_add_irqfd_notifier_gsi(n, NULL, irqfd->virq);
}
static void kvm_virtio_pci_irqfd_release(VirtIOPCIProxy *proxy,
@@ -860,7 +861,7 @@ static void kvm_virtio_pci_irqfd_release(VirtIOPCIProxy *proxy,
VirtIOIRQFD *irqfd = &proxy->vector_irqfd[vector];
int ret;
- ret = kvm_irqchip_remove_irqfd_notifier_gsi(kvm_state, n, irqfd->virq);
+ ret = accel_irqchip_remove_irqfd_notifier_gsi(n, irqfd->virq);
assert(ret == 0);
}
static int virtio_pci_get_notifier(VirtIOPCIProxy *proxy, int queue_no,
@@ -995,12 +996,12 @@ static int virtio_pci_one_vector_unmask(VirtIOPCIProxy *proxy,
if (proxy->vector_irqfd) {
irqfd = &proxy->vector_irqfd[vector];
if (irqfd->msg.data != msg.data || irqfd->msg.address != msg.address) {
- ret = kvm_irqchip_update_msi_route(kvm_state, irqfd->virq, msg,
- &proxy->pci_dev);
+ ret = accel_irqchip_update_msi_route(irqfd->virq, msg,
+ &proxy->pci_dev);
if (ret < 0) {
return ret;
}
- kvm_irqchip_commit_routes(kvm_state);
+ accel_irqchip_commit_routes();
}
}
@@ -1229,7 +1230,7 @@ static int virtio_pci_set_guest_notifiers(DeviceState *d, int nvqs, bool assign)
VirtioDeviceClass *k = VIRTIO_DEVICE_GET_CLASS(vdev);
int r, n;
bool with_irqfd = msix_enabled(&proxy->pci_dev) &&
- kvm_msi_via_irqfd_enabled();
+ accel_msi_via_irqfd_enabled() ;
nvqs = MIN(nvqs, VIRTIO_QUEUE_MAX);
@@ -1433,7 +1434,7 @@ static void virtio_pci_set_vector(VirtIODevice *vdev,
uint16_t new_vector)
{
bool kvm_irqfd = (vdev->status & VIRTIO_CONFIG_S_DRIVER_OK) &&
- msix_enabled(&proxy->pci_dev) && kvm_msi_via_irqfd_enabled();
+ msix_enabled(&proxy->pci_dev) && accel_msi_via_irqfd_enabled();
if (new_vector == old_vector) {
return;
diff --git a/include/system/accel-irq.h b/include/system/accel-irq.h
new file mode 100644
index 0000000000..671fb7dfdb
--- /dev/null
+++ b/include/system/accel-irq.h
@@ -0,0 +1,37 @@
+/*
+ * Accelerated irqchip abstraction
+ *
+ * Copyright Microsoft, Corp. 2025
+ *
+ * Authors: Ziqiao Zhou <ziqiaozhou@microsoft.com>
+ * Magnus Kulke <magnuskulke@microsoft.com>
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+
+#ifndef SYSTEM_ACCEL_IRQ_H
+#define SYSTEM_ACCEL_IRQ_H
+#include "hw/pci/msi.h"
+#include "qemu/osdep.h"
+#include "system/kvm.h"
+#include "system/mshv.h"
+
+static inline bool accel_msi_via_irqfd_enabled(void)
+{
+ return mshv_msi_via_irqfd_enabled() || kvm_msi_via_irqfd_enabled();
+}
+
+static inline bool accel_irqchip_is_split(void)
+{
+ return mshv_msi_via_irqfd_enabled() || kvm_irqchip_is_split();
+}
+
+int accel_irqchip_add_msi_route(KVMRouteChange *c, int vector, PCIDevice *dev);
+int accel_irqchip_update_msi_route(int vector, MSIMessage msg, PCIDevice *dev);
+void accel_irqchip_commit_route_changes(KVMRouteChange *c);
+void accel_irqchip_commit_routes(void);
+void accel_irqchip_release_virq(int virq);
+int accel_irqchip_add_irqfd_notifier_gsi(EventNotifier *n, EventNotifier *rn,
+ int virq);
+int accel_irqchip_remove_irqfd_notifier_gsi(EventNotifier *n, int virq);
+#endif
diff --git a/include/system/mshv.h b/include/system/mshv.h
index 342f1ef6a9..2a504ed81f 100644
--- a/include/system/mshv.h
+++ b/include/system/mshv.h
@@ -22,4 +22,21 @@
#define CONFIG_MSHV_IS_POSSIBLE
#endif
+#ifdef CONFIG_MSHV_IS_POSSIBLE
+extern bool mshv_allowed;
+#define mshv_enabled() (mshv_allowed)
+#else /* CONFIG_MSHV_IS_POSSIBLE */
+#define mshv_enabled() false
+#endif
+#define mshv_msi_via_irqfd_enabled() false
+
+/* interrupt */
+int mshv_irqchip_add_msi_route(int vector, PCIDevice *dev);
+int mshv_irqchip_update_msi_route(int virq, MSIMessage msg, PCIDevice *dev);
+void mshv_irqchip_commit_routes(void);
+void mshv_irqchip_release_virq(int virq);
+int mshv_irqchip_add_irqfd_notifier_gsi(const EventNotifier *n,
+ const EventNotifier *rn, int virq);
+int mshv_irqchip_remove_irqfd_notifier_gsi(const EventNotifier *n, int virq);
+
#endif
--
2.51.1

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,329 @@
From 9515494b6a17b26bca1451976a54b53a0524d65f Mon Sep 17 00:00:00 2001
From: Magnus Kulke <magnuskulke@linux.microsoft.com>
Date: Tue, 16 Sep 2025 18:48:26 +0200
Subject: [PATCH 07/31] linux-headers/linux: Add mshv.h headers
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
RH-Author: Igor Mammedov <imammedo@redhat.com>
RH-MergeRequest: 435: x86: enablement for Azure L1VH OCP readiness
RH-Jira: RHEL-132193
RH-Acked-by: Vitaly Kuznetsov <vkuznets@redhat.com>
RH-Acked-by: Miroslav Rezanina <mrezanin@redhat.com>
RH-Commit: [6/30] 866a40c74aede97a9b1a5ac50651eb13face82bd
This file has been added to the tree by running `update-linux-header.sh`
on linux v6.16.
Signed-off-by: Magnus Kulke <magnuskulke@linux.microsoft.com>
Reviewed-by: Daniel P. Berrangé <berrange@redhat.com>
Link: https://lore.kernel.org/r/20250916164847.77883-7-magnuskulke@linux.microsoft.com
Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
(cherry picked from commit a6d6878650a0f4982918e8cd9d7ea6c3c3c681f7)
Signed-off-by: Igor Mammedov <imammedo@redhat.com>
---
linux-headers/linux/mshv.h | 291 +++++++++++++++++++++++++++++++++++++
1 file changed, 291 insertions(+)
create mode 100644 linux-headers/linux/mshv.h
diff --git a/linux-headers/linux/mshv.h b/linux-headers/linux/mshv.h
new file mode 100644
index 0000000000..5bc83db6a3
--- /dev/null
+++ b/linux-headers/linux/mshv.h
@@ -0,0 +1,291 @@
+/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */
+/*
+ * Userspace interfaces for /dev/mshv* devices and derived fds
+ *
+ * This file is divided into sections containing data structures and IOCTLs for
+ * a particular set of related devices or derived file descriptors.
+ *
+ * The IOCTL definitions are at the end of each section. They are grouped by
+ * device/fd, so that new IOCTLs can easily be added with a monotonically
+ * increasing number.
+ */
+#ifndef _LINUX_MSHV_H
+#define _LINUX_MSHV_H
+
+#include <linux/types.h>
+
+#define MSHV_IOCTL 0xB8
+
+/*
+ *******************************************
+ * Entry point to main VMM APIs: /dev/mshv *
+ *******************************************
+ */
+
+enum {
+ MSHV_PT_BIT_LAPIC,
+ MSHV_PT_BIT_X2APIC,
+ MSHV_PT_BIT_GPA_SUPER_PAGES,
+ MSHV_PT_BIT_COUNT,
+};
+
+#define MSHV_PT_FLAGS_MASK ((1 << MSHV_PT_BIT_COUNT) - 1)
+
+enum {
+ MSHV_PT_ISOLATION_NONE,
+ MSHV_PT_ISOLATION_COUNT,
+};
+
+/**
+ * struct mshv_create_partition - arguments for MSHV_CREATE_PARTITION
+ * @pt_flags: Bitmask of 1 << MSHV_PT_BIT_*
+ * @pt_isolation: MSHV_PT_ISOLATION_*
+ *
+ * Returns a file descriptor to act as a handle to a guest partition.
+ * At this point the partition is not yet initialized in the hypervisor.
+ * Some operations must be done with the partition in this state, e.g. setting
+ * so-called "early" partition properties. The partition can then be
+ * initialized with MSHV_INITIALIZE_PARTITION.
+ */
+struct mshv_create_partition {
+ __u64 pt_flags;
+ __u64 pt_isolation;
+};
+
+/* /dev/mshv */
+#define MSHV_CREATE_PARTITION _IOW(MSHV_IOCTL, 0x00, struct mshv_create_partition)
+
+/*
+ ************************
+ * Child partition APIs *
+ ************************
+ */
+
+struct mshv_create_vp {
+ __u32 vp_index;
+};
+
+enum {
+ MSHV_SET_MEM_BIT_WRITABLE,
+ MSHV_SET_MEM_BIT_EXECUTABLE,
+ MSHV_SET_MEM_BIT_UNMAP,
+ MSHV_SET_MEM_BIT_COUNT
+};
+
+#define MSHV_SET_MEM_FLAGS_MASK ((1 << MSHV_SET_MEM_BIT_COUNT) - 1)
+
+/* The hypervisor's "native" page size */
+#define MSHV_HV_PAGE_SIZE 0x1000
+
+/**
+ * struct mshv_user_mem_region - arguments for MSHV_SET_GUEST_MEMORY
+ * @size: Size of the memory region (bytes). Must be aligned to
+ * MSHV_HV_PAGE_SIZE
+ * @guest_pfn: Base guest page number to map
+ * @userspace_addr: Base address of userspace memory. Must be aligned to
+ * MSHV_HV_PAGE_SIZE
+ * @flags: Bitmask of 1 << MSHV_SET_MEM_BIT_*. If (1 << MSHV_SET_MEM_BIT_UNMAP)
+ * is set, ignore other bits.
+ * @rsvd: MBZ
+ *
+ * Map or unmap a region of userspace memory to Guest Physical Addresses (GPA).
+ * Mappings can't overlap in GPA space or userspace.
+ * To unmap, these fields must match an existing mapping.
+ */
+struct mshv_user_mem_region {
+ __u64 size;
+ __u64 guest_pfn;
+ __u64 userspace_addr;
+ __u8 flags;
+ __u8 rsvd[7];
+};
+
+enum {
+ MSHV_IRQFD_BIT_DEASSIGN,
+ MSHV_IRQFD_BIT_RESAMPLE,
+ MSHV_IRQFD_BIT_COUNT,
+};
+
+#define MSHV_IRQFD_FLAGS_MASK ((1 << MSHV_IRQFD_BIT_COUNT) - 1)
+
+struct mshv_user_irqfd {
+ __s32 fd;
+ __s32 resamplefd;
+ __u32 gsi;
+ __u32 flags;
+};
+
+enum {
+ MSHV_IOEVENTFD_BIT_DATAMATCH,
+ MSHV_IOEVENTFD_BIT_PIO,
+ MSHV_IOEVENTFD_BIT_DEASSIGN,
+ MSHV_IOEVENTFD_BIT_COUNT,
+};
+
+#define MSHV_IOEVENTFD_FLAGS_MASK ((1 << MSHV_IOEVENTFD_BIT_COUNT) - 1)
+
+struct mshv_user_ioeventfd {
+ __u64 datamatch;
+ __u64 addr; /* legal pio/mmio address */
+ __u32 len; /* 1, 2, 4, or 8 bytes */
+ __s32 fd;
+ __u32 flags;
+ __u8 rsvd[4];
+};
+
+struct mshv_user_irq_entry {
+ __u32 gsi;
+ __u32 address_lo;
+ __u32 address_hi;
+ __u32 data;
+};
+
+struct mshv_user_irq_table {
+ __u32 nr;
+ __u32 rsvd; /* MBZ */
+ struct mshv_user_irq_entry entries[];
+};
+
+enum {
+ MSHV_GPAP_ACCESS_TYPE_ACCESSED,
+ MSHV_GPAP_ACCESS_TYPE_DIRTY,
+ MSHV_GPAP_ACCESS_TYPE_COUNT /* Count of enum members */
+};
+
+enum {
+ MSHV_GPAP_ACCESS_OP_NOOP,
+ MSHV_GPAP_ACCESS_OP_CLEAR,
+ MSHV_GPAP_ACCESS_OP_SET,
+ MSHV_GPAP_ACCESS_OP_COUNT /* Count of enum members */
+};
+
+/**
+ * struct mshv_gpap_access_bitmap - arguments for MSHV_GET_GPAP_ACCESS_BITMAP
+ * @access_type: MSHV_GPAP_ACCESS_TYPE_* - The type of access to record in the
+ * bitmap
+ * @access_op: MSHV_GPAP_ACCESS_OP_* - Allows an optional clear or set of all
+ * the access states in the range, after retrieving the current
+ * states.
+ * @rsvd: MBZ
+ * @page_count: Number of pages
+ * @gpap_base: Base gpa page number
+ * @bitmap_ptr: Output buffer for bitmap, at least (page_count + 7) / 8 bytes
+ *
+ * Retrieve a bitmap of either ACCESSED or DIRTY bits for a given range of guest
+ * memory, and optionally clear or set the bits.
+ */
+struct mshv_gpap_access_bitmap {
+ __u8 access_type;
+ __u8 access_op;
+ __u8 rsvd[6];
+ __u64 page_count;
+ __u64 gpap_base;
+ __u64 bitmap_ptr;
+};
+
+/**
+ * struct mshv_root_hvcall - arguments for MSHV_ROOT_HVCALL
+ * @code: Hypercall code (HVCALL_*)
+ * @reps: in: Rep count ('repcount')
+ * out: Reps completed ('repcomp'). MBZ unless rep hvcall
+ * @in_sz: Size of input incl rep data. <= MSHV_HV_PAGE_SIZE
+ * @out_sz: Size of output buffer. <= MSHV_HV_PAGE_SIZE. MBZ if out_ptr is 0
+ * @status: in: MBZ
+ * out: HV_STATUS_* from hypercall
+ * @rsvd: MBZ
+ * @in_ptr: Input data buffer (struct hv_input_*). If used with partition or
+ * vp fd, partition id field is populated by kernel.
+ * @out_ptr: Output data buffer (optional)
+ */
+struct mshv_root_hvcall {
+ __u16 code;
+ __u16 reps;
+ __u16 in_sz;
+ __u16 out_sz;
+ __u16 status;
+ __u8 rsvd[6];
+ __u64 in_ptr;
+ __u64 out_ptr;
+};
+
+/* Partition fds created with MSHV_CREATE_PARTITION */
+#define MSHV_INITIALIZE_PARTITION _IO(MSHV_IOCTL, 0x00)
+#define MSHV_CREATE_VP _IOW(MSHV_IOCTL, 0x01, struct mshv_create_vp)
+#define MSHV_SET_GUEST_MEMORY _IOW(MSHV_IOCTL, 0x02, struct mshv_user_mem_region)
+#define MSHV_IRQFD _IOW(MSHV_IOCTL, 0x03, struct mshv_user_irqfd)
+#define MSHV_IOEVENTFD _IOW(MSHV_IOCTL, 0x04, struct mshv_user_ioeventfd)
+#define MSHV_SET_MSI_ROUTING _IOW(MSHV_IOCTL, 0x05, struct mshv_user_irq_table)
+#define MSHV_GET_GPAP_ACCESS_BITMAP _IOWR(MSHV_IOCTL, 0x06, struct mshv_gpap_access_bitmap)
+/* Generic hypercall */
+#define MSHV_ROOT_HVCALL _IOWR(MSHV_IOCTL, 0x07, struct mshv_root_hvcall)
+
+/*
+ ********************************
+ * VP APIs for child partitions *
+ ********************************
+ */
+
+#define MSHV_RUN_VP_BUF_SZ 256
+
+/*
+ * VP state pages may be mapped to userspace via mmap().
+ * To specify which state page, use MSHV_VP_MMAP_OFFSET_ values multiplied by
+ * the system page size.
+ * e.g.
+ * long page_size = sysconf(_SC_PAGE_SIZE);
+ * void *reg_page = mmap(NULL, MSHV_HV_PAGE_SIZE, PROT_READ|PROT_WRITE,
+ * MAP_SHARED, vp_fd,
+ * MSHV_VP_MMAP_OFFSET_REGISTERS * page_size);
+ */
+enum {
+ MSHV_VP_MMAP_OFFSET_REGISTERS,
+ MSHV_VP_MMAP_OFFSET_INTERCEPT_MESSAGE,
+ MSHV_VP_MMAP_OFFSET_GHCB,
+ MSHV_VP_MMAP_OFFSET_COUNT
+};
+
+/**
+ * struct mshv_run_vp - argument for MSHV_RUN_VP
+ * @msg_buf: On success, the intercept message is copied here. It can be
+ * interpreted using the relevant hypervisor definitions.
+ */
+struct mshv_run_vp {
+ __u8 msg_buf[MSHV_RUN_VP_BUF_SZ];
+};
+
+enum {
+ MSHV_VP_STATE_LAPIC, /* Local interrupt controller state (either arch) */
+ MSHV_VP_STATE_XSAVE, /* XSAVE data in compacted form (x86_64) */
+ MSHV_VP_STATE_SIMP,
+ MSHV_VP_STATE_SIEFP,
+ MSHV_VP_STATE_SYNTHETIC_TIMERS,
+ MSHV_VP_STATE_COUNT,
+};
+
+/**
+ * struct mshv_get_set_vp_state - arguments for MSHV_[GET,SET]_VP_STATE
+ * @type: MSHV_VP_STATE_*
+ * @rsvd: MBZ
+ * @buf_sz: in: 4k page-aligned size of buffer
+ * out: Actual size of data (on EINVAL, check this to see if buffer
+ * was too small)
+ * @buf_ptr: 4k page-aligned data buffer
+ */
+struct mshv_get_set_vp_state {
+ __u8 type;
+ __u8 rsvd[3];
+ __u32 buf_sz;
+ __u64 buf_ptr;
+};
+
+/* VP fds created with MSHV_CREATE_VP */
+#define MSHV_RUN_VP _IOR(MSHV_IOCTL, 0x00, struct mshv_run_vp)
+#define MSHV_GET_VP_STATE _IOWR(MSHV_IOCTL, 0x01, struct mshv_get_set_vp_state)
+#define MSHV_SET_VP_STATE _IOWR(MSHV_IOCTL, 0x02, struct mshv_get_set_vp_state)
+/*
+ * Generic hypercall
+ * Defined above in partition IOCTLs, avoid redefining it here
+ * #define MSHV_ROOT_HVCALL _IOWR(MSHV_IOCTL, 0x07, struct mshv_root_hvcall)
+ */
+
+#endif
--
2.51.1

View File

@ -0,0 +1,74 @@
From 12f78b7e20b751e64a3de3e594579673956d60dc Mon Sep 17 00:00:00 2001
From: Stefan Hajnoczi <stefanha@redhat.com>
Date: Wed, 24 Sep 2025 11:51:53 -0400
Subject: [PATCH 01/31] pcie_sriov: make pcie_sriov_pf_exit() safe on
non-SR-IOV devices
RH-Author: Stefan Hajnoczi <stefanha@redhat.com>
RH-MergeRequest: 434: pcie_sriov: make pcie_sriov_pf_exit() safe on non-SR-IOV devices
RH-Jira: RHEL-131144
RH-Acked-by: Kevin Wolf <kwolf@redhat.com>
RH-Acked-by: Miroslav Rezanina <mrezanin@redhat.com>
RH-Commit: [1/1] 3b9b7e7385fe8e3700865955f6c3b9a350e8a545 (stefanha/centos-stream-qemu-kvm)
Commit 3f9cfaa92c96 ("virtio-pci: Implement SR-IOV PF") added an
unconditional call from virtio_pci_exit() to pcie_sriov_pf_exit().
pcie_sriov_pf_exit() reads from the SR-IOV Capability in Configuration
Space:
uint8_t *cfg = dev->config + dev->exp.sriov_cap;
...
unparent_vfs(dev, pci_get_word(cfg + PCI_SRIOV_TOTAL_VF));
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
This results in undefined behavior when dev->exp.sriov_cap is 0 because
this is not an SR-IOV device. For example, unparent_vfs() segfaults when
total_vfs happens to be non-zero.
Fix this by returning early from pcie_sriov_pf_exit() when
dev->exp.sriov_cap is 0 because this is not an SR-IOV device.
Cc: Akihiko Odaki <odaki@rsg.ci.i.u-tokyo.ac.jp>
Cc: Michael S. Tsirkin <mst@redhat.com>
Reported-by: Qing Wang <qinwang@redhat.com>
Buglink: https://issues.redhat.com/browse/RHEL-116443
Signed-off-by: Stefan Hajnoczi <stefanha@redhat.com>
Reviewed-by: Akihiko Odaki <odaki@rsg.ci.i.u-tokyo.ac.jp>
Fixes: cab1398a60eb ("pcie_sriov: Reuse SR-IOV VF device instances")
Reviewed-by: Michael S. Tsirkin <mst@redhat.com>
Message-ID: <20250924155153.579495-1-stefanha@redhat.com>
Signed-off-by: Michael S. Tsirkin <mst@redhat.com>
(cherry picked from commit bab681f752048c3bc22d561b1d314c7ec16419c9)
Signed-off-by: Stefan Hajnoczi <stefanha@redhat.com>
---
hw/pci/pcie_sriov.c | 6 +++++-
1 file changed, 5 insertions(+), 1 deletion(-)
diff --git a/hw/pci/pcie_sriov.c b/hw/pci/pcie_sriov.c
index 8a4bf0d6f7..cf1b5b5c05 100644
--- a/hw/pci/pcie_sriov.c
+++ b/hw/pci/pcie_sriov.c
@@ -195,7 +195,9 @@ bool pcie_sriov_pf_init(PCIDevice *dev, uint16_t offset,
void pcie_sriov_pf_exit(PCIDevice *dev)
{
- uint8_t *cfg = dev->config + dev->exp.sriov_cap;
+ if (dev->exp.sriov_cap == 0) {
+ return;
+ }
if (dev->exp.sriov_pf.vf_user_created) {
uint16_t ven_id = pci_get_word(dev->config + PCI_VENDOR_ID);
@@ -211,6 +213,8 @@ void pcie_sriov_pf_exit(PCIDevice *dev)
pci_config_set_device_id(dev->exp.sriov_pf.vf[i]->config, vf_dev_id);
}
} else {
+ uint8_t *cfg = dev->config + dev->exp.sriov_cap;
+
unparent_vfs(dev, pci_get_word(cfg + PCI_SRIOV_TOTAL_VF));
}
}
--
2.51.1

View File

@ -0,0 +1,169 @@
From 5c048c7fe16c14e864571824fefbf5749bfed34a Mon Sep 17 00:00:00 2001
From: Praveen K Paladugu <prapal@microsoft.com>
Date: Tue, 16 Sep 2025 18:48:44 +0200
Subject: [PATCH 26/31] qapi/accel: Allow to query mshv capabilities
RH-Author: Igor Mammedov <imammedo@redhat.com>
RH-MergeRequest: 435: x86: enablement for Azure L1VH OCP readiness
RH-Jira: RHEL-132193
RH-Acked-by: Vitaly Kuznetsov <vkuznets@redhat.com>
RH-Acked-by: Miroslav Rezanina <mrezanin@redhat.com>
RH-Commit: [25/30] 951d3f2530367f0d95b23b712227384229161cd7
Allow to query mshv capabilities via query-mshv QMP and info mshv HMP commands.
Signed-off-by: Magnus Kulke <magnuskulke@linux.microsoft.com>
Acked-by: Dr. David Alan Gilbert <dave@treblig.org>
Link: https://lore.kernel.org/r/20250916164847.77883-25-magnuskulke@linux.microsoft.com
[Fix "since" version. - Paolo]
Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
(cherry picked from commit e7b08dfb902430b4f8226d23d7cf9b2762b6fc83)
Signed-off-by: Igor Mammedov <imammedo@redhat.com>
---
hmp-commands-info.hx | 13 +++++++++++++
hw/core/machine-hmp-cmds.c | 15 +++++++++++++++
hw/core/machine-qmp-cmds.c | 14 ++++++++++++++
include/monitor/hmp.h | 1 +
include/system/hw_accel.h | 1 +
qapi/accelerator.json | 29 +++++++++++++++++++++++++++++
6 files changed, 73 insertions(+)
diff --git a/hmp-commands-info.hx b/hmp-commands-info.hx
index 6142f60e7b..eaaa880c1b 100644
--- a/hmp-commands-info.hx
+++ b/hmp-commands-info.hx
@@ -307,6 +307,19 @@ SRST
Show KVM information.
ERST
+ {
+ .name = "mshv",
+ .args_type = "",
+ .params = "",
+ .help = "show MSHV information",
+ .cmd = hmp_info_mshv,
+ },
+
+SRST
+ ``info mshv``
+ Show MSHV information.
+ERST
+
{
.name = "numa",
.args_type = "",
diff --git a/hw/core/machine-hmp-cmds.c b/hw/core/machine-hmp-cmds.c
index 3a612e2232..682ed9f49b 100644
--- a/hw/core/machine-hmp-cmds.c
+++ b/hw/core/machine-hmp-cmds.c
@@ -163,6 +163,21 @@ void hmp_info_kvm(Monitor *mon, const QDict *qdict)
qapi_free_KvmInfo(info);
}
+void hmp_info_mshv(Monitor *mon, const QDict *qdict)
+{
+ MshvInfo *info;
+
+ info = qmp_query_mshv(NULL);
+ monitor_printf(mon, "mshv support: ");
+ if (info->present) {
+ monitor_printf(mon, "%s\n", info->enabled ? "enabled" : "disabled");
+ } else {
+ monitor_printf(mon, "not compiled\n");
+ }
+
+ qapi_free_MshvInfo(info);
+}
+
void hmp_info_uuid(Monitor *mon, const QDict *qdict)
{
UuidInfo *info;
diff --git a/hw/core/machine-qmp-cmds.c b/hw/core/machine-qmp-cmds.c
index 6aca1a626e..e24bf0d97b 100644
--- a/hw/core/machine-qmp-cmds.c
+++ b/hw/core/machine-qmp-cmds.c
@@ -28,6 +28,20 @@
#include "system/runstate.h"
#include "system/system.h"
#include "hw/s390x/storage-keys.h"
+#include <sys/stat.h>
+
+/*
+ * QMP query for MSHV
+ */
+MshvInfo *qmp_query_mshv(Error **errp)
+{
+ MshvInfo *info = g_malloc0(sizeof(*info));
+
+ info->enabled = mshv_enabled();
+ info->present = accel_find("mshv");
+
+ return info;
+}
/*
* fast means: we NEVER interrupt vCPU threads to retrieve
diff --git a/include/monitor/hmp.h b/include/monitor/hmp.h
index ae116d9804..31bd812e5f 100644
--- a/include/monitor/hmp.h
+++ b/include/monitor/hmp.h
@@ -24,6 +24,7 @@ strList *hmp_split_at_comma(const char *str);
void hmp_info_name(Monitor *mon, const QDict *qdict);
void hmp_info_version(Monitor *mon, const QDict *qdict);
void hmp_info_kvm(Monitor *mon, const QDict *qdict);
+void hmp_info_mshv(Monitor *mon, const QDict *qdict);
void hmp_info_status(Monitor *mon, const QDict *qdict);
void hmp_info_uuid(Monitor *mon, const QDict *qdict);
void hmp_info_chardev(Monitor *mon, const QDict *qdict);
diff --git a/include/system/hw_accel.h b/include/system/hw_accel.h
index fa9228d5d2..55497edc29 100644
--- a/include/system/hw_accel.h
+++ b/include/system/hw_accel.h
@@ -14,6 +14,7 @@
#include "hw/core/cpu.h"
#include "system/kvm.h"
#include "system/hvf.h"
+#include "system/mshv.h"
#include "system/whpx.h"
#include "system/nvmm.h"
diff --git a/qapi/accelerator.json b/qapi/accelerator.json
index fb28c8d920..664e027246 100644
--- a/qapi/accelerator.json
+++ b/qapi/accelerator.json
@@ -54,3 +54,32 @@
{ 'command': 'x-accel-stats',
'returns': 'HumanReadableText',
'features': [ 'unstable' ] }
+
+##
+# @MshvInfo:
+#
+# Information about support for MSHV acceleration
+#
+# @enabled: true if MSHV acceleration is active
+#
+# @present: true if MSHV acceleration is built into this executable
+#
+# Since: 10.2.0
+##
+{ 'struct': 'MshvInfo', 'data': {'enabled': 'bool', 'present': 'bool'} }
+
+##
+# @query-mshv:
+#
+# Return information about MSHV acceleration
+#
+# Returns: @MshvInfo
+#
+# Since: 10.0.92
+#
+# .. qmp-example::
+#
+# -> { "execute": "query-mshv" }
+# <- { "return": { "enabled": true, "present": true } }
+##
+{ 'command': 'query-mshv', 'returns': 'MshvInfo' }
--
2.51.1

View File

@ -0,0 +1,150 @@
From be2a68162ad72e30332382b5d9e46458b3ac50d3 Mon Sep 17 00:00:00 2001
From: Magnus Kulke <magnuskulke@linux.microsoft.com>
Date: Tue, 16 Sep 2025 18:48:22 +0200
Subject: [PATCH 03/31] target/i386/emulate: Allow instruction decoding from
stream
RH-Author: Igor Mammedov <imammedo@redhat.com>
RH-MergeRequest: 435: x86: enablement for Azure L1VH OCP readiness
RH-Jira: RHEL-132193
RH-Acked-by: Vitaly Kuznetsov <vkuznets@redhat.com>
RH-Acked-by: Miroslav Rezanina <mrezanin@redhat.com>
RH-Commit: [2/30] a2c8de87daaf40fd824b5f0e1c07f435dc5085f8
Introduce a new helper function to decode x86 instructions from a
raw instruction byte stream. MSHV delivers an instruction stream in a
buffer of the vm_exit message. It can be used to speed up MMIO
emulation, since instructions do not have to be fetched and translated.
Added "fetch_instruction()" op to x86_emul_ops() to improve
traceability.
Signed-off-by: Magnus Kulke <magnuskulke@linux.microsoft.com>
Link: https://lore.kernel.org/r/20250916164847.77883-3-magnuskulke@linux.microsoft.com
Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
(cherry picked from commit 1e25327b244a217078e3fa5df18322c70932f478)
Signed-off-by: Igor Mammedov <imammedo@redhat.com>
---
target/i386/emulate/x86_decode.c | 27 +++++++++++++++++++++++----
target/i386/emulate/x86_decode.h | 9 +++++++++
target/i386/emulate/x86_emu.c | 3 ++-
target/i386/emulate/x86_emu.h | 2 ++
4 files changed, 36 insertions(+), 5 deletions(-)
diff --git a/target/i386/emulate/x86_decode.c b/target/i386/emulate/x86_decode.c
index 2eca39802e..97bd6f1a3b 100644
--- a/target/i386/emulate/x86_decode.c
+++ b/target/i386/emulate/x86_decode.c
@@ -71,10 +71,16 @@ static inline uint64_t decode_bytes(CPUX86State *env, struct x86_decode *decode,
VM_PANIC_EX("%s invalid size %d\n", __func__, size);
break;
}
- target_ulong va = linear_rip(env_cpu(env), env->eip) + decode->len;
- emul_ops->read_mem(env_cpu(env), &val, va, size);
+
+ /* copy the bytes from the instruction stream, if available */
+ if (decode->stream && decode->len + size <= decode->stream->len) {
+ memcpy(&val, decode->stream->bytes + decode->len, size);
+ } else {
+ target_ulong va = linear_rip(env_cpu(env), env->eip) + decode->len;
+ emul_ops->fetch_instruction(env_cpu(env), &val, va, size);
+ }
decode->len += size;
-
+
return val;
}
@@ -2076,9 +2082,10 @@ static void decode_opcodes(CPUX86State *env, struct x86_decode *decode)
}
}
-uint32_t decode_instruction(CPUX86State *env, struct x86_decode *decode)
+static uint32_t decode_opcode(CPUX86State *env, struct x86_decode *decode)
{
memset(decode, 0, sizeof(*decode));
+
decode_prefix(env, decode);
set_addressing_size(env, decode);
set_operand_size(env, decode);
@@ -2088,6 +2095,18 @@ uint32_t decode_instruction(CPUX86State *env, struct x86_decode *decode)
return decode->len;
}
+uint32_t decode_instruction(CPUX86State *env, struct x86_decode *decode)
+{
+ return decode_opcode(env, decode);
+}
+
+uint32_t decode_instruction_stream(CPUX86State *env, struct x86_decode *decode,
+ struct x86_insn_stream *stream)
+{
+ decode->stream = stream;
+ return decode_opcode(env, decode);
+}
+
void init_decoder(void)
{
int i;
diff --git a/target/i386/emulate/x86_decode.h b/target/i386/emulate/x86_decode.h
index 927645af1a..1cadf3694f 100644
--- a/target/i386/emulate/x86_decode.h
+++ b/target/i386/emulate/x86_decode.h
@@ -272,6 +272,11 @@ typedef struct x86_decode_op {
};
} x86_decode_op;
+typedef struct x86_insn_stream {
+ const uint8_t *bytes;
+ size_t len;
+} x86_insn_stream;
+
typedef struct x86_decode {
int len;
uint8_t opcode[4];
@@ -298,11 +303,15 @@ typedef struct x86_decode {
struct x86_modrm modrm;
struct x86_decode_op op[4];
bool is_fpu;
+
+ x86_insn_stream *stream;
} x86_decode;
uint64_t sign(uint64_t val, int size);
uint32_t decode_instruction(CPUX86State *env, struct x86_decode *decode);
+uint32_t decode_instruction_stream(CPUX86State *env, struct x86_decode *decode,
+ struct x86_insn_stream *stream);
void *get_reg_ref(CPUX86State *env, int reg, int rex_present,
int is_extended, int size);
diff --git a/target/i386/emulate/x86_emu.c b/target/i386/emulate/x86_emu.c
index db7a7f7437..4409f7bc13 100644
--- a/target/i386/emulate/x86_emu.c
+++ b/target/i386/emulate/x86_emu.c
@@ -1246,7 +1246,8 @@ static void init_cmd_handler(void)
bool exec_instruction(CPUX86State *env, struct x86_decode *ins)
{
if (!_cmd_handler[ins->cmd].handler) {
- printf("Unimplemented handler (" TARGET_FMT_lx ") for %d (%x %x) \n", env->eip,
+ printf("Unimplemented handler (" TARGET_FMT_lx ") for %d (%x %x)\n",
+ env->eip,
ins->cmd, ins->opcode[0],
ins->opcode_len > 1 ? ins->opcode[1] : 0);
env->eip += ins->len;
diff --git a/target/i386/emulate/x86_emu.h b/target/i386/emulate/x86_emu.h
index a1a961284b..05686b162f 100644
--- a/target/i386/emulate/x86_emu.h
+++ b/target/i386/emulate/x86_emu.h
@@ -24,6 +24,8 @@
#include "cpu.h"
struct x86_emul_ops {
+ void (*fetch_instruction)(CPUState *cpu, void *data, target_ulong addr,
+ int bytes);
void (*read_mem)(CPUState *cpu, void *data, target_ulong addr, int bytes);
void (*write_mem)(CPUState *cpu, void *data, target_ulong addr, int bytes);
void (*read_segment_descriptor)(CPUState *cpu, struct x86_segment_descriptor *desc,
--
2.51.1

View File

@ -0,0 +1,76 @@
From 4fe545d2156dca3a77dad94b858314f75cf6b8c9 Mon Sep 17 00:00:00 2001
From: Magnus Kulke <magnuskulke@linux.microsoft.com>
Date: Tue, 16 Sep 2025 18:48:32 +0200
Subject: [PATCH 14/31] target/i386/mshv: Add CPU create and remove logic
RH-Author: Igor Mammedov <imammedo@redhat.com>
RH-MergeRequest: 435: x86: enablement for Azure L1VH OCP readiness
RH-Jira: RHEL-132193
RH-Acked-by: Vitaly Kuznetsov <vkuznets@redhat.com>
RH-Acked-by: Miroslav Rezanina <mrezanin@redhat.com>
RH-Commit: [13/30] 8354d97d26446ff93646331936b6adbb0f12f293
Implement MSHV-specific hooks for vCPU creation and teardown in the
i386 target.
Signed-off-by: Magnus Kulke <magnuskulke@linux.microsoft.com>
Link: https://lore.kernel.org/r/20250916164847.77883-13-magnuskulke@linux.microsoft.com
Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
(cherry picked from commit 4ed605c06e743b2ec0bfe35954d88b8c64dd3767)
Signed-off-by: Igor Mammedov <imammedo@redhat.com>
---
target/i386/mshv/mshv-cpu.c | 23 +++++++++++++++++------
1 file changed, 17 insertions(+), 6 deletions(-)
diff --git a/target/i386/mshv/mshv-cpu.c b/target/i386/mshv/mshv-cpu.c
index 02d71ebc14..5069ab7a22 100644
--- a/target/i386/mshv/mshv-cpu.c
+++ b/target/i386/mshv/mshv-cpu.c
@@ -30,6 +30,8 @@
#include "trace-accel_mshv.h"
#include "trace.h"
+#include <sys/ioctl.h>
+
int mshv_store_regs(CPUState *cpu)
{
error_report("unimplemented");
@@ -62,20 +64,29 @@ int mshv_run_vcpu(int vm_fd, CPUState *cpu, hv_message *msg, MshvVmExit *exit)
void mshv_remove_vcpu(int vm_fd, int cpu_fd)
{
- error_report("unimplemented");
- abort();
+ close(cpu_fd);
}
+
int mshv_create_vcpu(int vm_fd, uint8_t vp_index, int *cpu_fd)
{
- error_report("unimplemented");
- abort();
+ int ret;
+ struct mshv_create_vp vp_arg = {
+ .vp_index = vp_index,
+ };
+ ret = ioctl(vm_fd, MSHV_CREATE_VP, &vp_arg);
+ if (ret < 0) {
+ error_report("failed to create mshv vcpu: %s", strerror(errno));
+ return -1;
+ }
+
+ *cpu_fd = ret;
+
+ return 0;
}
void mshv_init_mmio_emu(void)
{
- error_report("unimplemented");
- abort();
}
void mshv_arch_init_vcpu(CPUState *cpu)
--
2.51.1

View File

@ -0,0 +1,431 @@
From c44215842fbec5613df55d175ce9917a5767ca60 Mon Sep 17 00:00:00 2001
From: Magnus Kulke <magnuskulke@linux.microsoft.com>
Date: Tue, 16 Sep 2025 18:48:23 +0200
Subject: [PATCH 04/31] target/i386/mshv: Add x86 decoder/emu implementation
RH-Author: Igor Mammedov <imammedo@redhat.com>
RH-MergeRequest: 435: x86: enablement for Azure L1VH OCP readiness
RH-Jira: RHEL-132193
RH-Acked-by: Vitaly Kuznetsov <vkuznets@redhat.com>
RH-Acked-by: Miroslav Rezanina <mrezanin@redhat.com>
RH-Commit: [3/30] 927337096abcf5f1189df70e332adc25543a8729
The MSHV accelerator requires a x86 decoder/emulator in userland to
emulate MMIO instructions. This change contains the implementations for
the generalized i386 instruction decoder/emulator.
Signed-off-by: Magnus Kulke <magnuskulke@linux.microsoft.com>
Link: https://lore.kernel.org/r/20250916164847.77883-4-magnuskulke@linux.microsoft.com
Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
(cherry picked from commit 0daf817c80b57e58168309420abf0a8a3d2a60f6)
Signed-off-by: Igor Mammedov <imammedo@redhat.com>
---
include/system/mshv.h | 25 +++
target/i386/cpu.h | 2 +-
target/i386/emulate/meson.build | 7 +-
target/i386/meson.build | 2 +
target/i386/mshv/meson.build | 7 +
target/i386/mshv/x86.c | 297 ++++++++++++++++++++++++++++++++
6 files changed, 337 insertions(+), 3 deletions(-)
create mode 100644 include/system/mshv.h
create mode 100644 target/i386/mshv/meson.build
create mode 100644 target/i386/mshv/x86.c
diff --git a/include/system/mshv.h b/include/system/mshv.h
new file mode 100644
index 0000000000..342f1ef6a9
--- /dev/null
+++ b/include/system/mshv.h
@@ -0,0 +1,25 @@
+/*
+ * QEMU MSHV support
+ *
+ * Copyright Microsoft, Corp. 2025
+ *
+ * Authors: Ziqiao Zhou <ziqiaozhou@microsoft.com>
+ * Magnus Kulke <magnuskulke@microsoft.com>
+ * Jinank Jain <jinankjain@microsoft.com>
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ *
+ */
+
+#ifndef QEMU_MSHV_H
+#define QEMU_MSHV_H
+
+#ifdef COMPILING_PER_TARGET
+#ifdef CONFIG_MSHV
+#define CONFIG_MSHV_IS_POSSIBLE
+#endif
+#else
+#define CONFIG_MSHV_IS_POSSIBLE
+#endif
+
+#endif
diff --git a/target/i386/cpu.h b/target/i386/cpu.h
index 2187e61654..4b7eae43e1 100644
--- a/target/i386/cpu.h
+++ b/target/i386/cpu.h
@@ -2126,7 +2126,7 @@ typedef struct CPUArchState {
QEMUTimer *xen_periodic_timer;
QemuMutex xen_timers_lock;
#endif
-#if defined(CONFIG_HVF)
+#if defined(CONFIG_HVF) || defined(CONFIG_MSHV)
void *emu_mmio_buf;
#endif
diff --git a/target/i386/emulate/meson.build b/target/i386/emulate/meson.build
index 4edd4f462f..b6dafb6a5b 100644
--- a/target/i386/emulate/meson.build
+++ b/target/i386/emulate/meson.build
@@ -1,5 +1,8 @@
-i386_system_ss.add(when: [hvf, 'CONFIG_HVF'], if_true: files(
+emulator_files = files(
'x86_decode.c',
'x86_emu.c',
'x86_flags.c',
-))
+)
+
+i386_system_ss.add(when: [hvf, 'CONFIG_HVF'], if_true: emulator_files)
+i386_system_ss.add(when: 'CONFIG_MSHV', if_true: emulator_files)
diff --git a/target/i386/meson.build b/target/i386/meson.build
index 092af34e2d..89ba4912aa 100644
--- a/target/i386/meson.build
+++ b/target/i386/meson.build
@@ -13,6 +13,7 @@ i386_ss.add(when: 'CONFIG_KVM', if_true: files('host-cpu.c'))
i386_ss.add(when: 'CONFIG_HVF', if_true: files('host-cpu.c'))
i386_ss.add(when: 'CONFIG_WHPX', if_true: files('host-cpu.c'))
i386_ss.add(when: 'CONFIG_NVMM', if_true: files('host-cpu.c'))
+i386_ss.add(when: 'CONFIG_MSHV', if_true: files('host-cpu.c'))
i386_system_ss = ss.source_set()
i386_system_ss.add(files(
@@ -34,6 +35,7 @@ subdir('nvmm')
subdir('hvf')
subdir('tcg')
subdir('emulate')
+subdir('mshv')
target_arch += {'i386': i386_ss}
target_system_arch += {'i386': i386_system_ss}
diff --git a/target/i386/mshv/meson.build b/target/i386/mshv/meson.build
new file mode 100644
index 0000000000..8ddaa7c11d
--- /dev/null
+++ b/target/i386/mshv/meson.build
@@ -0,0 +1,7 @@
+i386_mshv_ss = ss.source_set()
+
+i386_mshv_ss.add(files(
+ 'x86.c',
+))
+
+i386_system_ss.add_all(when: 'CONFIG_MSHV', if_true: i386_mshv_ss)
diff --git a/target/i386/mshv/x86.c b/target/i386/mshv/x86.c
new file mode 100644
index 0000000000..d574b3bc52
--- /dev/null
+++ b/target/i386/mshv/x86.c
@@ -0,0 +1,297 @@
+/*
+ * QEMU MSHV support
+ *
+ * Copyright Microsoft, Corp. 2025
+ *
+ * Authors: Magnus Kulke <magnuskulke@microsoft.com>
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+
+#include "qemu/osdep.h"
+
+#include "cpu.h"
+#include "emulate/x86_decode.h"
+#include "emulate/x86_emu.h"
+#include "qemu/typedefs.h"
+#include "qemu/error-report.h"
+#include "system/mshv.h"
+
+/* RW or Exec segment */
+static const uint8_t RWRX_SEGMENT_TYPE = 0x2;
+static const uint8_t CODE_SEGMENT_TYPE = 0x8;
+static const uint8_t EXPAND_DOWN_SEGMENT_TYPE = 0x4;
+
+typedef enum CpuMode {
+ REAL_MODE,
+ PROTECTED_MODE,
+ LONG_MODE,
+} CpuMode;
+
+static CpuMode cpu_mode(CPUState *cpu)
+{
+ enum CpuMode m = REAL_MODE;
+
+ if (x86_is_protected(cpu)) {
+ m = PROTECTED_MODE;
+
+ if (x86_is_long_mode(cpu)) {
+ m = LONG_MODE;
+ }
+ }
+
+ return m;
+}
+
+static bool segment_type_ro(const SegmentCache *seg)
+{
+ uint32_t type_ = (seg->flags >> DESC_TYPE_SHIFT) & 15;
+ return (type_ & (~RWRX_SEGMENT_TYPE)) == 0;
+}
+
+static bool segment_type_code(const SegmentCache *seg)
+{
+ uint32_t type_ = (seg->flags >> DESC_TYPE_SHIFT) & 15;
+ return (type_ & CODE_SEGMENT_TYPE) != 0;
+}
+
+static bool segment_expands_down(const SegmentCache *seg)
+{
+ uint32_t type_ = (seg->flags >> DESC_TYPE_SHIFT) & 15;
+
+ if (segment_type_code(seg)) {
+ return false;
+ }
+
+ return (type_ & EXPAND_DOWN_SEGMENT_TYPE) != 0;
+}
+
+static uint32_t segment_limit(const SegmentCache *seg)
+{
+ uint32_t limit = seg->limit;
+ uint32_t granularity = (seg->flags & DESC_G_MASK) != 0;
+
+ if (granularity != 0) {
+ limit = (limit << 12) | 0xFFF;
+ }
+
+ return limit;
+}
+
+static uint8_t segment_db(const SegmentCache *seg)
+{
+ return (seg->flags >> DESC_B_SHIFT) & 1;
+}
+
+static uint32_t segment_max_limit(const SegmentCache *seg)
+{
+ if (segment_db(seg) != 0) {
+ return 0xFFFFFFFF;
+ }
+ return 0xFFFF;
+}
+
+static int linearize(CPUState *cpu,
+ target_ulong logical_addr, target_ulong *linear_addr,
+ X86Seg seg_idx)
+{
+ enum CpuMode mode;
+ X86CPU *x86_cpu = X86_CPU(cpu);
+ CPUX86State *env = &x86_cpu->env;
+ SegmentCache *seg = &env->segs[seg_idx];
+ target_ulong base = seg->base;
+ target_ulong logical_addr_32b;
+ uint32_t limit;
+ /* TODO: the emulator will not pass us "write" indicator yet */
+ bool write = false;
+
+ mode = cpu_mode(cpu);
+
+ switch (mode) {
+ case LONG_MODE:
+ if (__builtin_add_overflow(logical_addr, base, linear_addr)) {
+ error_report("Address overflow");
+ return -1;
+ }
+ break;
+ case PROTECTED_MODE:
+ case REAL_MODE:
+ if (segment_type_ro(seg) && write) {
+ error_report("Cannot write to read-only segment");
+ return -1;
+ }
+
+ logical_addr_32b = logical_addr & 0xFFFFFFFF;
+ limit = segment_limit(seg);
+
+ if (segment_expands_down(seg)) {
+ if (logical_addr_32b >= limit) {
+ error_report("Address exceeds limit (expands down)");
+ return -1;
+ }
+
+ limit = segment_max_limit(seg);
+ }
+
+ if (logical_addr_32b > limit) {
+ error_report("Address exceeds limit %u", limit);
+ return -1;
+ }
+ *linear_addr = logical_addr_32b + base;
+ break;
+ default:
+ error_report("Unknown cpu mode: %d", mode);
+ return -1;
+ }
+
+ return 0;
+}
+
+bool x86_read_segment_descriptor(CPUState *cpu,
+ struct x86_segment_descriptor *desc,
+ x86_segment_selector sel)
+{
+ target_ulong base;
+ uint32_t limit;
+ X86CPU *x86_cpu = X86_CPU(cpu);
+ CPUX86State *env = &x86_cpu->env;
+ target_ulong gva;
+
+ memset(desc, 0, sizeof(*desc));
+
+ /* valid gdt descriptors start from index 1 */
+ if (!sel.index && GDT_SEL == sel.ti) {
+ return false;
+ }
+
+ if (GDT_SEL == sel.ti) {
+ base = env->gdt.base;
+ limit = env->gdt.limit;
+ } else {
+ base = env->ldt.base;
+ limit = env->ldt.limit;
+ }
+
+ if (sel.index * 8 >= limit) {
+ return false;
+ }
+
+ gva = base + sel.index * 8;
+ emul_ops->read_mem(cpu, desc, gva, sizeof(*desc));
+
+ return true;
+}
+
+bool x86_read_call_gate(CPUState *cpu, struct x86_call_gate *idt_desc,
+ int gate)
+{
+ target_ulong base;
+ uint32_t limit;
+ X86CPU *x86_cpu = X86_CPU(cpu);
+ CPUX86State *env = &x86_cpu->env;
+ target_ulong gva;
+
+ base = env->idt.base;
+ limit = env->idt.limit;
+
+ memset(idt_desc, 0, sizeof(*idt_desc));
+ if (gate * 8 >= limit) {
+ perror("call gate exceeds idt limit");
+ return false;
+ }
+
+ gva = base + gate * 8;
+ emul_ops->read_mem(cpu, idt_desc, gva, sizeof(*idt_desc));
+
+ return true;
+}
+
+bool x86_is_protected(CPUState *cpu)
+{
+ X86CPU *x86_cpu = X86_CPU(cpu);
+ CPUX86State *env = &x86_cpu->env;
+ uint64_t cr0 = env->cr[0];
+
+ return cr0 & CR0_PE_MASK;
+}
+
+bool x86_is_real(CPUState *cpu)
+{
+ return !x86_is_protected(cpu);
+}
+
+bool x86_is_v8086(CPUState *cpu)
+{
+ X86CPU *x86_cpu = X86_CPU(cpu);
+ CPUX86State *env = &x86_cpu->env;
+ return x86_is_protected(cpu) && (env->eflags & VM_MASK);
+}
+
+bool x86_is_long_mode(CPUState *cpu)
+{
+ X86CPU *x86_cpu = X86_CPU(cpu);
+ CPUX86State *env = &x86_cpu->env;
+ uint64_t efer = env->efer;
+ uint64_t lme_lma = (MSR_EFER_LME | MSR_EFER_LMA);
+
+ return ((efer & lme_lma) == lme_lma);
+}
+
+bool x86_is_long64_mode(CPUState *cpu)
+{
+ error_report("unimplemented: is_long64_mode()");
+ abort();
+}
+
+bool x86_is_paging_mode(CPUState *cpu)
+{
+ X86CPU *x86_cpu = X86_CPU(cpu);
+ CPUX86State *env = &x86_cpu->env;
+ uint64_t cr0 = env->cr[0];
+
+ return cr0 & CR0_PG_MASK;
+}
+
+bool x86_is_pae_enabled(CPUState *cpu)
+{
+ X86CPU *x86_cpu = X86_CPU(cpu);
+ CPUX86State *env = &x86_cpu->env;
+ uint64_t cr4 = env->cr[4];
+
+ return cr4 & CR4_PAE_MASK;
+}
+
+target_ulong linear_addr(CPUState *cpu, target_ulong addr, X86Seg seg)
+{
+ int ret;
+ target_ulong linear_addr;
+
+ ret = linearize(cpu, addr, &linear_addr, seg);
+ if (ret < 0) {
+ error_report("failed to linearize address");
+ abort();
+ }
+
+ return linear_addr;
+}
+
+target_ulong linear_addr_size(CPUState *cpu, target_ulong addr, int size,
+ X86Seg seg)
+{
+ switch (size) {
+ case 2:
+ addr = (uint16_t)addr;
+ break;
+ case 4:
+ addr = (uint32_t)addr;
+ break;
+ default:
+ break;
+ }
+ return linear_addr(cpu, addr, seg);
+}
+
+target_ulong linear_rip(CPUState *cpu, target_ulong rip)
+{
+ return linear_addr(cpu, rip, R_CS);
+}
--
2.51.1

View File

@ -0,0 +1,319 @@
From d0c6c51df07c1e0343e700e9f5a14181b3f8f692 Mon Sep 17 00:00:00 2001
From: Magnus Kulke <magnuskulke@linux.microsoft.com>
Date: Tue, 16 Sep 2025 18:48:36 +0200
Subject: [PATCH 18/31] target/i386/mshv: Implement mshv_arch_put_registers()
RH-Author: Igor Mammedov <imammedo@redhat.com>
RH-MergeRequest: 435: x86: enablement for Azure L1VH OCP readiness
RH-Jira: RHEL-132193
RH-Acked-by: Vitaly Kuznetsov <vkuznets@redhat.com>
RH-Acked-by: Miroslav Rezanina <mrezanin@redhat.com>
RH-Commit: [17/30] d6aa93a9e455d49b8bf2fe562e84dace2a549ebb
Write CPU register state to MSHV vCPUs. Various mapping functions to
prepare the payload for the HV call have been implemented.
Signed-off-by: Magnus Kulke <magnuskulke@linux.microsoft.com>
Link: https://lore.kernel.org/r/20250916164847.77883-17-magnuskulke@linux.microsoft.com
[mshv.h/mshv_int.h split. - Paolo]
Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
(cherry picked from commit 25a1d871e0f19bbf8fee471af5fefec52465bb09)
Signed-off-by: Igor Mammedov <imammedo@redhat.com>
---
include/system/mshv_int.h | 15 +++
target/i386/mshv/mshv-cpu.c | 237 ++++++++++++++++++++++++++++++++++++
2 files changed, 252 insertions(+)
diff --git a/include/system/mshv_int.h b/include/system/mshv_int.h
index c6e6e8af30..0ea8d504fa 100644
--- a/include/system/mshv_int.h
+++ b/include/system/mshv_int.h
@@ -49,6 +49,20 @@ typedef struct MshvMsiControl {
#define mshv_vcpufd(cpu) (cpu->accel->cpufd)
/* cpu */
+typedef struct MshvFPU {
+ uint8_t fpr[8][16];
+ uint16_t fcw;
+ uint16_t fsw;
+ uint8_t ftwx;
+ uint8_t pad1;
+ uint16_t last_opcode;
+ uint64_t last_ip;
+ uint64_t last_dp;
+ uint8_t xmm[16][16];
+ uint32_t mxcsr;
+ uint32_t pad2;
+} MshvFPU;
+
typedef enum MshvVmExit {
MshvVmExitIgnore = 0,
MshvVmExitShutdown = 1,
@@ -58,6 +72,7 @@ typedef enum MshvVmExit {
void mshv_init_mmio_emu(void);
int mshv_create_vcpu(int vm_fd, uint8_t vp_index, int *cpu_fd);
void mshv_remove_vcpu(int vm_fd, int cpu_fd);
+int mshv_configure_vcpu(const CPUState *cpu, const MshvFPU *fpu, uint64_t xcr0);
int mshv_get_standard_regs(CPUState *cpu);
int mshv_get_special_regs(CPUState *cpu);
int mshv_run_vcpu(int vm_fd, CPUState *cpu, hv_message *msg, MshvVmExit *exit);
diff --git a/target/i386/mshv/mshv-cpu.c b/target/i386/mshv/mshv-cpu.c
index bc75686f82..8b10c79e54 100644
--- a/target/i386/mshv/mshv-cpu.c
+++ b/target/i386/mshv/mshv-cpu.c
@@ -73,6 +73,35 @@ static enum hv_register_name SPECIAL_REGISTER_NAMES[17] = {
HV_X64_REGISTER_APIC_BASE,
};
+static enum hv_register_name FPU_REGISTER_NAMES[26] = {
+ HV_X64_REGISTER_XMM0,
+ HV_X64_REGISTER_XMM1,
+ HV_X64_REGISTER_XMM2,
+ HV_X64_REGISTER_XMM3,
+ HV_X64_REGISTER_XMM4,
+ HV_X64_REGISTER_XMM5,
+ HV_X64_REGISTER_XMM6,
+ HV_X64_REGISTER_XMM7,
+ HV_X64_REGISTER_XMM8,
+ HV_X64_REGISTER_XMM9,
+ HV_X64_REGISTER_XMM10,
+ HV_X64_REGISTER_XMM11,
+ HV_X64_REGISTER_XMM12,
+ HV_X64_REGISTER_XMM13,
+ HV_X64_REGISTER_XMM14,
+ HV_X64_REGISTER_XMM15,
+ HV_X64_REGISTER_FP_MMX0,
+ HV_X64_REGISTER_FP_MMX1,
+ HV_X64_REGISTER_FP_MMX2,
+ HV_X64_REGISTER_FP_MMX3,
+ HV_X64_REGISTER_FP_MMX4,
+ HV_X64_REGISTER_FP_MMX5,
+ HV_X64_REGISTER_FP_MMX6,
+ HV_X64_REGISTER_FP_MMX7,
+ HV_X64_REGISTER_FP_CONTROL_STATUS,
+ HV_X64_REGISTER_XMM_CONTROL_STATUS,
+};
+
int mshv_set_generic_regs(const CPUState *cpu, const hv_register_assoc *assocs,
size_t n_regs)
{
@@ -372,8 +401,216 @@ int mshv_load_regs(CPUState *cpu)
return 0;
}
+static inline void populate_hv_segment_reg(SegmentCache *seg,
+ hv_x64_segment_register *hv_reg)
+{
+ uint32_t flags = seg->flags;
+
+ hv_reg->base = seg->base;
+ hv_reg->limit = seg->limit;
+ hv_reg->selector = seg->selector;
+ hv_reg->segment_type = (flags >> DESC_TYPE_SHIFT) & 0xF;
+ hv_reg->non_system_segment = (flags & DESC_S_MASK) != 0;
+ hv_reg->descriptor_privilege_level = (flags >> DESC_DPL_SHIFT) & 0x3;
+ hv_reg->present = (flags & DESC_P_MASK) != 0;
+ hv_reg->reserved = 0;
+ hv_reg->available = (flags & DESC_AVL_MASK) != 0;
+ hv_reg->_long = (flags >> DESC_L_SHIFT) & 0x1;
+ hv_reg->_default = (flags >> DESC_B_SHIFT) & 0x1;
+ hv_reg->granularity = (flags & DESC_G_MASK) != 0;
+}
+
+static inline void populate_hv_table_reg(const struct SegmentCache *seg,
+ hv_x64_table_register *hv_reg)
+{
+ memset(hv_reg, 0, sizeof(*hv_reg));
+
+ hv_reg->base = seg->base;
+ hv_reg->limit = seg->limit;
+}
+
+static int set_special_regs(const CPUState *cpu)
+{
+ X86CPU *x86cpu = X86_CPU(cpu);
+ CPUX86State *env = &x86cpu->env;
+ struct hv_register_assoc assocs[ARRAY_SIZE(SPECIAL_REGISTER_NAMES)];
+ size_t n_regs = ARRAY_SIZE(SPECIAL_REGISTER_NAMES);
+ int ret;
+
+ /* set names */
+ for (size_t i = 0; i < n_regs; i++) {
+ assocs[i].name = SPECIAL_REGISTER_NAMES[i];
+ }
+ populate_hv_segment_reg(&env->segs[R_CS], &assocs[0].value.segment);
+ populate_hv_segment_reg(&env->segs[R_DS], &assocs[1].value.segment);
+ populate_hv_segment_reg(&env->segs[R_ES], &assocs[2].value.segment);
+ populate_hv_segment_reg(&env->segs[R_FS], &assocs[3].value.segment);
+ populate_hv_segment_reg(&env->segs[R_GS], &assocs[4].value.segment);
+ populate_hv_segment_reg(&env->segs[R_SS], &assocs[5].value.segment);
+ populate_hv_segment_reg(&env->tr, &assocs[6].value.segment);
+ populate_hv_segment_reg(&env->ldt, &assocs[7].value.segment);
+
+ populate_hv_table_reg(&env->gdt, &assocs[8].value.table);
+ populate_hv_table_reg(&env->idt, &assocs[9].value.table);
+
+ assocs[10].value.reg64 = env->cr[0];
+ assocs[11].value.reg64 = env->cr[2];
+ assocs[12].value.reg64 = env->cr[3];
+ assocs[13].value.reg64 = env->cr[4];
+ assocs[14].value.reg64 = cpu_get_apic_tpr(x86cpu->apic_state);
+ assocs[15].value.reg64 = env->efer;
+ assocs[16].value.reg64 = cpu_get_apic_base(x86cpu->apic_state);
+
+ ret = mshv_set_generic_regs(cpu, assocs, n_regs);
+ if (ret < 0) {
+ error_report("failed to set special registers");
+ return -1;
+ }
+
+ return 0;
+}
+
+static int set_fpu(const CPUState *cpu, const struct MshvFPU *regs)
+{
+ struct hv_register_assoc assocs[ARRAY_SIZE(FPU_REGISTER_NAMES)];
+ union hv_register_value *value;
+ size_t fp_i;
+ union hv_x64_fp_control_status_register *ctrl_status;
+ union hv_x64_xmm_control_status_register *xmm_ctrl_status;
+ int ret;
+ size_t n_regs = ARRAY_SIZE(FPU_REGISTER_NAMES);
+
+ /* first 16 registers are xmm0-xmm15 */
+ for (size_t i = 0; i < 16; i++) {
+ assocs[i].name = FPU_REGISTER_NAMES[i];
+ value = &assocs[i].value;
+ memcpy(&value->reg128, &regs->xmm[i], 16);
+ }
+
+ /* next 8 registers are fp_mmx0-fp_mmx7 */
+ for (size_t i = 16; i < 24; i++) {
+ assocs[i].name = FPU_REGISTER_NAMES[i];
+ fp_i = (i - 16);
+ value = &assocs[i].value;
+ memcpy(&value->reg128, &regs->fpr[fp_i], 16);
+ }
+
+ /* last two registers are fp_control_status and xmm_control_status */
+ assocs[24].name = FPU_REGISTER_NAMES[24];
+ value = &assocs[24].value;
+ ctrl_status = &value->fp_control_status;
+ ctrl_status->fp_control = regs->fcw;
+ ctrl_status->fp_status = regs->fsw;
+ ctrl_status->fp_tag = regs->ftwx;
+ ctrl_status->reserved = 0;
+ ctrl_status->last_fp_op = regs->last_opcode;
+ ctrl_status->last_fp_rip = regs->last_ip;
+
+ assocs[25].name = FPU_REGISTER_NAMES[25];
+ value = &assocs[25].value;
+ xmm_ctrl_status = &value->xmm_control_status;
+ xmm_ctrl_status->xmm_status_control = regs->mxcsr;
+ xmm_ctrl_status->xmm_status_control_mask = 0;
+ xmm_ctrl_status->last_fp_rdp = regs->last_dp;
+
+ ret = mshv_set_generic_regs(cpu, assocs, n_regs);
+ if (ret < 0) {
+ error_report("failed to set fpu registers");
+ return -1;
+ }
+
+ return 0;
+}
+
+static int set_xc_reg(const CPUState *cpu, uint64_t xcr0)
+{
+ int ret;
+ struct hv_register_assoc assoc = {
+ .name = HV_X64_REGISTER_XFEM,
+ .value.reg64 = xcr0,
+ };
+
+ ret = mshv_set_generic_regs(cpu, &assoc, 1);
+ if (ret < 0) {
+ error_report("failed to set xcr0");
+ return -errno;
+ }
+ return 0;
+}
+
+static int set_cpu_state(const CPUState *cpu, const MshvFPU *fpu_regs,
+ uint64_t xcr0)
+{
+ int ret;
+
+ ret = set_standard_regs(cpu);
+ if (ret < 0) {
+ return ret;
+ }
+ ret = set_special_regs(cpu);
+ if (ret < 0) {
+ return ret;
+ }
+ ret = set_fpu(cpu, fpu_regs);
+ if (ret < 0) {
+ return ret;
+ }
+ ret = set_xc_reg(cpu, xcr0);
+ if (ret < 0) {
+ return ret;
+ }
+ return 0;
+}
+
+/*
+ * TODO: populate topology info:
+ *
+ * X86CPU *x86cpu = X86_CPU(cpu);
+ * CPUX86State *env = &x86cpu->env;
+ * X86CPUTopoInfo *topo_info = &env->topo_info;
+ */
+int mshv_configure_vcpu(const CPUState *cpu, const struct MshvFPU *fpu,
+ uint64_t xcr0)
+{
+ int ret;
+
+ ret = set_cpu_state(cpu, fpu, xcr0);
+ if (ret < 0) {
+ error_report("failed to set cpu state");
+ return -1;
+ }
+
+ return 0;
+}
+
+static int put_regs(const CPUState *cpu)
+{
+ X86CPU *x86cpu = X86_CPU(cpu);
+ CPUX86State *env = &x86cpu->env;
+ MshvFPU fpu = {0};
+ int ret;
+
+ memset(&fpu, 0, sizeof(fpu));
+
+ ret = mshv_configure_vcpu(cpu, &fpu, env->xcr0);
+ if (ret < 0) {
+ error_report("failed to configure vcpu");
+ return ret;
+ }
+
+ return 0;
+}
+
int mshv_arch_put_registers(const CPUState *cpu)
{
+ int ret;
+
+ ret = put_regs(cpu);
+ if (ret < 0) {
+ error_report("Failed to put registers");
+ return -1;
+ }
+
error_report("unimplemented");
abort();
}
--
2.51.1

View File

@ -0,0 +1,173 @@
From 07254ec97137018057b6afed344cee6347761193 Mon Sep 17 00:00:00 2001
From: Magnus Kulke <magnuskulke@linux.microsoft.com>
Date: Tue, 16 Sep 2025 18:48:35 +0200
Subject: [PATCH 17/31] target/i386/mshv: Implement mshv_get_special_regs()
RH-Author: Igor Mammedov <imammedo@redhat.com>
RH-MergeRequest: 435: x86: enablement for Azure L1VH OCP readiness
RH-Jira: RHEL-132193
RH-Acked-by: Vitaly Kuznetsov <vkuznets@redhat.com>
RH-Acked-by: Miroslav Rezanina <mrezanin@redhat.com>
RH-Commit: [16/30] 17fff3d39ea8cdd0934bd78589b828d075d0e84e
Retrieve special registers (e.g. segment, control, and descriptor
table registers) from MSHV vCPUs.
Various helper functions to map register state representations between
Qemu and MSHV are introduced.
Signed-off-by: Magnus Kulke <magnuskulke@linux.microsoft.com>
Link: https://lore.kernel.org/r/20250916164847.77883-16-magnuskulke@linux.microsoft.com
[mshv.h/mshv_int.h split. - Paolo]
Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
(cherry picked from commit 0382c2c8544da66619d16988caaab8e3d9e881d4)
Signed-off-by: Igor Mammedov <imammedo@redhat.com>
---
include/system/mshv_int.h | 1 +
target/i386/mshv/mshv-cpu.c | 104 ++++++++++++++++++++++++++++++++++++
2 files changed, 105 insertions(+)
diff --git a/include/system/mshv_int.h b/include/system/mshv_int.h
index b0a79296ad..c6e6e8af30 100644
--- a/include/system/mshv_int.h
+++ b/include/system/mshv_int.h
@@ -59,6 +59,7 @@ void mshv_init_mmio_emu(void);
int mshv_create_vcpu(int vm_fd, uint8_t vp_index, int *cpu_fd);
void mshv_remove_vcpu(int vm_fd, int cpu_fd);
int mshv_get_standard_regs(CPUState *cpu);
+int mshv_get_special_regs(CPUState *cpu);
int mshv_run_vcpu(int vm_fd, CPUState *cpu, hv_message *msg, MshvVmExit *exit);
int mshv_load_regs(CPUState *cpu);
int mshv_store_regs(CPUState *cpu);
diff --git a/target/i386/mshv/mshv-cpu.c b/target/i386/mshv/mshv-cpu.c
index 4e3eee113b..bc75686f82 100644
--- a/target/i386/mshv/mshv-cpu.c
+++ b/target/i386/mshv/mshv-cpu.c
@@ -53,6 +53,26 @@ static enum hv_register_name STANDARD_REGISTER_NAMES[18] = {
HV_X64_REGISTER_RFLAGS,
};
+static enum hv_register_name SPECIAL_REGISTER_NAMES[17] = {
+ HV_X64_REGISTER_CS,
+ HV_X64_REGISTER_DS,
+ HV_X64_REGISTER_ES,
+ HV_X64_REGISTER_FS,
+ HV_X64_REGISTER_GS,
+ HV_X64_REGISTER_SS,
+ HV_X64_REGISTER_TR,
+ HV_X64_REGISTER_LDTR,
+ HV_X64_REGISTER_GDTR,
+ HV_X64_REGISTER_IDTR,
+ HV_X64_REGISTER_CR0,
+ HV_X64_REGISTER_CR2,
+ HV_X64_REGISTER_CR3,
+ HV_X64_REGISTER_CR4,
+ HV_X64_REGISTER_CR8,
+ HV_X64_REGISTER_EFER,
+ HV_X64_REGISTER_APIC_BASE,
+};
+
int mshv_set_generic_regs(const CPUState *cpu, const hv_register_assoc *assocs,
size_t n_regs)
{
@@ -255,6 +275,84 @@ int mshv_get_standard_regs(CPUState *cpu)
return 0;
}
+static inline void populate_segment_reg(const hv_x64_segment_register *hv_seg,
+ SegmentCache *seg)
+{
+ memset(seg, 0, sizeof(SegmentCache));
+
+ seg->base = hv_seg->base;
+ seg->limit = hv_seg->limit;
+ seg->selector = hv_seg->selector;
+
+ seg->flags = (hv_seg->segment_type << DESC_TYPE_SHIFT)
+ | (hv_seg->present * DESC_P_MASK)
+ | (hv_seg->descriptor_privilege_level << DESC_DPL_SHIFT)
+ | (hv_seg->_default << DESC_B_SHIFT)
+ | (hv_seg->non_system_segment * DESC_S_MASK)
+ | (hv_seg->_long << DESC_L_SHIFT)
+ | (hv_seg->granularity * DESC_G_MASK)
+ | (hv_seg->available * DESC_AVL_MASK);
+
+}
+
+static inline void populate_table_reg(const hv_x64_table_register *hv_seg,
+ SegmentCache *tbl)
+{
+ memset(tbl, 0, sizeof(SegmentCache));
+
+ tbl->base = hv_seg->base;
+ tbl->limit = hv_seg->limit;
+}
+
+static void populate_special_regs(const hv_register_assoc *assocs,
+ X86CPU *x86cpu)
+{
+ CPUX86State *env = &x86cpu->env;
+
+ populate_segment_reg(&assocs[0].value.segment, &env->segs[R_CS]);
+ populate_segment_reg(&assocs[1].value.segment, &env->segs[R_DS]);
+ populate_segment_reg(&assocs[2].value.segment, &env->segs[R_ES]);
+ populate_segment_reg(&assocs[3].value.segment, &env->segs[R_FS]);
+ populate_segment_reg(&assocs[4].value.segment, &env->segs[R_GS]);
+ populate_segment_reg(&assocs[5].value.segment, &env->segs[R_SS]);
+
+ populate_segment_reg(&assocs[6].value.segment, &env->tr);
+ populate_segment_reg(&assocs[7].value.segment, &env->ldt);
+
+ populate_table_reg(&assocs[8].value.table, &env->gdt);
+ populate_table_reg(&assocs[9].value.table, &env->idt);
+
+ env->cr[0] = assocs[10].value.reg64;
+ env->cr[2] = assocs[11].value.reg64;
+ env->cr[3] = assocs[12].value.reg64;
+ env->cr[4] = assocs[13].value.reg64;
+
+ cpu_set_apic_tpr(x86cpu->apic_state, assocs[14].value.reg64);
+ env->efer = assocs[15].value.reg64;
+ cpu_set_apic_base(x86cpu->apic_state, assocs[16].value.reg64);
+}
+
+
+int mshv_get_special_regs(CPUState *cpu)
+{
+ struct hv_register_assoc assocs[ARRAY_SIZE(SPECIAL_REGISTER_NAMES)];
+ int ret;
+ X86CPU *x86cpu = X86_CPU(cpu);
+ size_t n_regs = ARRAY_SIZE(SPECIAL_REGISTER_NAMES);
+
+ for (size_t i = 0; i < n_regs; i++) {
+ assocs[i].name = SPECIAL_REGISTER_NAMES[i];
+ }
+ ret = get_generic_regs(cpu, assocs, n_regs);
+ if (ret < 0) {
+ error_report("failed to get special registers");
+ return -errno;
+ }
+
+ populate_special_regs(assocs, x86cpu);
+ return 0;
+}
+
int mshv_load_regs(CPUState *cpu)
{
int ret;
@@ -265,6 +363,12 @@ int mshv_load_regs(CPUState *cpu)
return -1;
}
+ ret = mshv_get_special_regs(cpu);
+ if (ret < 0) {
+ error_report("Failed to load special registers");
+ return -1;
+ }
+
return 0;
}
--
2.51.1

View File

@ -0,0 +1,182 @@
From abaa75c0fcb9f2caac6a75435b44f215fdfb7f20 Mon Sep 17 00:00:00 2001
From: Magnus Kulke <magnuskulke@linux.microsoft.com>
Date: Tue, 16 Sep 2025 18:48:34 +0200
Subject: [PATCH 16/31] target/i386/mshv: Implement mshv_get_standard_regs()
RH-Author: Igor Mammedov <imammedo@redhat.com>
RH-MergeRequest: 435: x86: enablement for Azure L1VH OCP readiness
RH-Jira: RHEL-132193
RH-Acked-by: Vitaly Kuznetsov <vkuznets@redhat.com>
RH-Acked-by: Miroslav Rezanina <mrezanin@redhat.com>
RH-Commit: [15/30] 514614ac3b954e8ced754ce2e522c4d0c11e22d8
Fetch standard register state from MSHV vCPUs to support debugging,
migration, and other introspection features in QEMU.
Fetch standard register state from a MHSV vCPU's. A generic get_regs()
function and a mapper to map the different register representations are
introduced.
Signed-off-by: Magnus Kulke <magnuskulke@linux.microsoft.com>
Link: https://lore.kernel.org/r/20250916164847.77883-15-magnuskulke@linux.microsoft.com
[mshv.h/mshv_int.h split. - Paolo]
Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
(cherry picked from commit 66480a048a01fc833ba153410282609f26166141)
Signed-off-by: Igor Mammedov <imammedo@redhat.com>
---
include/system/mshv_int.h | 1 +
target/i386/mshv/mshv-cpu.c | 116 +++++++++++++++++++++++++++++++++++-
2 files changed, 115 insertions(+), 2 deletions(-)
diff --git a/include/system/mshv_int.h b/include/system/mshv_int.h
index 731841af92..b0a79296ad 100644
--- a/include/system/mshv_int.h
+++ b/include/system/mshv_int.h
@@ -58,6 +58,7 @@ typedef enum MshvVmExit {
void mshv_init_mmio_emu(void);
int mshv_create_vcpu(int vm_fd, uint8_t vp_index, int *cpu_fd);
void mshv_remove_vcpu(int vm_fd, int cpu_fd);
+int mshv_get_standard_regs(CPUState *cpu);
int mshv_run_vcpu(int vm_fd, CPUState *cpu, hv_message *msg, MshvVmExit *exit);
int mshv_load_regs(CPUState *cpu);
int mshv_store_regs(CPUState *cpu);
diff --git a/target/i386/mshv/mshv-cpu.c b/target/i386/mshv/mshv-cpu.c
index 9ead03ca2d..4e3eee113b 100644
--- a/target/i386/mshv/mshv-cpu.c
+++ b/target/i386/mshv/mshv-cpu.c
@@ -96,6 +96,66 @@ int mshv_set_generic_regs(const CPUState *cpu, const hv_register_assoc *assocs,
return 0;
}
+static int get_generic_regs(CPUState *cpu, hv_register_assoc *assocs,
+ size_t n_regs)
+{
+ int cpu_fd = mshv_vcpufd(cpu);
+ int vp_index = cpu->cpu_index;
+ hv_input_get_vp_registers *in;
+ hv_register_value *values;
+ size_t in_sz, names_sz, values_sz;
+ int i, ret;
+ struct mshv_root_hvcall args = {0};
+
+ /* find out the size of the struct w/ a flexible array at the tail */
+ names_sz = n_regs * sizeof(hv_register_name);
+ in_sz = sizeof(hv_input_get_vp_registers) + names_sz;
+
+ /* fill the input struct */
+ in = g_malloc0(in_sz);
+ in->vp_index = vp_index;
+ for (i = 0; i < n_regs; i++) {
+ in->names[i] = assocs[i].name;
+ }
+
+ /* allocate value output buffer */
+ values_sz = n_regs * sizeof(union hv_register_value);
+ values = g_malloc0(values_sz);
+
+ /* create the hvcall envelope */
+ args.code = HVCALL_GET_VP_REGISTERS;
+ args.in_sz = in_sz;
+ args.in_ptr = (uint64_t) in;
+ args.out_sz = values_sz;
+ args.out_ptr = (uint64_t) values;
+ args.reps = (uint16_t) n_regs;
+
+ /* perform the call */
+ ret = mshv_hvcall(cpu_fd, &args);
+ g_free(in);
+ if (ret < 0) {
+ g_free(values);
+ error_report("Failed to retrieve registers");
+ return -1;
+ }
+
+ /* assert we got all registers */
+ if (args.reps != n_regs) {
+ g_free(values);
+ error_report("Failed to retrieve registers: expected %zu elements"
+ ", got %u", n_regs, args.reps);
+ return -1;
+ }
+
+ /* copy values into assoc */
+ for (i = 0; i < n_regs; i++) {
+ assocs[i].value = values[i];
+ }
+ g_free(values);
+
+ return 0;
+}
+
static int set_standard_regs(const CPUState *cpu)
{
X86CPU *x86cpu = X86_CPU(cpu);
@@ -149,11 +209,63 @@ int mshv_store_regs(CPUState *cpu)
return 0;
}
+static void populate_standard_regs(const hv_register_assoc *assocs,
+ CPUX86State *env)
+{
+ env->regs[R_EAX] = assocs[0].value.reg64;
+ env->regs[R_EBX] = assocs[1].value.reg64;
+ env->regs[R_ECX] = assocs[2].value.reg64;
+ env->regs[R_EDX] = assocs[3].value.reg64;
+ env->regs[R_ESI] = assocs[4].value.reg64;
+ env->regs[R_EDI] = assocs[5].value.reg64;
+ env->regs[R_ESP] = assocs[6].value.reg64;
+ env->regs[R_EBP] = assocs[7].value.reg64;
+ env->regs[R_R8] = assocs[8].value.reg64;
+ env->regs[R_R9] = assocs[9].value.reg64;
+ env->regs[R_R10] = assocs[10].value.reg64;
+ env->regs[R_R11] = assocs[11].value.reg64;
+ env->regs[R_R12] = assocs[12].value.reg64;
+ env->regs[R_R13] = assocs[13].value.reg64;
+ env->regs[R_R14] = assocs[14].value.reg64;
+ env->regs[R_R15] = assocs[15].value.reg64;
+
+ env->eip = assocs[16].value.reg64;
+ env->eflags = assocs[17].value.reg64;
+ rflags_to_lflags(env);
+}
+
+int mshv_get_standard_regs(CPUState *cpu)
+{
+ struct hv_register_assoc assocs[ARRAY_SIZE(STANDARD_REGISTER_NAMES)];
+ int ret;
+ X86CPU *x86cpu = X86_CPU(cpu);
+ CPUX86State *env = &x86cpu->env;
+ size_t n_regs = ARRAY_SIZE(STANDARD_REGISTER_NAMES);
+
+ for (size_t i = 0; i < n_regs; i++) {
+ assocs[i].name = STANDARD_REGISTER_NAMES[i];
+ }
+ ret = get_generic_regs(cpu, assocs, n_regs);
+ if (ret < 0) {
+ error_report("failed to get standard registers");
+ return -1;
+ }
+
+ populate_standard_regs(assocs, env);
+ return 0;
+}
int mshv_load_regs(CPUState *cpu)
{
- error_report("unimplemented");
- abort();
+ int ret;
+
+ ret = mshv_get_standard_regs(cpu);
+ if (ret < 0) {
+ error_report("Failed to load standard registers");
+ return -1;
+ }
+
+ return 0;
}
int mshv_arch_put_registers(const CPUState *cpu)
--
2.51.1

View File

@ -0,0 +1,188 @@
From 0e5bbb8ec6fe3f4826bb936105158066b53a4bd3 Mon Sep 17 00:00:00 2001
From: Magnus Kulke <magnuskulke@linux.microsoft.com>
Date: Thu, 2 Oct 2025 18:13:31 +0200
Subject: [PATCH 15/31] target/i386/mshv: Implement mshv_store_regs()
RH-Author: Igor Mammedov <imammedo@redhat.com>
RH-MergeRequest: 435: x86: enablement for Azure L1VH OCP readiness
RH-Jira: RHEL-132193
RH-Acked-by: Vitaly Kuznetsov <vkuznets@redhat.com>
RH-Acked-by: Miroslav Rezanina <mrezanin@redhat.com>
RH-Commit: [14/30] 5ec94e69cd408b47bd3f52a92a0fc671946a6f1a
Add support for writing general-purpose registers to MSHV vCPUs
during initialization or migration using the MSHV register interface. A
generic set_register call is introduced to abstract the HV call over
the various register types.
Signed-off-by: Magnus Kulke <magnuskulke@linux.microsoft.com>
Link: https://lore.kernel.org/r/20250916164847.77883-14-magnuskulke@linux.microsoft.com
[mshv.h/mshv_int.h split. - Paolo]
Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
(cherry picked from commit 2bd7a6aa4743ee85c77c0520e8b422b3bfeab386)
Signed-off-by: Igor Mammedov <imammedo@redhat.com>
---
include/system/mshv.h | 1 +
include/system/mshv_int.h | 2 +
target/i386/mshv/mshv-cpu.c | 116 +++++++++++++++++++++++++++++++++++-
3 files changed, 117 insertions(+), 2 deletions(-)
diff --git a/include/system/mshv.h b/include/system/mshv.h
index bbc42f4dc3..8b1fc20c80 100644
--- a/include/system/mshv.h
+++ b/include/system/mshv.h
@@ -18,6 +18,7 @@
#include "qemu/accel.h"
#include "hw/hyperv/hyperv-proto.h"
#include "hw/hyperv/hvhdk.h"
+#include "hw/hyperv/hvgdk_mini.h"
#include "qapi/qapi-types-common.h"
#include "system/memory.h"
#include "accel/accel-ops.h"
diff --git a/include/system/mshv_int.h b/include/system/mshv_int.h
index fb80f69772..731841af92 100644
--- a/include/system/mshv_int.h
+++ b/include/system/mshv_int.h
@@ -61,6 +61,8 @@ void mshv_remove_vcpu(int vm_fd, int cpu_fd);
int mshv_run_vcpu(int vm_fd, CPUState *cpu, hv_message *msg, MshvVmExit *exit);
int mshv_load_regs(CPUState *cpu);
int mshv_store_regs(CPUState *cpu);
+int mshv_set_generic_regs(const CPUState *cpu, const hv_register_assoc *assocs,
+ size_t n_regs);
int mshv_arch_put_registers(const CPUState *cpu);
void mshv_arch_init_vcpu(CPUState *cpu);
void mshv_arch_destroy_vcpu(CPUState *cpu);
diff --git a/target/i386/mshv/mshv-cpu.c b/target/i386/mshv/mshv-cpu.c
index 5069ab7a22..9ead03ca2d 100644
--- a/target/i386/mshv/mshv-cpu.c
+++ b/target/i386/mshv/mshv-cpu.c
@@ -32,12 +32,124 @@
#include <sys/ioctl.h>
+static enum hv_register_name STANDARD_REGISTER_NAMES[18] = {
+ HV_X64_REGISTER_RAX,
+ HV_X64_REGISTER_RBX,
+ HV_X64_REGISTER_RCX,
+ HV_X64_REGISTER_RDX,
+ HV_X64_REGISTER_RSI,
+ HV_X64_REGISTER_RDI,
+ HV_X64_REGISTER_RSP,
+ HV_X64_REGISTER_RBP,
+ HV_X64_REGISTER_R8,
+ HV_X64_REGISTER_R9,
+ HV_X64_REGISTER_R10,
+ HV_X64_REGISTER_R11,
+ HV_X64_REGISTER_R12,
+ HV_X64_REGISTER_R13,
+ HV_X64_REGISTER_R14,
+ HV_X64_REGISTER_R15,
+ HV_X64_REGISTER_RIP,
+ HV_X64_REGISTER_RFLAGS,
+};
+
+int mshv_set_generic_regs(const CPUState *cpu, const hv_register_assoc *assocs,
+ size_t n_regs)
+{
+ int cpu_fd = mshv_vcpufd(cpu);
+ int vp_index = cpu->cpu_index;
+ size_t in_sz, assocs_sz;
+ hv_input_set_vp_registers *in;
+ struct mshv_root_hvcall args = {0};
+ int ret;
+
+ /* find out the size of the struct w/ a flexible array at the tail */
+ assocs_sz = n_regs * sizeof(hv_register_assoc);
+ in_sz = sizeof(hv_input_set_vp_registers) + assocs_sz;
+
+ /* fill the input struct */
+ in = g_malloc0(in_sz);
+ in->vp_index = vp_index;
+ memcpy(in->elements, assocs, assocs_sz);
+
+ /* create the hvcall envelope */
+ args.code = HVCALL_SET_VP_REGISTERS;
+ args.in_sz = in_sz;
+ args.in_ptr = (uint64_t) in;
+ args.reps = (uint16_t) n_regs;
+
+ /* perform the call */
+ ret = mshv_hvcall(cpu_fd, &args);
+ g_free(in);
+ if (ret < 0) {
+ error_report("Failed to set registers");
+ return -1;
+ }
+
+ /* assert we set all registers */
+ if (args.reps != n_regs) {
+ error_report("Failed to set registers: expected %zu elements"
+ ", got %u", n_regs, args.reps);
+ return -1;
+ }
+
+ return 0;
+}
+
+static int set_standard_regs(const CPUState *cpu)
+{
+ X86CPU *x86cpu = X86_CPU(cpu);
+ CPUX86State *env = &x86cpu->env;
+ hv_register_assoc assocs[ARRAY_SIZE(STANDARD_REGISTER_NAMES)];
+ int ret;
+ size_t n_regs = ARRAY_SIZE(STANDARD_REGISTER_NAMES);
+
+ /* set names */
+ for (size_t i = 0; i < ARRAY_SIZE(STANDARD_REGISTER_NAMES); i++) {
+ assocs[i].name = STANDARD_REGISTER_NAMES[i];
+ }
+ assocs[0].value.reg64 = env->regs[R_EAX];
+ assocs[1].value.reg64 = env->regs[R_EBX];
+ assocs[2].value.reg64 = env->regs[R_ECX];
+ assocs[3].value.reg64 = env->regs[R_EDX];
+ assocs[4].value.reg64 = env->regs[R_ESI];
+ assocs[5].value.reg64 = env->regs[R_EDI];
+ assocs[6].value.reg64 = env->regs[R_ESP];
+ assocs[7].value.reg64 = env->regs[R_EBP];
+ assocs[8].value.reg64 = env->regs[R_R8];
+ assocs[9].value.reg64 = env->regs[R_R9];
+ assocs[10].value.reg64 = env->regs[R_R10];
+ assocs[11].value.reg64 = env->regs[R_R11];
+ assocs[12].value.reg64 = env->regs[R_R12];
+ assocs[13].value.reg64 = env->regs[R_R13];
+ assocs[14].value.reg64 = env->regs[R_R14];
+ assocs[15].value.reg64 = env->regs[R_R15];
+ assocs[16].value.reg64 = env->eip;
+ lflags_to_rflags(env);
+ assocs[17].value.reg64 = env->eflags;
+
+ ret = mshv_set_generic_regs(cpu, assocs, n_regs);
+ if (ret < 0) {
+ error_report("failed to set standard registers");
+ return -errno;
+ }
+ return 0;
+}
+
int mshv_store_regs(CPUState *cpu)
{
- error_report("unimplemented");
- abort();
+ int ret;
+
+ ret = set_standard_regs(cpu);
+ if (ret < 0) {
+ error_report("Failed to store standard registers");
+ return -1;
+ }
+
+ return 0;
}
+
int mshv_load_regs(CPUState *cpu)
{
error_report("unimplemented");
--
2.51.1

View File

@ -0,0 +1,489 @@
From 6e98581e6fda1f3624c9542940647d3d78e970c2 Mon Sep 17 00:00:00 2001
From: Magnus Kulke <magnuskulke@linux.microsoft.com>
Date: Tue, 16 Sep 2025 18:48:42 +0200
Subject: [PATCH 24/31] target/i386/mshv: Implement mshv_vcpu_run()
RH-Author: Igor Mammedov <imammedo@redhat.com>
RH-MergeRequest: 435: x86: enablement for Azure L1VH OCP readiness
RH-Jira: RHEL-132193
RH-Acked-by: Vitaly Kuznetsov <vkuznets@redhat.com>
RH-Acked-by: Miroslav Rezanina <mrezanin@redhat.com>
RH-Commit: [23/30] 956e2a76fee028cc4d94f061327b9b60d803866e
Add the main vCPU execution loop for MSHV using the MSHV_RUN_VP ioctl.
The execution loop handles guest entry and VM exits. There are handlers for
memory r/w, PIO and MMIO to which the exit events are dispatched.
In case of MMIO the i386 instruction decoder/emulator is invoked to
perform the operation in user space.
Signed-off-by: Magnus Kulke <magnuskulke@linux.microsoft.com>
Link: https://lore.kernel.org/r/20250916164847.77883-23-magnuskulke@linux.microsoft.com
Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
(cherry picked from commit 6dec60528c419781f1f79cd9837d5f26415a3fd7)
Signed-off-by: Igor Mammedov <imammedo@redhat.com>
---
target/i386/mshv/mshv-cpu.c | 444 +++++++++++++++++++++++++++++++++++-
1 file changed, 442 insertions(+), 2 deletions(-)
diff --git a/target/i386/mshv/mshv-cpu.c b/target/i386/mshv/mshv-cpu.c
index 33a3ce8b11..7edc032cea 100644
--- a/target/i386/mshv/mshv-cpu.c
+++ b/target/i386/mshv/mshv-cpu.c
@@ -1082,10 +1082,450 @@ void mshv_arch_amend_proc_features(
features->access_guest_idle_reg = 1;
}
+static int set_memory_info(const struct hyperv_message *msg,
+ struct hv_x64_memory_intercept_message *info)
+{
+ if (msg->header.message_type != HVMSG_GPA_INTERCEPT
+ && msg->header.message_type != HVMSG_UNMAPPED_GPA
+ && msg->header.message_type != HVMSG_UNACCEPTED_GPA) {
+ error_report("invalid message type");
+ return -1;
+ }
+ memcpy(info, msg->payload, sizeof(*info));
+
+ return 0;
+}
+
+static int emulate_instruction(CPUState *cpu,
+ const uint8_t *insn_bytes, size_t insn_len,
+ uint64_t gva, uint64_t gpa)
+{
+ X86CPU *x86_cpu = X86_CPU(cpu);
+ CPUX86State *env = &x86_cpu->env;
+ struct x86_decode decode = { 0 };
+ int ret;
+ x86_insn_stream stream = { .bytes = insn_bytes, .len = insn_len };
+
+ ret = mshv_load_regs(cpu);
+ if (ret < 0) {
+ error_report("failed to load registers");
+ return -1;
+ }
+
+ decode_instruction_stream(env, &decode, &stream);
+ exec_instruction(env, &decode);
+
+ ret = mshv_store_regs(cpu);
+ if (ret < 0) {
+ error_report("failed to store registers");
+ return -1;
+ }
+
+ return 0;
+}
+
+static int handle_mmio(CPUState *cpu, const struct hyperv_message *msg,
+ MshvVmExit *exit_reason)
+{
+ struct hv_x64_memory_intercept_message info = { 0 };
+ size_t insn_len;
+ uint8_t access_type;
+ uint8_t *instruction_bytes;
+ int ret;
+
+ ret = set_memory_info(msg, &info);
+ if (ret < 0) {
+ error_report("failed to convert message to memory info");
+ return -1;
+ }
+ insn_len = info.instruction_byte_count;
+ access_type = info.header.intercept_access_type;
+
+ if (access_type == HV_X64_INTERCEPT_ACCESS_TYPE_EXECUTE) {
+ error_report("invalid intercept access type: execute");
+ return -1;
+ }
+
+ if (insn_len > 16) {
+ error_report("invalid mmio instruction length: %zu", insn_len);
+ return -1;
+ }
+
+ trace_mshv_handle_mmio(info.guest_virtual_address,
+ info.guest_physical_address,
+ info.instruction_byte_count, access_type);
+
+ instruction_bytes = info.instruction_bytes;
+
+ ret = emulate_instruction(cpu, instruction_bytes, insn_len,
+ info.guest_virtual_address,
+ info.guest_physical_address);
+ if (ret < 0) {
+ error_report("failed to emulate mmio");
+ return -1;
+ }
+
+ *exit_reason = MshvVmExitIgnore;
+
+ return 0;
+}
+
+static int set_ioport_info(const struct hyperv_message *msg,
+ hv_x64_io_port_intercept_message *info)
+{
+ if (msg->header.message_type != HVMSG_X64_IO_PORT_INTERCEPT) {
+ error_report("Invalid message type");
+ return -1;
+ }
+ memcpy(info, msg->payload, sizeof(*info));
+
+ return 0;
+}
+
+static int set_x64_registers(const CPUState *cpu, const uint32_t *names,
+ const uint64_t *values)
+{
+
+ hv_register_assoc assocs[2];
+ int ret;
+
+ for (size_t i = 0; i < ARRAY_SIZE(assocs); i++) {
+ assocs[i].name = names[i];
+ assocs[i].value.reg64 = values[i];
+ }
+
+ ret = mshv_set_generic_regs(cpu, assocs, ARRAY_SIZE(assocs));
+ if (ret < 0) {
+ error_report("failed to set x64 registers");
+ return -1;
+ }
+
+ return 0;
+}
+
+static inline MemTxAttrs get_mem_attrs(bool is_secure_mode)
+{
+ MemTxAttrs memattr = {0};
+ memattr.secure = is_secure_mode;
+ return memattr;
+}
+
+static void pio_read(uint64_t port, uint8_t *data, uintptr_t size,
+ bool is_secure_mode)
+{
+ int ret = 0;
+ MemTxAttrs memattr = get_mem_attrs(is_secure_mode);
+ ret = address_space_rw(&address_space_io, port, memattr, (void *)data, size,
+ false);
+ if (ret != MEMTX_OK) {
+ error_report("Failed to read from port %lx: %d", port, ret);
+ abort();
+ }
+}
+
+static int pio_write(uint64_t port, const uint8_t *data, uintptr_t size,
+ bool is_secure_mode)
+{
+ int ret = 0;
+ MemTxAttrs memattr = get_mem_attrs(is_secure_mode);
+ ret = address_space_rw(&address_space_io, port, memattr, (void *)data, size,
+ true);
+ return ret;
+}
+
+static int handle_pio_non_str(const CPUState *cpu,
+ hv_x64_io_port_intercept_message *info)
+{
+ size_t len = info->access_info.access_size;
+ uint8_t access_type = info->header.intercept_access_type;
+ int ret;
+ uint32_t val, eax;
+ const uint32_t eax_mask = 0xffffffffu >> (32 - len * 8);
+ size_t insn_len;
+ uint64_t rip, rax;
+ uint32_t reg_names[2];
+ uint64_t reg_values[2];
+ uint16_t port = info->port_number;
+
+ if (access_type == HV_X64_INTERCEPT_ACCESS_TYPE_WRITE) {
+ union {
+ uint32_t u32;
+ uint8_t bytes[4];
+ } conv;
+
+ /* convert the first 4 bytes of rax to bytes */
+ conv.u32 = (uint32_t)info->rax;
+ /* secure mode is set to false */
+ ret = pio_write(port, conv.bytes, len, false);
+ if (ret < 0) {
+ error_report("Failed to write to io port");
+ return -1;
+ }
+ } else {
+ uint8_t data[4] = { 0 };
+ /* secure mode is set to false */
+ pio_read(info->port_number, data, len, false);
+
+ /* Preserve high bits in EAX, but clear out high bits in RAX */
+ val = *(uint32_t *)data;
+ eax = (((uint32_t)info->rax) & ~eax_mask) | (val & eax_mask);
+ info->rax = (uint64_t)eax;
+ }
+
+ insn_len = info->header.instruction_length;
+
+ /* Advance RIP and update RAX */
+ rip = info->header.rip + insn_len;
+ rax = info->rax;
+
+ reg_names[0] = HV_X64_REGISTER_RIP;
+ reg_values[0] = rip;
+ reg_names[1] = HV_X64_REGISTER_RAX;
+ reg_values[1] = rax;
+
+ ret = set_x64_registers(cpu, reg_names, reg_values);
+ if (ret < 0) {
+ error_report("Failed to set x64 registers");
+ return -1;
+ }
+
+ cpu->accel->dirty = false;
+
+ return 0;
+}
+
+static int fetch_guest_state(CPUState *cpu)
+{
+ int ret;
+
+ ret = mshv_get_standard_regs(cpu);
+ if (ret < 0) {
+ error_report("Failed to get standard registers");
+ return -1;
+ }
+
+ ret = mshv_get_special_regs(cpu);
+ if (ret < 0) {
+ error_report("Failed to get special registers");
+ return -1;
+ }
+
+ return 0;
+}
+
+static int read_memory(const CPUState *cpu, uint64_t initial_gva,
+ uint64_t initial_gpa, uint64_t gva, uint8_t *data,
+ size_t len)
+{
+ int ret;
+ uint64_t gpa, flags;
+
+ if (gva == initial_gva) {
+ gpa = initial_gpa;
+ } else {
+ flags = HV_TRANSLATE_GVA_VALIDATE_READ;
+ ret = translate_gva(cpu, gva, &gpa, flags);
+ if (ret < 0) {
+ return -1;
+ }
+
+ ret = mshv_guest_mem_read(gpa, data, len, false, false);
+ if (ret < 0) {
+ error_report("failed to read guest mem");
+ return -1;
+ }
+ }
+
+ return 0;
+}
+
+static int write_memory(const CPUState *cpu, uint64_t initial_gva,
+ uint64_t initial_gpa, uint64_t gva, const uint8_t *data,
+ size_t len)
+{
+ int ret;
+ uint64_t gpa, flags;
+
+ if (gva == initial_gva) {
+ gpa = initial_gpa;
+ } else {
+ flags = HV_TRANSLATE_GVA_VALIDATE_WRITE;
+ ret = translate_gva(cpu, gva, &gpa, flags);
+ if (ret < 0) {
+ error_report("failed to translate gva to gpa");
+ return -1;
+ }
+ }
+ ret = mshv_guest_mem_write(gpa, data, len, false);
+ if (ret != MEMTX_OK) {
+ error_report("failed to write to mmio");
+ return -1;
+ }
+
+ return 0;
+}
+
+static int handle_pio_str_write(CPUState *cpu,
+ hv_x64_io_port_intercept_message *info,
+ size_t repeat, uint16_t port,
+ bool direction_flag)
+{
+ int ret;
+ uint64_t src;
+ uint8_t data[4] = { 0 };
+ size_t len = info->access_info.access_size;
+
+ src = linear_addr(cpu, info->rsi, R_DS);
+
+ for (size_t i = 0; i < repeat; i++) {
+ ret = read_memory(cpu, 0, 0, src, data, len);
+ if (ret < 0) {
+ error_report("Failed to read memory");
+ return -1;
+ }
+ ret = pio_write(port, data, len, false);
+ if (ret < 0) {
+ error_report("Failed to write to io port");
+ return -1;
+ }
+ src += direction_flag ? -len : len;
+ info->rsi += direction_flag ? -len : len;
+ }
+
+ return 0;
+}
+
+static int handle_pio_str_read(CPUState *cpu,
+ hv_x64_io_port_intercept_message *info,
+ size_t repeat, uint16_t port,
+ bool direction_flag)
+{
+ int ret;
+ uint64_t dst;
+ size_t len = info->access_info.access_size;
+ uint8_t data[4] = { 0 };
+
+ dst = linear_addr(cpu, info->rdi, R_ES);
+
+ for (size_t i = 0; i < repeat; i++) {
+ pio_read(port, data, len, false);
+
+ ret = write_memory(cpu, 0, 0, dst, data, len);
+ if (ret < 0) {
+ error_report("Failed to write memory");
+ return -1;
+ }
+ dst += direction_flag ? -len : len;
+ info->rdi += direction_flag ? -len : len;
+ }
+
+ return 0;
+}
+
+static int handle_pio_str(CPUState *cpu, hv_x64_io_port_intercept_message *info)
+{
+ uint8_t access_type = info->header.intercept_access_type;
+ uint16_t port = info->port_number;
+ bool repop = info->access_info.rep_prefix == 1;
+ size_t repeat = repop ? info->rcx : 1;
+ size_t insn_len = info->header.instruction_length;
+ bool direction_flag;
+ uint32_t reg_names[3];
+ uint64_t reg_values[3];
+ int ret;
+ X86CPU *x86_cpu = X86_CPU(cpu);
+ CPUX86State *env = &x86_cpu->env;
+
+ ret = fetch_guest_state(cpu);
+ if (ret < 0) {
+ error_report("Failed to fetch guest state");
+ return -1;
+ }
+
+ direction_flag = (env->eflags & DESC_E_MASK) != 0;
+
+ if (access_type == HV_X64_INTERCEPT_ACCESS_TYPE_WRITE) {
+ ret = handle_pio_str_write(cpu, info, repeat, port, direction_flag);
+ if (ret < 0) {
+ error_report("Failed to handle pio str write");
+ return -1;
+ }
+ reg_names[0] = HV_X64_REGISTER_RSI;
+ reg_values[0] = info->rsi;
+ } else {
+ ret = handle_pio_str_read(cpu, info, repeat, port, direction_flag);
+ reg_names[0] = HV_X64_REGISTER_RDI;
+ reg_values[0] = info->rdi;
+ }
+
+ reg_names[1] = HV_X64_REGISTER_RIP;
+ reg_values[1] = info->header.rip + insn_len;
+ reg_names[2] = HV_X64_REGISTER_RAX;
+ reg_values[2] = info->rax;
+
+ ret = set_x64_registers(cpu, reg_names, reg_values);
+ if (ret < 0) {
+ error_report("Failed to set x64 registers");
+ return -1;
+ }
+
+ cpu->accel->dirty = false;
+
+ return 0;
+}
+
+static int handle_pio(CPUState *cpu, const struct hyperv_message *msg)
+{
+ struct hv_x64_io_port_intercept_message info = { 0 };
+ int ret;
+
+ ret = set_ioport_info(msg, &info);
+ if (ret < 0) {
+ error_report("Failed to convert message to ioport info");
+ return -1;
+ }
+
+ if (info.access_info.string_op) {
+ return handle_pio_str(cpu, &info);
+ }
+
+ return handle_pio_non_str(cpu, &info);
+}
+
int mshv_run_vcpu(int vm_fd, CPUState *cpu, hv_message *msg, MshvVmExit *exit)
{
- error_report("unimplemented");
- abort();
+ int ret;
+ enum MshvVmExit exit_reason;
+ int cpu_fd = mshv_vcpufd(cpu);
+
+ ret = ioctl(cpu_fd, MSHV_RUN_VP, msg);
+ if (ret < 0) {
+ return MshvVmExitShutdown;
+ }
+
+ switch (msg->header.message_type) {
+ case HVMSG_UNRECOVERABLE_EXCEPTION:
+ return MshvVmExitShutdown;
+ case HVMSG_UNMAPPED_GPA:
+ case HVMSG_GPA_INTERCEPT:
+ ret = handle_mmio(cpu, msg, &exit_reason);
+ if (ret < 0) {
+ error_report("failed to handle mmio");
+ return -1;
+ }
+ return exit_reason;
+ case HVMSG_X64_IO_PORT_INTERCEPT:
+ ret = handle_pio(cpu, msg);
+ if (ret < 0) {
+ return MshvVmExitSpecial;
+ }
+ return MshvVmExitIgnore;
+ default:
+ break;
+ }
+
+ *exit = MshvVmExitIgnore;
+ return 0;
}
void mshv_remove_vcpu(int vm_fd, int cpu_fd)
--
2.51.1

View File

@ -0,0 +1,283 @@
From 3728bde1fc86472ed2a9a95b3393fe86f6f1f0cb Mon Sep 17 00:00:00 2001
From: Magnus Kulke <magnuskulke@linux.microsoft.com>
Date: Tue, 16 Sep 2025 18:48:40 +0200
Subject: [PATCH 22/31] target/i386/mshv: Integrate x86 instruction
decoder/emulator
RH-Author: Igor Mammedov <imammedo@redhat.com>
RH-MergeRequest: 435: x86: enablement for Azure L1VH OCP readiness
RH-Jira: RHEL-132193
RH-Acked-by: Vitaly Kuznetsov <vkuznets@redhat.com>
RH-Acked-by: Miroslav Rezanina <mrezanin@redhat.com>
RH-Commit: [21/30] 72fddb8db33c05a299d78b37fc1974eab2ff1f5f
Connect the x86 instruction decoder and emulator to the MSHV backend
to handle intercepted instructions. This enables software emulation
of MMIO operations in MSHV guests. MSHV has a translate_gva hypercall
that is used to accessing the physical guest memory.
A guest might read from unmapped memory regions (e.g. OVMF will probe
0xfed40000 for a vTPM). In those cases 0xFF bytes is returned instead of
aborting the execution.
Signed-off-by: Magnus Kulke <magnuskulke@linux.microsoft.com>
Link: https://lore.kernel.org/r/20250916164847.77883-21-magnuskulke@linux.microsoft.com
[mshv.h/mshv_int.h split. - Paolo]
Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
(cherry picked from commit 9bc6a1d29605a13c541629a651e41787af65a963)
Signed-off-by: Igor Mammedov <imammedo@redhat.com>
---
accel/mshv/mem.c | 65 +++++++++++++++++
include/system/mshv_int.h | 4 ++
target/i386/mshv/mshv-cpu.c | 135 ++++++++++++++++++++++++++++++++++++
3 files changed, 204 insertions(+)
diff --git a/accel/mshv/mem.c b/accel/mshv/mem.c
index a0a40eb333..e55c38d4db 100644
--- a/accel/mshv/mem.c
+++ b/accel/mshv/mem.c
@@ -59,6 +59,71 @@ static int map_or_unmap(int vm_fd, const MshvMemoryRegion *mr, bool map)
return set_guest_memory(vm_fd, &region);
}
+static int handle_unmapped_mmio_region_read(uint64_t gpa, uint64_t size,
+ uint8_t *data)
+{
+ warn_report("read from unmapped mmio region gpa=0x%lx size=%lu", gpa, size);
+
+ if (size == 0 || size > 8) {
+ error_report("invalid size %lu for reading from unmapped mmio region",
+ size);
+ return -1;
+ }
+
+ memset(data, 0xFF, size);
+
+ return 0;
+}
+
+int mshv_guest_mem_read(uint64_t gpa, uint8_t *data, uintptr_t size,
+ bool is_secure_mode, bool instruction_fetch)
+{
+ int ret;
+ MemTxAttrs memattr = { .secure = is_secure_mode };
+
+ if (instruction_fetch) {
+ trace_mshv_insn_fetch(gpa, size);
+ } else {
+ trace_mshv_mem_read(gpa, size);
+ }
+
+ ret = address_space_rw(&address_space_memory, gpa, memattr, (void *)data,
+ size, false);
+ if (ret == MEMTX_OK) {
+ return 0;
+ }
+
+ if (ret == MEMTX_DECODE_ERROR) {
+ return handle_unmapped_mmio_region_read(gpa, size, data);
+ }
+
+ error_report("failed to read guest memory at 0x%lx", gpa);
+ return -1;
+}
+
+int mshv_guest_mem_write(uint64_t gpa, const uint8_t *data, uintptr_t size,
+ bool is_secure_mode)
+{
+ int ret;
+ MemTxAttrs memattr = { .secure = is_secure_mode };
+
+ trace_mshv_mem_write(gpa, size);
+ ret = address_space_rw(&address_space_memory, gpa, memattr, (void *)data,
+ size, true);
+ if (ret == MEMTX_OK) {
+ return 0;
+ }
+
+ if (ret == MEMTX_DECODE_ERROR) {
+ warn_report("write to unmapped mmio region gpa=0x%lx size=%lu", gpa,
+ size);
+ return 0;
+ }
+
+ error_report("Failed to write guest memory");
+ return -1;
+}
+
static int set_memory(const MshvMemoryRegion *mshv_mr, bool add)
{
int ret = 0;
diff --git a/include/system/mshv_int.h b/include/system/mshv_int.h
index 6649438313..b29d39911d 100644
--- a/include/system/mshv_int.h
+++ b/include/system/mshv_int.h
@@ -101,6 +101,10 @@ typedef struct MshvMemoryRegion {
bool readonly;
} MshvMemoryRegion;
+int mshv_guest_mem_read(uint64_t gpa, uint8_t *data, uintptr_t size,
+ bool is_secure_mode, bool instruction_fetch);
+int mshv_guest_mem_write(uint64_t gpa, const uint8_t *data, uintptr_t size,
+ bool is_secure_mode);
void mshv_set_phys_mem(MshvMemoryListener *mml, MemoryRegionSection *section,
bool add);
diff --git a/target/i386/mshv/mshv-cpu.c b/target/i386/mshv/mshv-cpu.c
index 1f43dfc58a..424ebdb122 100644
--- a/target/i386/mshv/mshv-cpu.c
+++ b/target/i386/mshv/mshv-cpu.c
@@ -104,6 +104,47 @@ static enum hv_register_name FPU_REGISTER_NAMES[26] = {
HV_X64_REGISTER_XMM_CONTROL_STATUS,
};
+static int translate_gva(const CPUState *cpu, uint64_t gva, uint64_t *gpa,
+ uint64_t flags)
+{
+ int ret;
+ int cpu_fd = mshv_vcpufd(cpu);
+ int vp_index = cpu->cpu_index;
+
+ hv_input_translate_virtual_address in = { 0 };
+ hv_output_translate_virtual_address out = { 0 };
+ struct mshv_root_hvcall args = {0};
+ uint64_t gva_page = gva >> HV_HYP_PAGE_SHIFT;
+
+ in.vp_index = vp_index;
+ in.control_flags = flags;
+ in.gva_page = gva_page;
+
+ /* create the hvcall envelope */
+ args.code = HVCALL_TRANSLATE_VIRTUAL_ADDRESS;
+ args.in_sz = sizeof(in);
+ args.in_ptr = (uint64_t) &in;
+ args.out_sz = sizeof(out);
+ args.out_ptr = (uint64_t) &out;
+
+ /* perform the call */
+ ret = mshv_hvcall(cpu_fd, &args);
+ if (ret < 0) {
+ error_report("Failed to invoke gva->gpa translation");
+ return -errno;
+ }
+
+ if (out.translation_result.result_code != HV_TRANSLATE_GVA_SUCCESS) {
+ error_report("Failed to translate gva (" TARGET_FMT_lx ") to gpa", gva);
+ return -1;
+ }
+
+ *gpa = ((out.gpa_page << HV_HYP_PAGE_SHIFT)
+ | (gva & ~(uint64_t)HV_HYP_PAGE_MASK));
+
+ return 0;
+}
+
int mshv_set_generic_regs(const CPUState *cpu, const hv_register_assoc *assocs,
size_t n_regs)
{
@@ -1006,8 +1047,102 @@ int mshv_create_vcpu(int vm_fd, uint8_t vp_index, int *cpu_fd)
return 0;
}
+static int guest_mem_read_with_gva(const CPUState *cpu, uint64_t gva,
+ uint8_t *data, uintptr_t size,
+ bool fetch_instruction)
+{
+ int ret;
+ uint64_t gpa, flags;
+
+ flags = HV_TRANSLATE_GVA_VALIDATE_READ;
+ ret = translate_gva(cpu, gva, &gpa, flags);
+ if (ret < 0) {
+ error_report("failed to translate gva to gpa");
+ return -1;
+ }
+
+ ret = mshv_guest_mem_read(gpa, data, size, false, fetch_instruction);
+ if (ret < 0) {
+ error_report("failed to read from guest memory");
+ return -1;
+ }
+
+ return 0;
+}
+
+static int guest_mem_write_with_gva(const CPUState *cpu, uint64_t gva,
+ const uint8_t *data, uintptr_t size)
+{
+ int ret;
+ uint64_t gpa, flags;
+
+ flags = HV_TRANSLATE_GVA_VALIDATE_WRITE;
+ ret = translate_gva(cpu, gva, &gpa, flags);
+ if (ret < 0) {
+ error_report("failed to translate gva to gpa");
+ return -1;
+ }
+ ret = mshv_guest_mem_write(gpa, data, size, false);
+ if (ret < 0) {
+ error_report("failed to write to guest memory");
+ return -1;
+ }
+ return 0;
+}
+
+static void write_mem(CPUState *cpu, void *data, target_ulong addr, int bytes)
+{
+ if (guest_mem_write_with_gva(cpu, addr, data, bytes) < 0) {
+ error_report("failed to write memory");
+ abort();
+ }
+}
+
+static void fetch_instruction(CPUState *cpu, void *data,
+ target_ulong addr, int bytes)
+{
+ if (guest_mem_read_with_gva(cpu, addr, data, bytes, true) < 0) {
+ error_report("failed to fetch instruction");
+ abort();
+ }
+}
+
+static void read_mem(CPUState *cpu, void *data, target_ulong addr, int bytes)
+{
+ if (guest_mem_read_with_gva(cpu, addr, data, bytes, false) < 0) {
+ error_report("failed to read memory");
+ abort();
+ }
+}
+
+static void read_segment_descriptor(CPUState *cpu,
+ struct x86_segment_descriptor *desc,
+ enum X86Seg seg_idx)
+{
+ bool ret;
+ X86CPU *x86_cpu = X86_CPU(cpu);
+ CPUX86State *env = &x86_cpu->env;
+ SegmentCache *seg = &env->segs[seg_idx];
+ x86_segment_selector sel = { .sel = seg->selector & 0xFFFF };
+
+ ret = x86_read_segment_descriptor(cpu, desc, sel);
+ if (ret == false) {
+ error_report("failed to read segment descriptor");
+ abort();
+ }
+}
+
+static const struct x86_emul_ops mshv_x86_emul_ops = {
+ .fetch_instruction = fetch_instruction,
+ .read_mem = read_mem,
+ .write_mem = write_mem,
+ .read_segment_descriptor = read_segment_descriptor,
+};
+
void mshv_init_mmio_emu(void)
{
+ init_decoder();
+ init_emu(&mshv_x86_emul_ops);
}
void mshv_arch_init_vcpu(CPUState *cpu)
--
2.51.1

View File

@ -0,0 +1,252 @@
From 869cabe9c6bf4cc9f77362eb8bca93ed918b8399 Mon Sep 17 00:00:00 2001
From: Magnus Kulke <magnuskulke@linux.microsoft.com>
Date: Tue, 16 Sep 2025 18:48:38 +0200
Subject: [PATCH 20/31] target/i386/mshv: Register CPUID entries with MSHV
RH-Author: Igor Mammedov <imammedo@redhat.com>
RH-MergeRequest: 435: x86: enablement for Azure L1VH OCP readiness
RH-Jira: RHEL-132193
RH-Acked-by: Vitaly Kuznetsov <vkuznets@redhat.com>
RH-Acked-by: Miroslav Rezanina <mrezanin@redhat.com>
RH-Commit: [19/30] 3f0bb94850c8f124c84a90e0b2afe2760fc38d0a
Convert the guest CPU's CPUID model into MSHV's format and register it
with the hypervisor. This ensures that the guest observes the correct
CPU feature set during CPUID instructions.
Signed-off-by: Magnus Kulke <magnuskulke@linux.microsoft.com>
Link: https://lore.kernel.org/r/20250916164847.77883-19-magnuskulke@linux.microsoft.com
Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
(cherry picked from commit 4fa04dd16216e266564606b7da582e5fce07bced)
Signed-off-by: Igor Mammedov <imammedo@redhat.com>
---
target/i386/mshv/mshv-cpu.c | 206 ++++++++++++++++++++++++++++++++++++
1 file changed, 206 insertions(+)
diff --git a/target/i386/mshv/mshv-cpu.c b/target/i386/mshv/mshv-cpu.c
index 0fe3cbb48d..2b7a81274b 100644
--- a/target/i386/mshv/mshv-cpu.c
+++ b/target/i386/mshv/mshv-cpu.c
@@ -403,6 +403,206 @@ int mshv_load_regs(CPUState *cpu)
return 0;
}
+static void add_cpuid_entry(GList *cpuid_entries,
+ uint32_t function, uint32_t index,
+ uint32_t eax, uint32_t ebx,
+ uint32_t ecx, uint32_t edx)
+{
+ struct hv_cpuid_entry *entry;
+
+ entry = g_malloc0(sizeof(struct hv_cpuid_entry));
+ entry->function = function;
+ entry->index = index;
+ entry->eax = eax;
+ entry->ebx = ebx;
+ entry->ecx = ecx;
+ entry->edx = edx;
+
+ cpuid_entries = g_list_append(cpuid_entries, entry);
+}
+
+static void collect_cpuid_entries(const CPUState *cpu, GList *cpuid_entries)
+{
+ X86CPU *x86_cpu = X86_CPU(cpu);
+ CPUX86State *env = &x86_cpu->env;
+ uint32_t eax, ebx, ecx, edx;
+ uint32_t leaf, subleaf;
+ size_t max_leaf = 0x1F;
+ size_t max_subleaf = 0x20;
+
+ uint32_t leaves_with_subleaves[] = {0x4, 0x7, 0xD, 0xF, 0x10};
+ int n_subleaf_leaves = ARRAY_SIZE(leaves_with_subleaves);
+
+ /* Regular leaves without subleaves */
+ for (leaf = 0; leaf <= max_leaf; leaf++) {
+ bool has_subleaves = false;
+ for (int i = 0; i < n_subleaf_leaves; i++) {
+ if (leaf == leaves_with_subleaves[i]) {
+ has_subleaves = true;
+ break;
+ }
+ }
+
+ if (!has_subleaves) {
+ cpu_x86_cpuid(env, leaf, 0, &eax, &ebx, &ecx, &edx);
+ if (eax == 0 && ebx == 0 && ecx == 0 && edx == 0) {
+ /* all zeroes indicates no more leaves */
+ continue;
+ }
+
+ add_cpuid_entry(cpuid_entries, leaf, 0, eax, ebx, ecx, edx);
+ continue;
+ }
+
+ subleaf = 0;
+ while (subleaf < max_subleaf) {
+ cpu_x86_cpuid(env, leaf, subleaf, &eax, &ebx, &ecx, &edx);
+
+ if (eax == 0 && ebx == 0 && ecx == 0 && edx == 0) {
+ /* all zeroes indicates no more leaves */
+ break;
+ }
+ add_cpuid_entry(cpuid_entries, leaf, 0, eax, ebx, ecx, edx);
+ subleaf++;
+ }
+ }
+}
+
+static int register_intercept_result_cpuid_entry(const CPUState *cpu,
+ uint8_t subleaf_specific,
+ uint8_t always_override,
+ struct hv_cpuid_entry *entry)
+{
+ int ret;
+ int vp_index = cpu->cpu_index;
+ int cpu_fd = mshv_vcpufd(cpu);
+
+ struct hv_register_x64_cpuid_result_parameters cpuid_params = {
+ .input.eax = entry->function,
+ .input.ecx = entry->index,
+ .input.subleaf_specific = subleaf_specific,
+ .input.always_override = always_override,
+ .input.padding = 0,
+ /*
+ * With regard to masks - these are to specify bits to be overwritten
+ * The current CpuidEntry structure wouldn't allow to carry the masks
+ * in addition to the actual register values. For this reason, the
+ * masks are set to the exact values of the corresponding register bits
+ * to be registered for an overwrite. To view resulting values the
+ * hypervisor would return, HvCallGetVpCpuidValues hypercall can be
+ * used.
+ */
+ .result.eax = entry->eax,
+ .result.eax_mask = entry->eax,
+ .result.ebx = entry->ebx,
+ .result.ebx_mask = entry->ebx,
+ .result.ecx = entry->ecx,
+ .result.ecx_mask = entry->ecx,
+ .result.edx = entry->edx,
+ .result.edx_mask = entry->edx,
+ };
+ union hv_register_intercept_result_parameters parameters = {
+ .cpuid = cpuid_params,
+ };
+
+ hv_input_register_intercept_result in = {0};
+ in.vp_index = vp_index;
+ in.intercept_type = HV_INTERCEPT_TYPE_X64_CPUID;
+ in.parameters = parameters;
+
+ struct mshv_root_hvcall args = {0};
+ args.code = HVCALL_REGISTER_INTERCEPT_RESULT;
+ args.in_sz = sizeof(in);
+ args.in_ptr = (uint64_t)&in;
+
+ ret = mshv_hvcall(cpu_fd, &args);
+ if (ret < 0) {
+ error_report("failed to register intercept result for cpuid");
+ return -1;
+ }
+
+ return 0;
+}
+
+static int register_intercept_result_cpuid(const CPUState *cpu,
+ struct hv_cpuid *cpuid)
+{
+ int ret = 0, entry_ret;
+ struct hv_cpuid_entry *entry;
+ uint8_t subleaf_specific, always_override;
+
+ for (size_t i = 0; i < cpuid->nent; i++) {
+ entry = &cpuid->entries[i];
+
+ /* set defaults */
+ subleaf_specific = 0;
+ always_override = 1;
+
+ /* Intel */
+ /* 0xb - Extended Topology Enumeration Leaf */
+ /* 0x1f - V2 Extended Topology Enumeration Leaf */
+ /* AMD */
+ /* 0x8000_001e - Processor Topology Information */
+ /* 0x8000_0026 - Extended CPU Topology */
+ if (entry->function == 0xb
+ || entry->function == 0x1f
+ || entry->function == 0x8000001e
+ || entry->function == 0x80000026) {
+ subleaf_specific = 1;
+ always_override = 1;
+ } else if (entry->function == 0x00000001
+ || entry->function == 0x80000000
+ || entry->function == 0x80000001
+ || entry->function == 0x80000008) {
+ subleaf_specific = 0;
+ always_override = 1;
+ }
+
+ entry_ret = register_intercept_result_cpuid_entry(cpu, subleaf_specific,
+ always_override,
+ entry);
+ if ((entry_ret < 0) && (ret == 0)) {
+ ret = entry_ret;
+ }
+ }
+
+ return ret;
+}
+
+static int set_cpuid2(const CPUState *cpu)
+{
+ int ret;
+ size_t n_entries, cpuid_size;
+ struct hv_cpuid *cpuid;
+ struct hv_cpuid_entry *entry;
+ GList *entries = NULL;
+
+ collect_cpuid_entries(cpu, entries);
+ n_entries = g_list_length(entries);
+
+ cpuid_size = sizeof(struct hv_cpuid)
+ + n_entries * sizeof(struct hv_cpuid_entry);
+
+ cpuid = g_malloc0(cpuid_size);
+ cpuid->nent = n_entries;
+ cpuid->padding = 0;
+
+ for (size_t i = 0; i < n_entries; i++) {
+ entry = g_list_nth_data(entries, i);
+ cpuid->entries[i] = *entry;
+ g_free(entry);
+ }
+ g_list_free(entries);
+
+ ret = register_intercept_result_cpuid(cpu, cpuid);
+ g_free(cpuid);
+ if (ret < 0) {
+ return ret;
+ }
+
+ return 0;
+}
+
static inline void populate_hv_segment_reg(SegmentCache *seg,
hv_x64_segment_register *hv_reg)
{
@@ -685,6 +885,12 @@ int mshv_configure_vcpu(const CPUState *cpu, const struct MshvFPU *fpu,
int ret;
int cpu_fd = mshv_vcpufd(cpu);
+ ret = set_cpuid2(cpu);
+ if (ret < 0) {
+ error_report("failed to set cpuid");
+ return -1;
+ }
+
ret = set_cpu_state(cpu, fpu, xcr0);
if (ret < 0) {
error_report("failed to set cpu state");
--
2.51.1

View File

@ -0,0 +1,528 @@
From bb8c88f7b92e99b58db1e1f4a7c59d9cd24e3d90 Mon Sep 17 00:00:00 2001
From: Magnus Kulke <magnuskulke@linux.microsoft.com>
Date: Thu, 2 Oct 2025 18:19:22 +0200
Subject: [PATCH 21/31] target/i386/mshv: Register MSRs with MSHV
RH-Author: Igor Mammedov <imammedo@redhat.com>
RH-MergeRequest: 435: x86: enablement for Azure L1VH OCP readiness
RH-Jira: RHEL-132193
RH-Acked-by: Vitaly Kuznetsov <vkuznets@redhat.com>
RH-Acked-by: Miroslav Rezanina <mrezanin@redhat.com>
RH-Commit: [20/30] 3c6a297eff5a862bae60febd181a8f52252b37cb
Build and register the guest vCPU's model-specific registers using
the MSHV interface.
Signed-off-by: Magnus Kulke <magnuskulke@linux.microsoft.com>
Link: https://lore.kernel.org/r/20250916164847.77883-20-magnuskulke@linux.microsoft.com
[mshv.h/mshv_int.h split. - Paolo]
Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
(cherry picked from commit f38e2a63e541730e114f6ed09e5f8719e388c8db)
Signed-off-by: Igor Mammedov <imammedo@redhat.com>
---
accel/mshv/meson.build | 1 +
accel/mshv/msr.c | 375 ++++++++++++++++++++++++++++++++++++
include/system/mshv_int.h | 17 ++
target/i386/cpu.h | 2 +
target/i386/mshv/mshv-cpu.c | 33 ++++
5 files changed, 428 insertions(+)
create mode 100644 accel/mshv/msr.c
diff --git a/accel/mshv/meson.build b/accel/mshv/meson.build
index f88fc8678c..d3a2b32581 100644
--- a/accel/mshv/meson.build
+++ b/accel/mshv/meson.build
@@ -2,6 +2,7 @@ mshv_ss = ss.source_set()
mshv_ss.add(if_true: files(
'irq.c',
'mem.c',
+ 'msr.c',
'mshv-all.c'
))
diff --git a/accel/mshv/msr.c b/accel/mshv/msr.c
new file mode 100644
index 0000000000..e6e5baef50
--- /dev/null
+++ b/accel/mshv/msr.c
@@ -0,0 +1,375 @@
+/*
+ * QEMU MSHV support
+ *
+ * Copyright Microsoft, Corp. 2025
+ *
+ * Authors: Magnus Kulke <magnuskulke@microsoft.com>
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+
+#include "qemu/osdep.h"
+#include "system/mshv.h"
+#include "system/mshv_int.h"
+#include "hw/hyperv/hvgdk_mini.h"
+#include "linux/mshv.h"
+#include "qemu/error-report.h"
+
+static uint32_t supported_msrs[64] = {
+ IA32_MSR_TSC,
+ IA32_MSR_EFER,
+ IA32_MSR_KERNEL_GS_BASE,
+ IA32_MSR_APIC_BASE,
+ IA32_MSR_PAT,
+ IA32_MSR_SYSENTER_CS,
+ IA32_MSR_SYSENTER_ESP,
+ IA32_MSR_SYSENTER_EIP,
+ IA32_MSR_STAR,
+ IA32_MSR_LSTAR,
+ IA32_MSR_CSTAR,
+ IA32_MSR_SFMASK,
+ IA32_MSR_MTRR_DEF_TYPE,
+ IA32_MSR_MTRR_PHYSBASE0,
+ IA32_MSR_MTRR_PHYSMASK0,
+ IA32_MSR_MTRR_PHYSBASE1,
+ IA32_MSR_MTRR_PHYSMASK1,
+ IA32_MSR_MTRR_PHYSBASE2,
+ IA32_MSR_MTRR_PHYSMASK2,
+ IA32_MSR_MTRR_PHYSBASE3,
+ IA32_MSR_MTRR_PHYSMASK3,
+ IA32_MSR_MTRR_PHYSBASE4,
+ IA32_MSR_MTRR_PHYSMASK4,
+ IA32_MSR_MTRR_PHYSBASE5,
+ IA32_MSR_MTRR_PHYSMASK5,
+ IA32_MSR_MTRR_PHYSBASE6,
+ IA32_MSR_MTRR_PHYSMASK6,
+ IA32_MSR_MTRR_PHYSBASE7,
+ IA32_MSR_MTRR_PHYSMASK7,
+ IA32_MSR_MTRR_FIX64K_00000,
+ IA32_MSR_MTRR_FIX16K_80000,
+ IA32_MSR_MTRR_FIX16K_A0000,
+ IA32_MSR_MTRR_FIX4K_C0000,
+ IA32_MSR_MTRR_FIX4K_C8000,
+ IA32_MSR_MTRR_FIX4K_D0000,
+ IA32_MSR_MTRR_FIX4K_D8000,
+ IA32_MSR_MTRR_FIX4K_E0000,
+ IA32_MSR_MTRR_FIX4K_E8000,
+ IA32_MSR_MTRR_FIX4K_F0000,
+ IA32_MSR_MTRR_FIX4K_F8000,
+ IA32_MSR_TSC_AUX,
+ IA32_MSR_DEBUG_CTL,
+ HV_X64_MSR_GUEST_OS_ID,
+ HV_X64_MSR_SINT0,
+ HV_X64_MSR_SINT1,
+ HV_X64_MSR_SINT2,
+ HV_X64_MSR_SINT3,
+ HV_X64_MSR_SINT4,
+ HV_X64_MSR_SINT5,
+ HV_X64_MSR_SINT6,
+ HV_X64_MSR_SINT7,
+ HV_X64_MSR_SINT8,
+ HV_X64_MSR_SINT9,
+ HV_X64_MSR_SINT10,
+ HV_X64_MSR_SINT11,
+ HV_X64_MSR_SINT12,
+ HV_X64_MSR_SINT13,
+ HV_X64_MSR_SINT14,
+ HV_X64_MSR_SINT15,
+ HV_X64_MSR_SCONTROL,
+ HV_X64_MSR_SIEFP,
+ HV_X64_MSR_SIMP,
+ HV_X64_MSR_REFERENCE_TSC,
+ HV_X64_MSR_EOM,
+};
+static const size_t msr_count = ARRAY_SIZE(supported_msrs);
+
+static int compare_msr_index(const void *a, const void *b)
+{
+ return *(uint32_t *)a - *(uint32_t *)b;
+}
+
+__attribute__((constructor))
+static void init_sorted_msr_map(void)
+{
+ qsort(supported_msrs, msr_count, sizeof(uint32_t), compare_msr_index);
+}
+
+static int mshv_is_supported_msr(uint32_t msr)
+{
+ return bsearch(&msr, supported_msrs, msr_count, sizeof(uint32_t),
+ compare_msr_index) != NULL;
+}
+
+static int mshv_msr_to_hv_reg_name(uint32_t msr, uint32_t *hv_reg)
+{
+ switch (msr) {
+ case IA32_MSR_TSC:
+ *hv_reg = HV_X64_REGISTER_TSC;
+ return 0;
+ case IA32_MSR_EFER:
+ *hv_reg = HV_X64_REGISTER_EFER;
+ return 0;
+ case IA32_MSR_KERNEL_GS_BASE:
+ *hv_reg = HV_X64_REGISTER_KERNEL_GS_BASE;
+ return 0;
+ case IA32_MSR_APIC_BASE:
+ *hv_reg = HV_X64_REGISTER_APIC_BASE;
+ return 0;
+ case IA32_MSR_PAT:
+ *hv_reg = HV_X64_REGISTER_PAT;
+ return 0;
+ case IA32_MSR_SYSENTER_CS:
+ *hv_reg = HV_X64_REGISTER_SYSENTER_CS;
+ return 0;
+ case IA32_MSR_SYSENTER_ESP:
+ *hv_reg = HV_X64_REGISTER_SYSENTER_ESP;
+ return 0;
+ case IA32_MSR_SYSENTER_EIP:
+ *hv_reg = HV_X64_REGISTER_SYSENTER_EIP;
+ return 0;
+ case IA32_MSR_STAR:
+ *hv_reg = HV_X64_REGISTER_STAR;
+ return 0;
+ case IA32_MSR_LSTAR:
+ *hv_reg = HV_X64_REGISTER_LSTAR;
+ return 0;
+ case IA32_MSR_CSTAR:
+ *hv_reg = HV_X64_REGISTER_CSTAR;
+ return 0;
+ case IA32_MSR_SFMASK:
+ *hv_reg = HV_X64_REGISTER_SFMASK;
+ return 0;
+ case IA32_MSR_MTRR_CAP:
+ *hv_reg = HV_X64_REGISTER_MSR_MTRR_CAP;
+ return 0;
+ case IA32_MSR_MTRR_DEF_TYPE:
+ *hv_reg = HV_X64_REGISTER_MSR_MTRR_DEF_TYPE;
+ return 0;
+ case IA32_MSR_MTRR_PHYSBASE0:
+ *hv_reg = HV_X64_REGISTER_MSR_MTRR_PHYS_BASE0;
+ return 0;
+ case IA32_MSR_MTRR_PHYSMASK0:
+ *hv_reg = HV_X64_REGISTER_MSR_MTRR_PHYS_MASK0;
+ return 0;
+ case IA32_MSR_MTRR_PHYSBASE1:
+ *hv_reg = HV_X64_REGISTER_MSR_MTRR_PHYS_BASE1;
+ return 0;
+ case IA32_MSR_MTRR_PHYSMASK1:
+ *hv_reg = HV_X64_REGISTER_MSR_MTRR_PHYS_MASK1;
+ return 0;
+ case IA32_MSR_MTRR_PHYSBASE2:
+ *hv_reg = HV_X64_REGISTER_MSR_MTRR_PHYS_BASE2;
+ return 0;
+ case IA32_MSR_MTRR_PHYSMASK2:
+ *hv_reg = HV_X64_REGISTER_MSR_MTRR_PHYS_MASK2;
+ return 0;
+ case IA32_MSR_MTRR_PHYSBASE3:
+ *hv_reg = HV_X64_REGISTER_MSR_MTRR_PHYS_BASE3;
+ return 0;
+ case IA32_MSR_MTRR_PHYSMASK3:
+ *hv_reg = HV_X64_REGISTER_MSR_MTRR_PHYS_MASK3;
+ return 0;
+ case IA32_MSR_MTRR_PHYSBASE4:
+ *hv_reg = HV_X64_REGISTER_MSR_MTRR_PHYS_BASE4;
+ return 0;
+ case IA32_MSR_MTRR_PHYSMASK4:
+ *hv_reg = HV_X64_REGISTER_MSR_MTRR_PHYS_MASK4;
+ return 0;
+ case IA32_MSR_MTRR_PHYSBASE5:
+ *hv_reg = HV_X64_REGISTER_MSR_MTRR_PHYS_BASE5;
+ return 0;
+ case IA32_MSR_MTRR_PHYSMASK5:
+ *hv_reg = HV_X64_REGISTER_MSR_MTRR_PHYS_MASK5;
+ return 0;
+ case IA32_MSR_MTRR_PHYSBASE6:
+ *hv_reg = HV_X64_REGISTER_MSR_MTRR_PHYS_BASE6;
+ return 0;
+ case IA32_MSR_MTRR_PHYSMASK6:
+ *hv_reg = HV_X64_REGISTER_MSR_MTRR_PHYS_MASK6;
+ return 0;
+ case IA32_MSR_MTRR_PHYSBASE7:
+ *hv_reg = HV_X64_REGISTER_MSR_MTRR_PHYS_BASE7;
+ return 0;
+ case IA32_MSR_MTRR_PHYSMASK7:
+ *hv_reg = HV_X64_REGISTER_MSR_MTRR_PHYS_MASK7;
+ return 0;
+ case IA32_MSR_MTRR_FIX64K_00000:
+ *hv_reg = HV_X64_REGISTER_MSR_MTRR_FIX64K00000;
+ return 0;
+ case IA32_MSR_MTRR_FIX16K_80000:
+ *hv_reg = HV_X64_REGISTER_MSR_MTRR_FIX16K80000;
+ return 0;
+ case IA32_MSR_MTRR_FIX16K_A0000:
+ *hv_reg = HV_X64_REGISTER_MSR_MTRR_FIX16KA0000;
+ return 0;
+ case IA32_MSR_MTRR_FIX4K_C0000:
+ *hv_reg = HV_X64_REGISTER_MSR_MTRR_FIX4KC0000;
+ return 0;
+ case IA32_MSR_MTRR_FIX4K_C8000:
+ *hv_reg = HV_X64_REGISTER_MSR_MTRR_FIX4KC8000;
+ return 0;
+ case IA32_MSR_MTRR_FIX4K_D0000:
+ *hv_reg = HV_X64_REGISTER_MSR_MTRR_FIX4KD0000;
+ return 0;
+ case IA32_MSR_MTRR_FIX4K_D8000:
+ *hv_reg = HV_X64_REGISTER_MSR_MTRR_FIX4KD8000;
+ return 0;
+ case IA32_MSR_MTRR_FIX4K_E0000:
+ *hv_reg = HV_X64_REGISTER_MSR_MTRR_FIX4KE0000;
+ return 0;
+ case IA32_MSR_MTRR_FIX4K_E8000:
+ *hv_reg = HV_X64_REGISTER_MSR_MTRR_FIX4KE8000;
+ return 0;
+ case IA32_MSR_MTRR_FIX4K_F0000:
+ *hv_reg = HV_X64_REGISTER_MSR_MTRR_FIX4KF0000;
+ return 0;
+ case IA32_MSR_MTRR_FIX4K_F8000:
+ *hv_reg = HV_X64_REGISTER_MSR_MTRR_FIX4KF8000;
+ return 0;
+ case IA32_MSR_TSC_AUX:
+ *hv_reg = HV_X64_REGISTER_TSC_AUX;
+ return 0;
+ case IA32_MSR_BNDCFGS:
+ *hv_reg = HV_X64_REGISTER_BNDCFGS;
+ return 0;
+ case IA32_MSR_DEBUG_CTL:
+ *hv_reg = HV_X64_REGISTER_DEBUG_CTL;
+ return 0;
+ case IA32_MSR_TSC_ADJUST:
+ *hv_reg = HV_X64_REGISTER_TSC_ADJUST;
+ return 0;
+ case IA32_MSR_SPEC_CTRL:
+ *hv_reg = HV_X64_REGISTER_SPEC_CTRL;
+ return 0;
+ case HV_X64_MSR_GUEST_OS_ID:
+ *hv_reg = HV_REGISTER_GUEST_OS_ID;
+ return 0;
+ case HV_X64_MSR_SINT0:
+ *hv_reg = HV_REGISTER_SINT0;
+ return 0;
+ case HV_X64_MSR_SINT1:
+ *hv_reg = HV_REGISTER_SINT1;
+ return 0;
+ case HV_X64_MSR_SINT2:
+ *hv_reg = HV_REGISTER_SINT2;
+ return 0;
+ case HV_X64_MSR_SINT3:
+ *hv_reg = HV_REGISTER_SINT3;
+ return 0;
+ case HV_X64_MSR_SINT4:
+ *hv_reg = HV_REGISTER_SINT4;
+ return 0;
+ case HV_X64_MSR_SINT5:
+ *hv_reg = HV_REGISTER_SINT5;
+ return 0;
+ case HV_X64_MSR_SINT6:
+ *hv_reg = HV_REGISTER_SINT6;
+ return 0;
+ case HV_X64_MSR_SINT7:
+ *hv_reg = HV_REGISTER_SINT7;
+ return 0;
+ case HV_X64_MSR_SINT8:
+ *hv_reg = HV_REGISTER_SINT8;
+ return 0;
+ case HV_X64_MSR_SINT9:
+ *hv_reg = HV_REGISTER_SINT9;
+ return 0;
+ case HV_X64_MSR_SINT10:
+ *hv_reg = HV_REGISTER_SINT10;
+ return 0;
+ case HV_X64_MSR_SINT11:
+ *hv_reg = HV_REGISTER_SINT11;
+ return 0;
+ case HV_X64_MSR_SINT12:
+ *hv_reg = HV_REGISTER_SINT12;
+ return 0;
+ case HV_X64_MSR_SINT13:
+ *hv_reg = HV_REGISTER_SINT13;
+ return 0;
+ case HV_X64_MSR_SINT14:
+ *hv_reg = HV_REGISTER_SINT14;
+ return 0;
+ case HV_X64_MSR_SINT15:
+ *hv_reg = HV_REGISTER_SINT15;
+ return 0;
+ case IA32_MSR_MISC_ENABLE:
+ *hv_reg = HV_X64_REGISTER_MSR_IA32_MISC_ENABLE;
+ return 0;
+ case HV_X64_MSR_SCONTROL:
+ *hv_reg = HV_REGISTER_SCONTROL;
+ return 0;
+ case HV_X64_MSR_SIEFP:
+ *hv_reg = HV_REGISTER_SIEFP;
+ return 0;
+ case HV_X64_MSR_SIMP:
+ *hv_reg = HV_REGISTER_SIMP;
+ return 0;
+ case HV_X64_MSR_REFERENCE_TSC:
+ *hv_reg = HV_REGISTER_REFERENCE_TSC;
+ return 0;
+ case HV_X64_MSR_EOM:
+ *hv_reg = HV_REGISTER_EOM;
+ return 0;
+ default:
+ error_report("failed to map MSR %u to HV register name", msr);
+ return -1;
+ }
+}
+
+static int set_msrs(const CPUState *cpu, GList *msrs)
+{
+ size_t n_msrs;
+ GList *entries;
+ MshvMsrEntry *entry;
+ enum hv_register_name name;
+ struct hv_register_assoc *assoc;
+ int ret;
+ size_t i = 0;
+
+ n_msrs = g_list_length(msrs);
+ hv_register_assoc *assocs = g_new0(hv_register_assoc, n_msrs);
+
+ entries = msrs;
+ for (const GList *elem = entries; elem != NULL; elem = elem->next) {
+ entry = elem->data;
+ ret = mshv_msr_to_hv_reg_name(entry->index, &name);
+ if (ret < 0) {
+ g_free(assocs);
+ return ret;
+ }
+ assoc = &assocs[i];
+ assoc->name = name;
+ /* the union has been initialized to 0 */
+ assoc->value.reg64 = entry->data;
+ i++;
+ }
+ ret = mshv_set_generic_regs(cpu, assocs, n_msrs);
+ g_free(assocs);
+ if (ret < 0) {
+ error_report("failed to set msrs");
+ return -1;
+ }
+ return 0;
+}
+
+
+int mshv_configure_msr(const CPUState *cpu, const MshvMsrEntry *msrs,
+ size_t n_msrs)
+{
+ GList *valid_msrs = NULL;
+ uint32_t msr_index;
+ int ret;
+
+ for (size_t i = 0; i < n_msrs; i++) {
+ msr_index = msrs[i].index;
+ /* check whether index of msrs is in SUPPORTED_MSRS */
+ if (mshv_is_supported_msr(msr_index)) {
+ valid_msrs = g_list_append(valid_msrs, (void *) &msrs[i]);
+ }
+ }
+
+ ret = set_msrs(cpu, valid_msrs);
+ g_list_free(valid_msrs);
+
+ return ret;
+}
diff --git a/include/system/mshv_int.h b/include/system/mshv_int.h
index 0ea8d504fa..6649438313 100644
--- a/include/system/mshv_int.h
+++ b/include/system/mshv_int.h
@@ -14,6 +14,8 @@
#ifndef QEMU_MSHV_INT_H
#define QEMU_MSHV_INT_H
+#define MSHV_MSR_ENTRIES_COUNT 64
+
typedef struct hyperv_message hv_message;
struct AccelCPUState {
@@ -102,6 +104,21 @@ typedef struct MshvMemoryRegion {
void mshv_set_phys_mem(MshvMemoryListener *mml, MemoryRegionSection *section,
bool add);
+/* msr */
+typedef struct MshvMsrEntry {
+ uint32_t index;
+ uint32_t reserved;
+ uint64_t data;
+} MshvMsrEntry;
+
+typedef struct MshvMsrEntries {
+ MshvMsrEntry entries[MSHV_MSR_ENTRIES_COUNT];
+ uint32_t nmsrs;
+} MshvMsrEntries;
+
+int mshv_configure_msr(const CPUState *cpu, const MshvMsrEntry *msrs,
+ size_t n_msrs);
+
/* interrupt */
void mshv_init_msicontrol(void);
int mshv_reserve_ioapic_msi_routes(int vm_fd);
diff --git a/target/i386/cpu.h b/target/i386/cpu.h
index 4b7eae43e1..8f780a3abe 100644
--- a/target/i386/cpu.h
+++ b/target/i386/cpu.h
@@ -435,9 +435,11 @@ typedef enum X86Seg {
#define MSR_SMI_COUNT 0x34
#define MSR_CORE_THREAD_COUNT 0x35
#define MSR_MTRRcap 0xfe
+#define MSR_MTRR_MEM_TYPE_WB 0x06
#define MSR_MTRRcap_VCNT 8
#define MSR_MTRRcap_FIXRANGE_SUPPORT (1 << 8)
#define MSR_MTRRcap_WC_SUPPORTED (1 << 10)
+#define MSR_MTRR_ENABLE (1 << 11)
#define MSR_IA32_SYSENTER_CS 0x174
#define MSR_IA32_SYSENTER_ESP 0x175
diff --git a/target/i386/mshv/mshv-cpu.c b/target/i386/mshv/mshv-cpu.c
index 2b7a81274b..1f43dfc58a 100644
--- a/target/i386/mshv/mshv-cpu.c
+++ b/target/i386/mshv/mshv-cpu.c
@@ -872,6 +872,33 @@ static int set_lint(int cpu_fd)
return set_lapic(cpu_fd, &lapic_state);
}
+static int setup_msrs(const CPUState *cpu)
+{
+ int ret;
+ uint64_t default_type = MSR_MTRR_ENABLE | MSR_MTRR_MEM_TYPE_WB;
+
+ /* boot msr entries */
+ MshvMsrEntry msrs[9] = {
+ { .index = IA32_MSR_SYSENTER_CS, .data = 0x0, },
+ { .index = IA32_MSR_SYSENTER_ESP, .data = 0x0, },
+ { .index = IA32_MSR_SYSENTER_EIP, .data = 0x0, },
+ { .index = IA32_MSR_STAR, .data = 0x0, },
+ { .index = IA32_MSR_CSTAR, .data = 0x0, },
+ { .index = IA32_MSR_LSTAR, .data = 0x0, },
+ { .index = IA32_MSR_KERNEL_GS_BASE, .data = 0x0, },
+ { .index = IA32_MSR_SFMASK, .data = 0x0, },
+ { .index = IA32_MSR_MTRR_DEF_TYPE, .data = default_type, },
+ };
+
+ ret = mshv_configure_msr(cpu, msrs, 9);
+ if (ret < 0) {
+ error_report("failed to setup msrs");
+ return -1;
+ }
+
+ return 0;
+}
+
/*
* TODO: populate topology info:
*
@@ -891,6 +918,12 @@ int mshv_configure_vcpu(const CPUState *cpu, const struct MshvFPU *fpu,
return -1;
}
+ ret = setup_msrs(cpu);
+ if (ret < 0) {
+ error_report("failed to setup msrs");
+ return -1;
+ }
+
ret = set_cpu_state(cpu, fpu, xcr0);
if (ret < 0) {
error_report("failed to set cpu state");
--
2.51.1

View File

@ -0,0 +1,183 @@
From ea7024c1a3846c5f6a33b60b47759c1dff355564 Mon Sep 17 00:00:00 2001
From: Magnus Kulke <magnuskulke@linux.microsoft.com>
Date: Tue, 16 Sep 2025 18:48:37 +0200
Subject: [PATCH 19/31] target/i386/mshv: Set local interrupt controller state
RH-Author: Igor Mammedov <imammedo@redhat.com>
RH-MergeRequest: 435: x86: enablement for Azure L1VH OCP readiness
RH-Jira: RHEL-132193
RH-Acked-by: Vitaly Kuznetsov <vkuznets@redhat.com>
RH-Acked-by: Miroslav Rezanina <mrezanin@redhat.com>
RH-Commit: [18/30] 290eb63d2e8f99e5a91168b4fcd416ed8ddc9ada
To set the local interrupt controller state, perform hv calls retrieving
partition state from the hypervisor.
Signed-off-by: Magnus Kulke <magnuskulke@linux.microsoft.com>
Link: https://lore.kernel.org/r/20250916164847.77883-18-magnuskulke@linux.microsoft.com
Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
(cherry picked from commit ca20d46fa94887a6e42061646a71ee44cbc9adb0)
Signed-off-by: Igor Mammedov <imammedo@redhat.com>
---
target/i386/mshv/mshv-cpu.c | 117 ++++++++++++++++++++++++++++++++++++
1 file changed, 117 insertions(+)
diff --git a/target/i386/mshv/mshv-cpu.c b/target/i386/mshv/mshv-cpu.c
index 8b10c79e54..0fe3cbb48d 100644
--- a/target/i386/mshv/mshv-cpu.c
+++ b/target/i386/mshv/mshv-cpu.c
@@ -12,6 +12,7 @@
#include "qemu/osdep.h"
#include "qemu/error-report.h"
+#include "qemu/memalign.h"
#include "qemu/typedefs.h"
#include "system/mshv.h"
@@ -21,6 +22,7 @@
#include "hw/hyperv/hvgdk.h"
#include "hw/hyperv/hvgdk_mini.h"
#include "hw/hyperv/hvhdk_mini.h"
+#include "hw/i386/apic_internal.h"
#include "cpu.h"
#include "emulate/x86_decode.h"
@@ -562,6 +564,114 @@ static int set_cpu_state(const CPUState *cpu, const MshvFPU *fpu_regs,
return 0;
}
+static int get_vp_state(int cpu_fd, struct mshv_get_set_vp_state *state)
+{
+ int ret;
+
+ ret = ioctl(cpu_fd, MSHV_GET_VP_STATE, state);
+ if (ret < 0) {
+ error_report("failed to get partition state: %s", strerror(errno));
+ return -1;
+ }
+
+ return 0;
+}
+
+static int get_lapic(int cpu_fd,
+ struct hv_local_interrupt_controller_state *state)
+{
+ int ret;
+ size_t size = 4096;
+ /* buffer aligned to 4k, as *state requires that */
+ void *buffer = qemu_memalign(size, size);
+ struct mshv_get_set_vp_state mshv_state = { 0 };
+
+ mshv_state.buf_ptr = (uint64_t) buffer;
+ mshv_state.buf_sz = size;
+ mshv_state.type = MSHV_VP_STATE_LAPIC;
+
+ ret = get_vp_state(cpu_fd, &mshv_state);
+ if (ret == 0) {
+ memcpy(state, buffer, sizeof(*state));
+ }
+ qemu_vfree(buffer);
+ if (ret < 0) {
+ error_report("failed to get lapic");
+ return -1;
+ }
+
+ return 0;
+}
+
+static uint32_t set_apic_delivery_mode(uint32_t reg, uint32_t mode)
+{
+ return ((reg) & ~0x700) | ((mode) << 8);
+}
+
+static int set_vp_state(int cpu_fd, const struct mshv_get_set_vp_state *state)
+{
+ int ret;
+
+ ret = ioctl(cpu_fd, MSHV_SET_VP_STATE, state);
+ if (ret < 0) {
+ error_report("failed to set partition state: %s", strerror(errno));
+ return -1;
+ }
+
+ return 0;
+}
+
+static int set_lapic(int cpu_fd,
+ const struct hv_local_interrupt_controller_state *state)
+{
+ int ret;
+ size_t size = 4096;
+ /* buffer aligned to 4k, as *state requires that */
+ void *buffer = qemu_memalign(size, size);
+ struct mshv_get_set_vp_state mshv_state = { 0 };
+
+ if (!state) {
+ error_report("lapic state is NULL");
+ return -1;
+ }
+ memcpy(buffer, state, sizeof(*state));
+
+ mshv_state.buf_ptr = (uint64_t) buffer;
+ mshv_state.buf_sz = size;
+ mshv_state.type = MSHV_VP_STATE_LAPIC;
+
+ ret = set_vp_state(cpu_fd, &mshv_state);
+ qemu_vfree(buffer);
+ if (ret < 0) {
+ error_report("failed to set lapic: %s", strerror(errno));
+ return -1;
+ }
+
+ return 0;
+}
+
+static int set_lint(int cpu_fd)
+{
+ int ret;
+ uint32_t *lvt_lint0, *lvt_lint1;
+
+ struct hv_local_interrupt_controller_state lapic_state = { 0 };
+ ret = get_lapic(cpu_fd, &lapic_state);
+ if (ret < 0) {
+ return ret;
+ }
+
+ lvt_lint0 = &lapic_state.apic_lvt_lint0;
+ *lvt_lint0 = set_apic_delivery_mode(*lvt_lint0, APIC_DM_EXTINT);
+
+ lvt_lint1 = &lapic_state.apic_lvt_lint1;
+ *lvt_lint1 = set_apic_delivery_mode(*lvt_lint1, APIC_DM_NMI);
+
+ /* TODO: should we skip setting lapic if the values are the same? */
+
+ return set_lapic(cpu_fd, &lapic_state);
+}
+
/*
* TODO: populate topology info:
*
@@ -573,6 +683,7 @@ int mshv_configure_vcpu(const CPUState *cpu, const struct MshvFPU *fpu,
uint64_t xcr0)
{
int ret;
+ int cpu_fd = mshv_vcpufd(cpu);
ret = set_cpu_state(cpu, fpu, xcr0);
if (ret < 0) {
@@ -580,6 +691,12 @@ int mshv_configure_vcpu(const CPUState *cpu, const struct MshvFPU *fpu,
return -1;
}
+ ret = set_lint(cpu_fd);
+ if (ret < 0) {
+ error_report("failed to set lpic int");
+ return -1;
+ }
+
return 0;
}
--
2.51.1

View File

@ -0,0 +1,193 @@
From 8baf8bd7e48ed651f83da39417c55f5b0ab34d08 Mon Sep 17 00:00:00 2001
From: Magnus Kulke <magnuskulke@linux.microsoft.com>
Date: Thu, 2 Oct 2025 09:50:12 +0200
Subject: [PATCH 27/31] target/i386/mshv: Use preallocated page for hvcall
RH-Author: Igor Mammedov <imammedo@redhat.com>
RH-MergeRequest: 435: x86: enablement for Azure L1VH OCP readiness
RH-Jira: RHEL-132193
RH-Acked-by: Vitaly Kuznetsov <vkuznets@redhat.com>
RH-Acked-by: Miroslav Rezanina <mrezanin@redhat.com>
RH-Commit: [26/30] d0c3fa02d62be1a4b15d28abd4d92b4302e30ea3
There are hvcalls that are invoked during MMIO exits, the payload is of
dynamic size. To avoid heap allocations we can use preallocated pages as
in/out buffer for those calls. A page is reserved per vCPU and used for
set/get register hv calls.
Signed-off-by: Magnus Kulke <magnuskulke@linux.microsoft.com>
Link: https://lore.kernel.org/r/20250916164847.77883-26-magnuskulke@linux.microsoft.com
[Use standard MAX_CONST macro; mshv.h/mshv_int.h split. - Paolo]
Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
(cherry picked from commit e4a20afce59937073298d716cc829bdc026542dc)
Signed-off-by: Igor Mammedov <imammedo@redhat.com>
---
accel/mshv/mshv-all.c | 2 +-
include/system/mshv_int.h | 7 +++++++
target/i386/mshv/mshv-cpu.c | 38 +++++++++++++++++++++++++------------
3 files changed, 34 insertions(+), 13 deletions(-)
diff --git a/accel/mshv/mshv-all.c b/accel/mshv/mshv-all.c
index 5edfcbad9d..45174f7c4e 100644
--- a/accel/mshv/mshv-all.c
+++ b/accel/mshv/mshv-all.c
@@ -399,8 +399,8 @@ static int mshv_init_vcpu(CPUState *cpu)
uint8_t vp_index = cpu->cpu_index;
int ret;
- mshv_arch_init_vcpu(cpu);
cpu->accel = g_new0(AccelCPUState, 1);
+ mshv_arch_init_vcpu(cpu);
ret = mshv_create_vcpu(vm_fd, vp_index, &cpu->accel->cpufd);
if (ret < 0) {
diff --git a/include/system/mshv_int.h b/include/system/mshv_int.h
index 6350c69e9d..490563c1ab 100644
--- a/include/system/mshv_int.h
+++ b/include/system/mshv_int.h
@@ -20,9 +20,16 @@
typedef struct hyperv_message hv_message;
+typedef struct MshvHvCallArgs {
+ void *base;
+ void *input_page;
+ void *output_page;
+} MshvHvCallArgs;
+
struct AccelCPUState {
int cpufd;
bool dirty;
+ MshvHvCallArgs hvcall_args;
};
typedef struct MshvMemoryListener {
diff --git a/target/i386/mshv/mshv-cpu.c b/target/i386/mshv/mshv-cpu.c
index de87142bff..1f7b9cb37e 100644
--- a/target/i386/mshv/mshv-cpu.c
+++ b/target/i386/mshv/mshv-cpu.c
@@ -34,6 +34,10 @@
#include <sys/ioctl.h>
+#define MAX_REGISTER_COUNT (MAX_CONST(ARRAY_SIZE(STANDARD_REGISTER_NAMES), \
+ MAX_CONST(ARRAY_SIZE(SPECIAL_REGISTER_NAMES), \
+ ARRAY_SIZE(FPU_REGISTER_NAMES))))
+
static enum hv_register_name STANDARD_REGISTER_NAMES[18] = {
HV_X64_REGISTER_RAX,
HV_X64_REGISTER_RBX,
@@ -151,7 +155,7 @@ int mshv_set_generic_regs(const CPUState *cpu, const hv_register_assoc *assocs,
int cpu_fd = mshv_vcpufd(cpu);
int vp_index = cpu->cpu_index;
size_t in_sz, assocs_sz;
- hv_input_set_vp_registers *in;
+ hv_input_set_vp_registers *in = cpu->accel->hvcall_args.input_page;
struct mshv_root_hvcall args = {0};
int ret;
@@ -160,7 +164,7 @@ int mshv_set_generic_regs(const CPUState *cpu, const hv_register_assoc *assocs,
in_sz = sizeof(hv_input_set_vp_registers) + assocs_sz;
/* fill the input struct */
- in = g_malloc0(in_sz);
+ memset(in, 0, sizeof(hv_input_set_vp_registers));
in->vp_index = vp_index;
memcpy(in->elements, assocs, assocs_sz);
@@ -172,7 +176,6 @@ int mshv_set_generic_regs(const CPUState *cpu, const hv_register_assoc *assocs,
/* perform the call */
ret = mshv_hvcall(cpu_fd, &args);
- g_free(in);
if (ret < 0) {
error_report("Failed to set registers");
return -1;
@@ -193,8 +196,8 @@ static int get_generic_regs(CPUState *cpu, hv_register_assoc *assocs,
{
int cpu_fd = mshv_vcpufd(cpu);
int vp_index = cpu->cpu_index;
- hv_input_get_vp_registers *in;
- hv_register_value *values;
+ hv_input_get_vp_registers *in = cpu->accel->hvcall_args.input_page;
+ hv_register_value *values = cpu->accel->hvcall_args.output_page;
size_t in_sz, names_sz, values_sz;
int i, ret;
struct mshv_root_hvcall args = {0};
@@ -204,15 +207,14 @@ static int get_generic_regs(CPUState *cpu, hv_register_assoc *assocs,
in_sz = sizeof(hv_input_get_vp_registers) + names_sz;
/* fill the input struct */
- in = g_malloc0(in_sz);
+ memset(in, 0, sizeof(hv_input_get_vp_registers));
in->vp_index = vp_index;
for (i = 0; i < n_regs; i++) {
in->names[i] = assocs[i].name;
}
- /* allocate value output buffer */
+ /* determine size of value output buffer */
values_sz = n_regs * sizeof(union hv_register_value);
- values = g_malloc0(values_sz);
/* create the hvcall envelope */
args.code = HVCALL_GET_VP_REGISTERS;
@@ -224,16 +226,13 @@ static int get_generic_regs(CPUState *cpu, hv_register_assoc *assocs,
/* perform the call */
ret = mshv_hvcall(cpu_fd, &args);
- g_free(in);
if (ret < 0) {
- g_free(values);
error_report("Failed to retrieve registers");
return -1;
}
/* assert we got all registers */
if (args.reps != n_regs) {
- g_free(values);
error_report("Failed to retrieve registers: expected %zu elements"
", got %u", n_regs, args.reps);
return -1;
@@ -243,7 +242,6 @@ static int get_generic_regs(CPUState *cpu, hv_register_assoc *assocs,
for (i = 0; i < n_regs; i++) {
assocs[i].value = values[i];
}
- g_free(values);
return 0;
}
@@ -1696,6 +1694,19 @@ void mshv_arch_init_vcpu(CPUState *cpu)
{
X86CPU *x86_cpu = X86_CPU(cpu);
CPUX86State *env = &x86_cpu->env;
+ AccelCPUState *state = cpu->accel;
+ size_t page = HV_HYP_PAGE_SIZE;
+ void *mem = qemu_memalign(page, 2 * page);
+
+ /* sanity check, to make sure we don't overflow the page */
+ QEMU_BUILD_BUG_ON((MAX_REGISTER_COUNT
+ * sizeof(hv_register_assoc)
+ + sizeof(hv_input_get_vp_registers)
+ > HV_HYP_PAGE_SIZE));
+
+ state->hvcall_args.base = mem;
+ state->hvcall_args.input_page = mem;
+ state->hvcall_args.output_page = (uint8_t *)mem + page;
env->emu_mmio_buf = g_new(char, 4096);
}
@@ -1704,7 +1715,10 @@ void mshv_arch_destroy_vcpu(CPUState *cpu)
{
X86CPU *x86_cpu = X86_CPU(cpu);
CPUX86State *env = &x86_cpu->env;
+ AccelCPUState *state = cpu->accel;
+ g_free(state->hvcall_args.base);
+ state->hvcall_args = (MshvHvCallArgs){0};
g_clear_pointer(&env->emu_mmio_buf, g_free);
}
--
2.51.1

View File

@ -0,0 +1,113 @@
From 392edd1e35e63e6f92d8badfb086b656ea82593a Mon Sep 17 00:00:00 2001
From: Magnus Kulke <magnuskulke@linux.microsoft.com>
Date: Tue, 16 Sep 2025 18:48:41 +0200
Subject: [PATCH 23/31] target/i386/mshv: Write MSRs to the hypervisor
RH-Author: Igor Mammedov <imammedo@redhat.com>
RH-MergeRequest: 435: x86: enablement for Azure L1VH OCP readiness
RH-Jira: RHEL-132193
RH-Acked-by: Vitaly Kuznetsov <vkuznets@redhat.com>
RH-Acked-by: Miroslav Rezanina <mrezanin@redhat.com>
RH-Commit: [22/30] 341bb2428b5a41d9b5d6ebc325ba384464c7542a
Push current model-specific register (MSR) values to MSHV's vCPUs as
part of setting state to the hypervisor.
Signed-off-by: Magnus Kulke <magnuskulke@linux.microsoft.com>
Link: https://lore.kernel.org/r/20250916164847.77883-22-magnuskulke@linux.microsoft.com
Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
(cherry picked from commit 64118f452cbd97cd9fa790b1c15b65b435f136d2)
Signed-off-by: Igor Mammedov <imammedo@redhat.com>
---
target/i386/mshv/mshv-cpu.c | 68 +++++++++++++++++++++++++++++++++++--
1 file changed, 66 insertions(+), 2 deletions(-)
diff --git a/target/i386/mshv/mshv-cpu.c b/target/i386/mshv/mshv-cpu.c
index 424ebdb122..33a3ce8b11 100644
--- a/target/i386/mshv/mshv-cpu.c
+++ b/target/i386/mshv/mshv-cpu.c
@@ -998,6 +998,65 @@ static int put_regs(const CPUState *cpu)
return 0;
}
+struct MsrPair {
+ uint32_t index;
+ uint64_t value;
+};
+
+static int put_msrs(const CPUState *cpu)
+{
+ int ret = 0;
+ X86CPU *x86cpu = X86_CPU(cpu);
+ CPUX86State *env = &x86cpu->env;
+ MshvMsrEntries *msrs = g_malloc0(sizeof(MshvMsrEntries));
+
+ struct MsrPair pairs[] = {
+ { MSR_IA32_SYSENTER_CS, env->sysenter_cs },
+ { MSR_IA32_SYSENTER_ESP, env->sysenter_esp },
+ { MSR_IA32_SYSENTER_EIP, env->sysenter_eip },
+ { MSR_EFER, env->efer },
+ { MSR_PAT, env->pat },
+ { MSR_STAR, env->star },
+ { MSR_CSTAR, env->cstar },
+ { MSR_LSTAR, env->lstar },
+ { MSR_KERNELGSBASE, env->kernelgsbase },
+ { MSR_FMASK, env->fmask },
+ { MSR_MTRRdefType, env->mtrr_deftype },
+ { MSR_VM_HSAVE_PA, env->vm_hsave },
+ { MSR_SMI_COUNT, env->msr_smi_count },
+ { MSR_IA32_PKRS, env->pkrs },
+ { MSR_IA32_BNDCFGS, env->msr_bndcfgs },
+ { MSR_IA32_XSS, env->xss },
+ { MSR_IA32_UMWAIT_CONTROL, env->umwait },
+ { MSR_IA32_TSX_CTRL, env->tsx_ctrl },
+ { MSR_AMD64_TSC_RATIO, env->amd_tsc_scale_msr },
+ { MSR_TSC_AUX, env->tsc_aux },
+ { MSR_TSC_ADJUST, env->tsc_adjust },
+ { MSR_IA32_SMBASE, env->smbase },
+ { MSR_IA32_SPEC_CTRL, env->spec_ctrl },
+ { MSR_VIRT_SSBD, env->virt_ssbd },
+ };
+
+ if (ARRAY_SIZE(pairs) > MSHV_MSR_ENTRIES_COUNT) {
+ error_report("MSR entries exceed maximum size");
+ g_free(msrs);
+ return -1;
+ }
+
+ for (size_t i = 0; i < ARRAY_SIZE(pairs); i++) {
+ MshvMsrEntry *entry = &msrs->entries[i];
+ entry->index = pairs[i].index;
+ entry->reserved = 0;
+ entry->data = pairs[i].value;
+ msrs->nmsrs++;
+ }
+
+ ret = mshv_configure_msr(cpu, &msrs->entries[0], msrs->nmsrs);
+ g_free(msrs);
+ return ret;
+}
+
+
int mshv_arch_put_registers(const CPUState *cpu)
{
int ret;
@@ -1008,8 +1067,13 @@ int mshv_arch_put_registers(const CPUState *cpu)
return -1;
}
- error_report("unimplemented");
- abort();
+ ret = put_msrs(cpu);
+ if (ret < 0) {
+ error_report("Failed to put msrs");
+ return -1;
+ }
+
+ return 0;
}
void mshv_arch_amend_proc_features(
--
2.51.1

View File

@ -0,0 +1,219 @@
From 48b40bd483dbafcea66f84465796bdf1eb1fc962 Mon Sep 17 00:00:00 2001
From: Paolo Bonzini <pbonzini@redhat.com>
Date: Tue, 2 Sep 2025 07:17:09 +0200
Subject: [PATCH 12/31] treewide: rename
qemu_wait_io_event/qemu_wait_io_event_common
RH-Author: Igor Mammedov <imammedo@redhat.com>
RH-MergeRequest: 435: x86: enablement for Azure L1VH OCP readiness
RH-Jira: RHEL-132193
RH-Acked-by: Vitaly Kuznetsov <vkuznets@redhat.com>
RH-Acked-by: Miroslav Rezanina <mrezanin@redhat.com>
RH-Commit: [11/30] cabe3f5e84e62f47c8ff23a3bc253c2f64e05804
Do so before extending it to the user-mode emulators, where there is no
such thing as an "I/O thread".
Reviewed-by: Richard Henderson <richard.henderson@linaro.org>
Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
(cherry picked from commit 871de7078fcaf597605576b97b32fab14722ea43)
Signed-off-by: Igor Mammedov <imammedo@redhat.com>
Conflicts: (due to missing cpu exit/interrupt_request refactoring)
cpu-common.c
include/hw/core/cpu.h
---
accel/dummy-cpus.c | 2 +-
accel/hvf/hvf-accel-ops.c | 2 +-
accel/kvm/kvm-accel-ops.c | 2 +-
accel/tcg/tcg-accel-ops-mttcg.c | 2 +-
accel/tcg/tcg-accel-ops-rr.c | 4 ++--
cpu-common.c | 1 +
include/hw/core/cpu.h | 9 +++++++++
include/system/cpus.h | 4 ++--
system/cpus.c | 6 +++---
target/i386/nvmm/nvmm-accel-ops.c | 2 +-
target/i386/whpx/whpx-accel-ops.c | 2 +-
11 files changed, 23 insertions(+), 13 deletions(-)
diff --git a/accel/dummy-cpus.c b/accel/dummy-cpus.c
index 03cfc0fa01..225a47c31f 100644
--- a/accel/dummy-cpus.c
+++ b/accel/dummy-cpus.c
@@ -57,7 +57,7 @@ static void *dummy_cpu_thread_fn(void *arg)
qemu_sem_wait(&cpu->sem);
#endif
bql_lock();
- qemu_wait_io_event(cpu);
+ qemu_process_cpu_events(cpu);
} while (!cpu->unplug);
bql_unlock();
diff --git a/accel/hvf/hvf-accel-ops.c b/accel/hvf/hvf-accel-ops.c
index d488d6afba..7a27bdadb4 100644
--- a/accel/hvf/hvf-accel-ops.c
+++ b/accel/hvf/hvf-accel-ops.c
@@ -198,7 +198,7 @@ static void *hvf_cpu_thread_fn(void *arg)
cpu_handle_guest_debug(cpu);
}
}
- qemu_wait_io_event(cpu);
+ qemu_process_cpu_events(cpu);
} while (!cpu->unplug || cpu_can_run(cpu));
hvf_vcpu_destroy(cpu);
diff --git a/accel/kvm/kvm-accel-ops.c b/accel/kvm/kvm-accel-ops.c
index b709187c7d..65a7f76a69 100644
--- a/accel/kvm/kvm-accel-ops.c
+++ b/accel/kvm/kvm-accel-ops.c
@@ -53,7 +53,7 @@ static void *kvm_vcpu_thread_fn(void *arg)
cpu_handle_guest_debug(cpu);
}
}
- qemu_wait_io_event(cpu);
+ qemu_process_cpu_events(cpu);
} while (!cpu->unplug || cpu_can_run(cpu));
kvm_destroy_vcpu(cpu);
diff --git a/accel/tcg/tcg-accel-ops-mttcg.c b/accel/tcg/tcg-accel-ops-mttcg.c
index 337b993d3d..a3255000df 100644
--- a/accel/tcg/tcg-accel-ops-mttcg.c
+++ b/accel/tcg/tcg-accel-ops-mttcg.c
@@ -113,7 +113,7 @@ static void *mttcg_cpu_thread_fn(void *arg)
}
}
- qemu_wait_io_event(cpu);
+ qemu_process_cpu_events(cpu);
} while (!cpu->unplug || cpu_can_run(cpu));
tcg_cpu_destroy(cpu);
diff --git a/accel/tcg/tcg-accel-ops-rr.c b/accel/tcg/tcg-accel-ops-rr.c
index 6eec5c9eee..60ad1a39f0 100644
--- a/accel/tcg/tcg-accel-ops-rr.c
+++ b/accel/tcg/tcg-accel-ops-rr.c
@@ -117,7 +117,7 @@ static void rr_wait_io_event(void)
rr_start_kick_timer();
CPU_FOREACH(cpu) {
- qemu_wait_io_event_common(cpu);
+ qemu_process_cpu_events_common(cpu);
}
}
@@ -203,7 +203,7 @@ static void *rr_cpu_thread_fn(void *arg)
/* process any pending work */
CPU_FOREACH(cpu) {
current_cpu = cpu;
- qemu_wait_io_event_common(cpu);
+ qemu_process_cpu_events_common(cpu);
}
}
diff --git a/cpu-common.c b/cpu-common.c
index ef5757d23b..8e4cbac290 100644
--- a/cpu-common.c
+++ b/cpu-common.c
@@ -137,6 +137,7 @@ static void queue_work_on_cpu(CPUState *cpu, struct qemu_work_item *wi)
wi->done = false;
qemu_mutex_unlock(&cpu->work_mutex);
+ /* conflict fixup due missing exit and interrupt refactoring */
qemu_cpu_kick(cpu);
}
diff --git a/include/hw/core/cpu.h b/include/hw/core/cpu.h
index 5eaf41a566..fadd6c5ac0 100644
--- a/include/hw/core/cpu.h
+++ b/include/hw/core/cpu.h
@@ -422,6 +422,15 @@ struct qemu_work_item;
* valid under cpu_list_lock.
* @created: Indicates whether the CPU thread has been successfully created.
* @halt_cond: condition variable sleeping threads can wait on.
+ * @exit_request: Another thread requests the CPU to call qemu_process_cpu_events().
+ * Should be read only by CPU thread with load-acquire, to synchronize with
+ * other threads' store-release operation.
+ *
+ * In some cases, accelerator-specific code will write exit_request from
+ * within the same thread, to "bump" the effect of qemu_cpu_kick() to
+ * the one provided by cpu_exit(), especially when processing interrupt
+ * flags. In this case, the write and read happen in the same thread
+ * and the write therefore can use qemu_atomic_set().
* @interrupt_request: Indicates a pending interrupt request.
* @halted: Nonzero if the CPU is in suspended state.
* @stop: Indicates a pending stop request.
diff --git a/include/system/cpus.h b/include/system/cpus.h
index 69be6a77a7..4aebec4870 100644
--- a/include/system/cpus.h
+++ b/include/system/cpus.h
@@ -17,8 +17,8 @@ bool cpu_work_list_empty(CPUState *cpu);
bool cpu_thread_is_idle(CPUState *cpu);
bool all_cpu_threads_idle(void);
bool cpu_can_run(CPUState *cpu);
-void qemu_wait_io_event_common(CPUState *cpu);
-void qemu_wait_io_event(CPUState *cpu);
+void qemu_process_cpu_events_common(CPUState *cpu);
+void qemu_process_cpu_events(CPUState *cpu);
void cpu_thread_signal_created(CPUState *cpu);
void cpu_thread_signal_destroyed(CPUState *cpu);
void cpu_handle_guest_debug(CPUState *cpu);
diff --git a/system/cpus.c b/system/cpus.c
index 256723558d..efffbd8df6 100644
--- a/system/cpus.c
+++ b/system/cpus.c
@@ -444,7 +444,7 @@ static void qemu_cpu_stop(CPUState *cpu, bool exit)
qemu_cond_broadcast(&qemu_pause_cond);
}
-void qemu_wait_io_event_common(CPUState *cpu)
+void qemu_process_cpu_events_common(CPUState *cpu)
{
qatomic_set_mb(&cpu->thread_kicked, false);
if (cpu->stop) {
@@ -453,7 +453,7 @@ void qemu_wait_io_event_common(CPUState *cpu)
process_queued_cpu_work(cpu);
}
-void qemu_wait_io_event(CPUState *cpu)
+void qemu_process_cpu_events(CPUState *cpu)
{
bool slept = false;
@@ -468,7 +468,7 @@ void qemu_wait_io_event(CPUState *cpu)
qemu_plugin_vcpu_resume_cb(cpu);
}
- qemu_wait_io_event_common(cpu);
+ qemu_process_cpu_events_common(cpu);
}
void cpus_kick_thread(CPUState *cpu)
diff --git a/target/i386/nvmm/nvmm-accel-ops.c b/target/i386/nvmm/nvmm-accel-ops.c
index 3799260bbd..c7b1d0f1d3 100644
--- a/target/i386/nvmm/nvmm-accel-ops.c
+++ b/target/i386/nvmm/nvmm-accel-ops.c
@@ -51,7 +51,7 @@ static void *qemu_nvmm_cpu_thread_fn(void *arg)
while (cpu_thread_is_idle(cpu)) {
qemu_cond_wait_bql(cpu->halt_cond);
}
- qemu_wait_io_event_common(cpu);
+ qemu_process_cpu_events_common(cpu);
} while (!cpu->unplug || cpu_can_run(cpu));
nvmm_destroy_vcpu(cpu);
diff --git a/target/i386/whpx/whpx-accel-ops.c b/target/i386/whpx/whpx-accel-ops.c
index da58805b1a..2ca4ee0263 100644
--- a/target/i386/whpx/whpx-accel-ops.c
+++ b/target/i386/whpx/whpx-accel-ops.c
@@ -51,7 +51,7 @@ static void *whpx_cpu_thread_fn(void *arg)
while (cpu_thread_is_idle(cpu)) {
qemu_cond_wait_bql(cpu->halt_cond);
}
- qemu_wait_io_event_common(cpu);
+ qemu_process_cpu_events_common(cpu);
} while (!cpu->unplug || cpu_can_run(cpu));
whpx_destroy_vcpu(cpu);
--
2.51.1

View File

@ -149,7 +149,7 @@ Obsoletes: %{name}-block-ssh <= %{epoch}:%{version} \
Summary: QEMU is a machine emulator and virtualizer
Name: qemu-kvm
Version: 10.1.0
Release: 8%{?rcrel}%{?dist}%{?cc_suffix}
Release: 9%{?rcrel}%{?dist}%{?cc_suffix}
# Epoch because we pushed a qemu-1.0 package. AIUI this can't ever be dropped
# Epoch 15 used for RHEL 8
# Epoch 17 used for RHEL 9 (due to release versioning offset in RHEL 8.5)
@ -230,6 +230,68 @@ Patch42: kvm-Fix-the-typo-of-vfio-pci-device-s-enable-migration-o.patch
Patch43: kvm-block-backend-Fix-race-when-resuming-queued-requests.patch
# For RHEL-133303 - The VM hit io error when do S3-PR integration on the pass-through failover multipath device [rhel-9]
Patch44: kvm-file-posix-Handle-suspended-dm-multipath-better-for-.patch
# For RHEL-131144 - qemu crash after hot-unplug disk from the multifunction enabled bus [RHEL.9.8]
Patch45: kvm-pcie_sriov-make-pcie_sriov_pf_exit-safe-on-non-SR-IO.patch
# For RHEL-132193 - [rhel 9.8]L1VH qemu downstream initial merge RHEL9
Patch46: kvm-accel-Add-Meson-and-config-support-for-MSHV-accelera.patch
# For RHEL-132193 - [rhel 9.8]L1VH qemu downstream initial merge RHEL9
Patch47: kvm-target-i386-emulate-Allow-instruction-decoding-from-.patch
# For RHEL-132193 - [rhel 9.8]L1VH qemu downstream initial merge RHEL9
Patch48: kvm-target-i386-mshv-Add-x86-decoder-emu-implementation.patch
# For RHEL-132193 - [rhel 9.8]L1VH qemu downstream initial merge RHEL9
Patch49: kvm-hw-intc-Generalize-APIC-helper-names-from-kvm_-to-ac.patch
# For RHEL-132193 - [rhel 9.8]L1VH qemu downstream initial merge RHEL9
Patch50: kvm-include-hw-hyperv-Add-MSHV-ABI-header-definitions.patch
# For RHEL-132193 - [rhel 9.8]L1VH qemu downstream initial merge RHEL9
Patch51: kvm-linux-headers-linux-Add-mshv.h-headers.patch
# For RHEL-132193 - [rhel 9.8]L1VH qemu downstream initial merge RHEL9
Patch52: kvm-accel-mshv-Add-accelerator-skeleton.patch
# For RHEL-132193 - [rhel 9.8]L1VH qemu downstream initial merge RHEL9
Patch53: kvm-accel-mshv-Register-memory-region-listeners.patch
# For RHEL-132193 - [rhel 9.8]L1VH qemu downstream initial merge RHEL9
Patch54: kvm-accel-mshv-Initialize-VM-partition.patch
# For RHEL-132193 - [rhel 9.8]L1VH qemu downstream initial merge RHEL9
Patch55: kvm-accel-mshv-Add-vCPU-creation-and-execution-loop.patch
# For RHEL-132193 - [rhel 9.8]L1VH qemu downstream initial merge RHEL9
Patch56: kvm-treewide-rename-qemu_wait_io_event-qemu_wait_io_even.patch
# For RHEL-132193 - [rhel 9.8]L1VH qemu downstream initial merge RHEL9
Patch57: kvm-accel-mshv-Add-vCPU-signal-handling.patch
# For RHEL-132193 - [rhel 9.8]L1VH qemu downstream initial merge RHEL9
Patch58: kvm-target-i386-mshv-Add-CPU-create-and-remove-logic.patch
# For RHEL-132193 - [rhel 9.8]L1VH qemu downstream initial merge RHEL9
Patch59: kvm-target-i386-mshv-Implement-mshv_store_regs.patch
# For RHEL-132193 - [rhel 9.8]L1VH qemu downstream initial merge RHEL9
Patch60: kvm-target-i386-mshv-Implement-mshv_get_standard_regs.patch
# For RHEL-132193 - [rhel 9.8]L1VH qemu downstream initial merge RHEL9
Patch61: kvm-target-i386-mshv-Implement-mshv_get_special_regs.patch
# For RHEL-132193 - [rhel 9.8]L1VH qemu downstream initial merge RHEL9
Patch62: kvm-target-i386-mshv-Implement-mshv_arch_put_registers.patch
# For RHEL-132193 - [rhel 9.8]L1VH qemu downstream initial merge RHEL9
Patch63: kvm-target-i386-mshv-Set-local-interrupt-controller-stat.patch
# For RHEL-132193 - [rhel 9.8]L1VH qemu downstream initial merge RHEL9
Patch64: kvm-target-i386-mshv-Register-CPUID-entries-with-MSHV.patch
# For RHEL-132193 - [rhel 9.8]L1VH qemu downstream initial merge RHEL9
Patch65: kvm-target-i386-mshv-Register-MSRs-with-MSHV.patch
# For RHEL-132193 - [rhel 9.8]L1VH qemu downstream initial merge RHEL9
Patch66: kvm-target-i386-mshv-Integrate-x86-instruction-decoder-e.patch
# For RHEL-132193 - [rhel 9.8]L1VH qemu downstream initial merge RHEL9
Patch67: kvm-target-i386-mshv-Write-MSRs-to-the-hypervisor.patch
# For RHEL-132193 - [rhel 9.8]L1VH qemu downstream initial merge RHEL9
Patch68: kvm-target-i386-mshv-Implement-mshv_vcpu_run.patch
# For RHEL-132193 - [rhel 9.8]L1VH qemu downstream initial merge RHEL9
Patch69: kvm-accel-mshv-Handle-overlapping-mem-mappings.patch
# For RHEL-132193 - [rhel 9.8]L1VH qemu downstream initial merge RHEL9
Patch70: kvm-qapi-accel-Allow-to-query-mshv-capabilities.patch
# For RHEL-132193 - [rhel 9.8]L1VH qemu downstream initial merge RHEL9
Patch71: kvm-target-i386-mshv-Use-preallocated-page-for-hvcall.patch
# For RHEL-132193 - [rhel 9.8]L1VH qemu downstream initial merge RHEL9
Patch72: kvm-docs-Add-mshv-to-documentation.patch
# For RHEL-132193 - [rhel 9.8]L1VH qemu downstream initial merge RHEL9
Patch73: kvm-MAINTAINERS-Add-maintainers-for-mshv-accelerator.patch
# For RHEL-132193 - [rhel 9.8]L1VH qemu downstream initial merge RHEL9
Patch74: kvm-accel-mshv-initialize-thread-name.patch
# For RHEL-132193 - [rhel 9.8]L1VH qemu downstream initial merge RHEL9
Patch75: kvm-accel-mshv-use-return-value-of-handle_pio_str_read.patch
# For RHEL-11424 - [IBM 9.6 FEAT] KVM: Full boot order support - qemu part
@ -1944,6 +2006,43 @@ useradd -r -u 107 -g qemu -G kvm -d / -s /sbin/nologin \
%endif
%changelog
* Tue Dec 09 2025 Jon Maloy <jmaloy@redhat.com> - 10.1.0-9
- kvm-pcie_sriov-make-pcie_sriov_pf_exit-safe-on-non-SR-IO.patch [RHEL-131144]
- kvm-accel-Add-Meson-and-config-support-for-MSHV-accelera.patch [RHEL-132193]
- kvm-target-i386-emulate-Allow-instruction-decoding-from-.patch [RHEL-132193]
- kvm-target-i386-mshv-Add-x86-decoder-emu-implementation.patch [RHEL-132193]
- kvm-hw-intc-Generalize-APIC-helper-names-from-kvm_-to-ac.patch [RHEL-132193]
- kvm-include-hw-hyperv-Add-MSHV-ABI-header-definitions.patch [RHEL-132193]
- kvm-linux-headers-linux-Add-mshv.h-headers.patch [RHEL-132193]
- kvm-accel-mshv-Add-accelerator-skeleton.patch [RHEL-132193]
- kvm-accel-mshv-Register-memory-region-listeners.patch [RHEL-132193]
- kvm-accel-mshv-Initialize-VM-partition.patch [RHEL-132193]
- kvm-accel-mshv-Add-vCPU-creation-and-execution-loop.patch [RHEL-132193]
- kvm-treewide-rename-qemu_wait_io_event-qemu_wait_io_even.patch [RHEL-132193]
- kvm-accel-mshv-Add-vCPU-signal-handling.patch [RHEL-132193]
- kvm-target-i386-mshv-Add-CPU-create-and-remove-logic.patch [RHEL-132193]
- kvm-target-i386-mshv-Implement-mshv_store_regs.patch [RHEL-132193]
- kvm-target-i386-mshv-Implement-mshv_get_standard_regs.patch [RHEL-132193]
- kvm-target-i386-mshv-Implement-mshv_get_special_regs.patch [RHEL-132193]
- kvm-target-i386-mshv-Implement-mshv_arch_put_registers.patch [RHEL-132193]
- kvm-target-i386-mshv-Set-local-interrupt-controller-stat.patch [RHEL-132193]
- kvm-target-i386-mshv-Register-CPUID-entries-with-MSHV.patch [RHEL-132193]
- kvm-target-i386-mshv-Register-MSRs-with-MSHV.patch [RHEL-132193]
- kvm-target-i386-mshv-Integrate-x86-instruction-decoder-e.patch [RHEL-132193]
- kvm-target-i386-mshv-Write-MSRs-to-the-hypervisor.patch [RHEL-132193]
- kvm-target-i386-mshv-Implement-mshv_vcpu_run.patch [RHEL-132193]
- kvm-accel-mshv-Handle-overlapping-mem-mappings.patch [RHEL-132193]
- kvm-qapi-accel-Allow-to-query-mshv-capabilities.patch [RHEL-132193]
- kvm-target-i386-mshv-Use-preallocated-page-for-hvcall.patch [RHEL-132193]
- kvm-docs-Add-mshv-to-documentation.patch [RHEL-132193]
- kvm-MAINTAINERS-Add-maintainers-for-mshv-accelerator.patch [RHEL-132193]
- kvm-accel-mshv-initialize-thread-name.patch [RHEL-132193]
- kvm-accel-mshv-use-return-value-of-handle_pio_str_read.patch [RHEL-132193]
- Resolves: RHEL-131144
(qemu crash after hot-unplug disk from the multifunction enabled bus [RHEL.9.8])
- Resolves: RHEL-132193
([rhel 9.8]L1VH qemu downstream initial merge RHEL9)
* Mon Dec 08 2025 Jon Maloy <jmaloy@redhat.com> - 10.1.0-8
- kvm-file-posix-Handle-suspended-dm-multipath-better-for-.patch [RHEL-133303]
- Resolves: RHEL-133303