Add the main vCPU run loop for MSHV using the MSHV_RUN_VP_IOCTL.
Handle MMIO exits by emulating the instruction using the syndrome
information from ESR_EL2.
Signed-off-by: Anirudh Rayabharam (Microsoft) <anirudh@anirudhrb.com>
---
include/hw/hyperv/hvgdk_mini.h | 44 +++++++++
target/arm/mshv/mshv-all.c | 198 +++++++++++++++++++++++++++++++++++++++++
2 files changed, 242 insertions(+)
diff --git a/include/hw/hyperv/hvgdk_mini.h b/include/hw/hyperv/hvgdk_mini.h
index d56be0d70f..84b3c6af5f 100644
--- a/include/hw/hyperv/hvgdk_mini.h
+++ b/include/hw/hyperv/hvgdk_mini.h
@@ -750,6 +750,50 @@ struct hv_x64_memory_intercept_message {
uint8_t instruction_bytes[16];
};
+union hv_arm64_vp_execution_state {
+ uint16_t as_uint16;
+ struct {
+ uint16_t cpl:2;
+ uint16_t debug_active:1;
+ uint16_t interruption_pending:1;
+ uint16_t vtl:4;
+ uint16_t virtualization_fault_active:1;
+ uint16_t reserved:7;
+ };
+};
+
+struct hv_arm64_intercept_message_header {
+ uint32_t vp_index;
+ uint8_t instruction_length;
+ uint8_t intercept_access_type;
+ union hv_arm64_vp_execution_state execution_state;
+ uint64_t pc;
+ uint64_t cpsr;
+};
+
+union hv_arm64_memory_access_info {
+ uint8_t as_uint8;
+ struct {
+ uint8_t gva_valid:1;
+ uint8_t gva_gpa_valid:1;
+ uint8_t hypercall_output_pending:1;
+ uint8_t reserved:5;
+ };
+};
+
+struct hv_arm64_memory_intercept_message {
+ struct hv_arm64_intercept_message_header header;
+ uint32_t cache_type; /* enum hv_cache_type */
+ uint8_t instruction_byte_count;
+ union hv_arm64_memory_access_info memory_access_info;
+ uint16_t reserved1;
+ uint8_t instruction_bytes[4];
+ uint32_t reserved2;
+ uint64_t guest_virtual_address;
+ uint64_t guest_physical_address;
+ uint64_t syndrome;
+};
+
union hv_message_flags {
uint8_t asu8;
struct {
diff --git a/target/arm/mshv/mshv-all.c b/target/arm/mshv/mshv-all.c
index 2b983845d3..b0649ba10b 100644
--- a/target/arm/mshv/mshv-all.c
+++ b/target/arm/mshv/mshv-all.c
@@ -166,8 +166,206 @@ int mshv_arch_put_registers(const CPUState *cpu)
return 0;
}
+static int set_memory_info(const struct hyperv_message *msg,
+ struct hv_arm64_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;
+}
+
+typedef union {
+ uint64_t raw;
+ struct {
+ uint32_t iss:25;
+ uint32_t il:1;
+ uint32_t ec:6;
+ uint32_t iss2:5;
+ uint32_t _rsvd:27;
+ } QEMU_PACKED;
+} EsrEl2;
+
+typedef union {
+ uint32_t raw;
+ struct {
+ uint32_t dfsc:6;
+ uint32_t wnr:1;
+ uint32_t s1ptw:1;
+ uint32_t cm:1;
+ uint32_t ea:1;
+ uint32_t fnv:1;
+ uint32_t set:2;
+ uint32_t vncr:1;
+ uint32_t ar:1;
+ uint32_t sf:1;
+ uint32_t srt:5;
+ uint32_t sse:1;
+ uint32_t sas:2;
+ uint32_t isv:1;
+ uint32_t _unused:7;
+ } QEMU_PACKED;
+} IssDataAbort;
+
+typedef enum {
+ data_abort_lower = 36,
+ data_abort = 37,
+} ExceptionClass;
+
+int mshv_store_regs(CPUState *cpu)
+{
+ int ret;
+
+ ret = set_standard_regs(cpu);
+ if (ret < 0) {
+ error_report("Failed to store standard registers");
+ return -1;
+ }
+
+ /* TODO: should store special registers? the equivalent hvf code doesn't */
+
+ return 0;
+}
+
+static int emulate_with_syndrome(CPUState *cpu,
+ struct hv_arm64_memory_intercept_message *info)
+{
+ ARMCPU *arm_cpu = ARM_CPU(cpu);
+ CPUARMState *env = &arm_cpu->env;
+ int ret;
+ EsrEl2 syndrome = { 0 };
+ IssDataAbort iss = { 0 };
+ uint64_t gpa = info->guest_physical_address;
+ uint64_t len, reg_index;
+ bool sign_extend;
+
+ syndrome.raw = info->syndrome;
+
+ if (!(syndrome.ec == data_abort_lower || syndrome.ec == data_abort)) {
+ error_report("Unknown exception class 0x%x", syndrome.ec);
+ return -1;
+ }
+
+ iss.raw = syndrome.iss;
+ if (!iss.isv) {
+ error_report("Invalid ESR EL2 ISV field");
+ return -1;
+ }
+
+ len = 1ULL << iss.sas;
+ sign_extend = iss.sse;
+ reg_index = iss.srt;
+
+ ret = mshv_load_regs(cpu);
+ if (ret < 0) {
+ error_report("Failed to load registers");
+ return -1;
+ }
+
+ if (iss.wnr) {
+ uint8_t data[8];
+ uint64_t val = reg_index < 31 ? env->xregs[reg_index] : 0ULL;
+
+ val = cpu_to_le64(val);
+
+ memcpy(data, &val, sizeof(val));
+ ret = mshv_guest_mem_write(gpa, data, len, false);
+ if (ret < 0) {
+ error_report("Failed to write guest memory");
+ return -1;
+ }
+ } else {
+ uint8_t data[8] = { 0 };
+
+ ret = mshv_guest_mem_read(gpa, data, len, false, false);
+ if (ret < 0) {
+ error_report("Failed to read guest memory");
+ return -1;
+ }
+
+ uint64_t val;
+ memcpy(&val, data, sizeof(val));
+
+ val = le64_to_cpu(val);
+
+ if (sign_extend) {
+ uint64_t shift = 64 - (len * 8);
+ val = (((int64_t)val << shift) >> shift);
+ if (!iss.sf) {
+ val &= 0xffffffff;
+ }
+ }
+
+ env->xregs[reg_index] = val;
+ }
+
+ env->pc += (syndrome.il == 1) ? 4 : 2;
+
+ ret = mshv_store_regs(cpu);
+ if (ret < 0) {
+ error_report("failed to store registers");
+ return -1;
+ }
+
+ return 0;
+}
+
+static int handle_unmapped_mem(int vm_fd, CPUState *cpu,
+ const struct hyperv_message *msg,
+ MshvVmExit *exit_reason)
+{
+ struct hv_arm64_memory_intercept_message info = { 0 };
+ int ret;
+
+ ret = set_memory_info(msg, &info);
+ if (ret < 0) {
+ error_report("failed to convert message to memory info");
+ return -1;
+ }
+
+ ret = emulate_with_syndrome(cpu, &info);
+ if (ret < 0) {
+ error_report("Failed to emulate with syndrome");
+ return -1;
+ }
+
+ *exit_reason = MshvVmExitIgnore;
+
+ return 0;
+}
+
int mshv_run_vcpu(int vm_fd, CPUState *cpu, hv_message *msg, MshvVmExit *exit)
{
+ int ret;
+ int cpu_fd = mshv_vcpufd(cpu);
+
+ ret = ioctl(cpu_fd, MSHV_RUN_VP, msg);
+ if (ret < 0) {
+ *exit = MshvVmExitShutdown;
+ return ret;
+ }
+
+ switch (msg->header.message_type) {
+ case HVMSG_UNRECOVERABLE_EXCEPTION:
+ *exit = MshvVmExitShutdown;
+ break;
+ case HVMSG_UNMAPPED_GPA:
+ ret = handle_unmapped_mem(vm_fd, cpu, msg, exit);
+ if (ret < 0) {
+ error_report("failed to handle mmio");
+ return -1;
+ }
+ break;
+ default:
+ error_report("Unhandled message type: 0x%x", msg->header.message_type);
+ return -1;
+ }
+
return 0;
}
--
2.43.0
> On 11. Mar 2026, at 16:15, Anirudh Rayabharam (Microsoft) <anirudh@anirudhrb.com> wrote:
>
> Add the main vCPU run loop for MSHV using the MSHV_RUN_VP_IOCTL.
>
> Handle MMIO exits by emulating the instruction using the syndrome
> information from ESR_EL2.
>
> Signed-off-by: Anirudh Rayabharam (Microsoft) <anirudh@anirudhrb.com>
> ---
> include/hw/hyperv/hvgdk_mini.h | 44 +++++++++
> target/arm/mshv/mshv-all.c | 198 +++++++++++++++++++++++++++++++++++++++++
> 2 files changed, 242 insertions(+)
>
Hello,
GDK updates in separate patches from the rest please. Maybe even combine them together.
And more comments below...
> diff --git a/include/hw/hyperv/hvgdk_mini.h b/include/hw/hyperv/hvgdk_mini.h
> index d56be0d70f..84b3c6af5f 100644
> --- a/include/hw/hyperv/hvgdk_mini.h
> +++ b/include/hw/hyperv/hvgdk_mini.h
> @@ -750,6 +750,50 @@ struct hv_x64_memory_intercept_message {
> uint8_t instruction_bytes[16];
> };
>
> +union hv_arm64_vp_execution_state {
> + uint16_t as_uint16;
> + struct {
> + uint16_t cpl:2;
> + uint16_t debug_active:1;
> + uint16_t interruption_pending:1;
> + uint16_t vtl:4;
> + uint16_t virtualization_fault_active:1;
> + uint16_t reserved:7;
> + };
> +};
> +
> +struct hv_arm64_intercept_message_header {
> + uint32_t vp_index;
> + uint8_t instruction_length;
> + uint8_t intercept_access_type;
> + union hv_arm64_vp_execution_state execution_state;
> + uint64_t pc;
> + uint64_t cpsr;
> +};
> +
> +union hv_arm64_memory_access_info {
> + uint8_t as_uint8;
> + struct {
> + uint8_t gva_valid:1;
> + uint8_t gva_gpa_valid:1;
> + uint8_t hypercall_output_pending:1;
> + uint8_t reserved:5;
> + };
> +};
> +
> +struct hv_arm64_memory_intercept_message {
> + struct hv_arm64_intercept_message_header header;
> + uint32_t cache_type; /* enum hv_cache_type */
> + uint8_t instruction_byte_count;
> + union hv_arm64_memory_access_info memory_access_info;
> + uint16_t reserved1;
> + uint8_t instruction_bytes[4];
> + uint32_t reserved2;
> + uint64_t guest_virtual_address;
> + uint64_t guest_physical_address;
> + uint64_t syndrome;
> +};
> +
> union hv_message_flags {
> uint8_t asu8;
> struct {
> diff --git a/target/arm/mshv/mshv-all.c b/target/arm/mshv/mshv-all.c
> index 2b983845d3..b0649ba10b 100644
> --- a/target/arm/mshv/mshv-all.c
> +++ b/target/arm/mshv/mshv-all.c
> @@ -166,8 +166,206 @@ int mshv_arch_put_registers(const CPUState *cpu)
> return 0;
> }
>
> +static int set_memory_info(const struct hyperv_message *msg,
> + struct hv_arm64_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;
> +}
> +
> +typedef union {
> + uint64_t raw;
> + struct {
> + uint32_t iss:25;
> + uint32_t il:1;
> + uint32_t ec:6;
> + uint32_t iss2:5;
> + uint32_t _rsvd:27;
> + } QEMU_PACKED;
> +} EsrEl2;
> +
> +typedef union {
> + uint32_t raw;
> + struct {
> + uint32_t dfsc:6;
> + uint32_t wnr:1;
> + uint32_t s1ptw:1;
> + uint32_t cm:1;
> + uint32_t ea:1;
> + uint32_t fnv:1;
> + uint32_t set:2;
> + uint32_t vncr:1;
> + uint32_t ar:1;
> + uint32_t sf:1;
> + uint32_t srt:5;
> + uint32_t sse:1;
> + uint32_t sas:2;
> + uint32_t isv:1;
> + uint32_t _unused:7;
> + } QEMU_PACKED;
> +} IssDataAbort;
Think this is better suited for common code somewhere...
> +
> +typedef enum {
> + data_abort_lower = 36,
> + data_abort = 37,
> +} ExceptionClass;
> +
> +int mshv_store_regs(CPUState *cpu)
> +{
> + int ret;
> +
> + ret = set_standard_regs(cpu);
> + if (ret < 0) {
> + error_report("Failed to store standard registers");
> + return -1;
> + }
> +
> + /* TODO: should store special registers? the equivalent hvf code doesn't */
WHPX does handle this via state levels, but what HVF does via a separate function is also workable.
> + return 0;
> +}
> +
> +static int emulate_with_syndrome(CPUState *cpu,
> + struct hv_arm64_memory_intercept_message *info)
> +{
> + ARMCPU *arm_cpu = ARM_CPU(cpu);
> + CPUARMState *env = &arm_cpu->env;
> + int ret;
> + EsrEl2 syndrome = { 0 };
> + IssDataAbort iss = { 0 };
> + uint64_t gpa = info->guest_physical_address;
> + uint64_t len, reg_index;
> + bool sign_extend;
> +
> + syndrome.raw = info->syndrome;
> +
> + if (!(syndrome.ec == data_abort_lower || syndrome.ec == data_abort)) {
> + error_report("Unknown exception class 0x%x", syndrome.ec);
> + return -1;
> + }
> +
> + iss.raw = syndrome.iss;
> + if (!iss.isv) {
> + error_report("Invalid ESR EL2 ISV field");
> + return -1;
> + }
> +
> + len = 1ULL << iss.sas;
> + sign_extend = iss.sse;
> + reg_index = iss.srt;
> +
> + ret = mshv_load_regs(cpu);
> + if (ret < 0) {
> + error_report("Failed to load registers");
> + return -1;
> + }
> +
> + if (iss.wnr) {
> + uint8_t data[8];
> + uint64_t val = reg_index < 31 ? env->xregs[reg_index] : 0ULL;
> +
> + val = cpu_to_le64(val);
> +
> + memcpy(data, &val, sizeof(val));
> + ret = mshv_guest_mem_write(gpa, data, len, false);
> + if (ret < 0) {
> + error_report("Failed to write guest memory");
> + return -1;
> + }
> + } else {
> + uint8_t data[8] = { 0 };
> +
> + ret = mshv_guest_mem_read(gpa, data, len, false, false);
> + if (ret < 0) {
> + error_report("Failed to read guest memory");
> + return -1;
> + }
> +
> + uint64_t val;
> + memcpy(&val, data, sizeof(val));
> +
> + val = le64_to_cpu(val);
> +
> + if (sign_extend) {
> + uint64_t shift = 64 - (len * 8);
> + val = (((int64_t)val << shift) >> shift);
> + if (!iss.sf) {
> + val &= 0xffffffff;
> + }
> + }
> +
> + env->xregs[reg_index] = val;
> + }
> +
> + env->pc += (syndrome.il == 1) ? 4 : 2;
> +
> + ret = mshv_store_regs(cpu);
> + if (ret < 0) {
> + error_report("failed to store registers");
> + return -1;
> + }
> +
> + return 0;
> +}
I think this deserves to go to common code somewhere at this point :)
> +
> +static int handle_unmapped_mem(int vm_fd, CPUState *cpu,
> + const struct hyperv_message *msg,
> + MshvVmExit *exit_reason)
> +{
> + struct hv_arm64_memory_intercept_message info = { 0 };
> + int ret;
> +
> + ret = set_memory_info(msg, &info);
> + if (ret < 0) {
> + error_report("failed to convert message to memory info");
> + return -1;
> + }
> +
> + ret = emulate_with_syndrome(cpu, &info);
> + if (ret < 0) {
> + error_report("Failed to emulate with syndrome");
> + return -1;
> + }
> +
> + *exit_reason = MshvVmExitIgnore;
> +
> + return 0;
> +}
> +
> int mshv_run_vcpu(int vm_fd, CPUState *cpu, hv_message *msg, MshvVmExit *exit)
> {
> + int ret;
> + int cpu_fd = mshv_vcpufd(cpu);
> +
> + ret = ioctl(cpu_fd, MSHV_RUN_VP, msg);
> + if (ret < 0) {
> + *exit = MshvVmExitShutdown;
> + return ret;
> + }
> +
> + switch (msg->header.message_type) {
> + case HVMSG_UNRECOVERABLE_EXCEPTION:
> + *exit = MshvVmExitShutdown;
> + break;
> + case HVMSG_UNMAPPED_GPA:
At least as observed on WHPX you also need to handle GpaIntercept the same way
for EDK2 to be happy
>
>
> + ret = handle_unmapped_mem(vm_fd, cpu, msg, exit);
> + if (ret < 0) {
> + error_report("failed to handle mmio");
> + return -1;
> + }
> + break;
> + default:
> + error_report("Unhandled message type: 0x%x", msg->header.message_type);
> + return -1;
> + }
> +
> return 0;
> }
>
>
> --
> 2.43.0
>
>
On Wed, Mar 11, 2026 at 04:58:28PM +0100, Mohamed Mediouni wrote:
>
>
> > On 11. Mar 2026, at 16:15, Anirudh Rayabharam (Microsoft) <anirudh@anirudhrb.com> wrote:
> >
> > Add the main vCPU run loop for MSHV using the MSHV_RUN_VP_IOCTL.
> >
> > Handle MMIO exits by emulating the instruction using the syndrome
> > information from ESR_EL2.
> >
> > Signed-off-by: Anirudh Rayabharam (Microsoft) <anirudh@anirudhrb.com>
> > ---
> > include/hw/hyperv/hvgdk_mini.h | 44 +++++++++
> > target/arm/mshv/mshv-all.c | 198 +++++++++++++++++++++++++++++++++++++++++
> > 2 files changed, 242 insertions(+)
> >
> Hello,
>
> GDK updates in separate patches from the rest please. Maybe even combine them together.
The header updates are closely related to what this patch is doing.
>
> And more comments below...
>
> > diff --git a/include/hw/hyperv/hvgdk_mini.h b/include/hw/hyperv/hvgdk_mini.h
> > index d56be0d70f..84b3c6af5f 100644
> > --- a/include/hw/hyperv/hvgdk_mini.h
> > +++ b/include/hw/hyperv/hvgdk_mini.h
> > @@ -750,6 +750,50 @@ struct hv_x64_memory_intercept_message {
> > uint8_t instruction_bytes[16];
> > };
> >
> > +union hv_arm64_vp_execution_state {
> > + uint16_t as_uint16;
> > + struct {
> > + uint16_t cpl:2;
> > + uint16_t debug_active:1;
> > + uint16_t interruption_pending:1;
> > + uint16_t vtl:4;
> > + uint16_t virtualization_fault_active:1;
> > + uint16_t reserved:7;
> > + };
> > +};
> > +
> > +struct hv_arm64_intercept_message_header {
> > + uint32_t vp_index;
> > + uint8_t instruction_length;
> > + uint8_t intercept_access_type;
> > + union hv_arm64_vp_execution_state execution_state;
> > + uint64_t pc;
> > + uint64_t cpsr;
> > +};
> > +
> > +union hv_arm64_memory_access_info {
> > + uint8_t as_uint8;
> > + struct {
> > + uint8_t gva_valid:1;
> > + uint8_t gva_gpa_valid:1;
> > + uint8_t hypercall_output_pending:1;
> > + uint8_t reserved:5;
> > + };
> > +};
> > +
> > +struct hv_arm64_memory_intercept_message {
> > + struct hv_arm64_intercept_message_header header;
> > + uint32_t cache_type; /* enum hv_cache_type */
> > + uint8_t instruction_byte_count;
> > + union hv_arm64_memory_access_info memory_access_info;
> > + uint16_t reserved1;
> > + uint8_t instruction_bytes[4];
> > + uint32_t reserved2;
> > + uint64_t guest_virtual_address;
> > + uint64_t guest_physical_address;
> > + uint64_t syndrome;
> > +};
> > +
> > union hv_message_flags {
> > uint8_t asu8;
> > struct {
> > diff --git a/target/arm/mshv/mshv-all.c b/target/arm/mshv/mshv-all.c
> > index 2b983845d3..b0649ba10b 100644
> > --- a/target/arm/mshv/mshv-all.c
> > +++ b/target/arm/mshv/mshv-all.c
> > @@ -166,8 +166,206 @@ int mshv_arch_put_registers(const CPUState *cpu)
> > return 0;
> > }
> >
> > +static int set_memory_info(const struct hyperv_message *msg,
> > + struct hv_arm64_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;
> > +}
> > +
> > +typedef union {
> > + uint64_t raw;
> > + struct {
> > + uint32_t iss:25;
> > + uint32_t il:1;
> > + uint32_t ec:6;
> > + uint32_t iss2:5;
> > + uint32_t _rsvd:27;
> > + } QEMU_PACKED;
> > +} EsrEl2;
> > +
> > +typedef union {
> > + uint32_t raw;
> > + struct {
> > + uint32_t dfsc:6;
> > + uint32_t wnr:1;
> > + uint32_t s1ptw:1;
> > + uint32_t cm:1;
> > + uint32_t ea:1;
> > + uint32_t fnv:1;
> > + uint32_t set:2;
> > + uint32_t vncr:1;
> > + uint32_t ar:1;
> > + uint32_t sf:1;
> > + uint32_t srt:5;
> > + uint32_t sse:1;
> > + uint32_t sas:2;
> > + uint32_t isv:1;
> > + uint32_t _unused:7;
> > + } QEMU_PACKED;
> > +} IssDataAbort;
> Think this is better suited for common code somewhere...
> > +
> > +typedef enum {
> > + data_abort_lower = 36,
> > + data_abort = 37,
> > +} ExceptionClass;
> > +
> > +int mshv_store_regs(CPUState *cpu)
> > +{
> > + int ret;
> > +
> > + ret = set_standard_regs(cpu);
> > + if (ret < 0) {
> > + error_report("Failed to store standard registers");
> > + return -1;
> > + }
> > +
> > + /* TODO: should store special registers? the equivalent hvf code doesn't */
> WHPX does handle this via state levels, but what HVF does via a separate function is also workable.
Yeah, we'll add more registers in a later series. I think I'll get rid of
this TODO.
>
> > + return 0;
> > +}
> > +
> > +static int emulate_with_syndrome(CPUState *cpu,
> > + struct hv_arm64_memory_intercept_message *info)
> > +{
> > + ARMCPU *arm_cpu = ARM_CPU(cpu);
> > + CPUARMState *env = &arm_cpu->env;
> > + int ret;
> > + EsrEl2 syndrome = { 0 };
> > + IssDataAbort iss = { 0 };
> > + uint64_t gpa = info->guest_physical_address;
> > + uint64_t len, reg_index;
> > + bool sign_extend;
> > +
> > + syndrome.raw = info->syndrome;
> > +
> > + if (!(syndrome.ec == data_abort_lower || syndrome.ec == data_abort)) {
> > + error_report("Unknown exception class 0x%x", syndrome.ec);
> > + return -1;
> > + }
> > +
> > + iss.raw = syndrome.iss;
> > + if (!iss.isv) {
> > + error_report("Invalid ESR EL2 ISV field");
> > + return -1;
> > + }
> > +
> > + len = 1ULL << iss.sas;
> > + sign_extend = iss.sse;
> > + reg_index = iss.srt;
> > +
> > + ret = mshv_load_regs(cpu);
> > + if (ret < 0) {
> > + error_report("Failed to load registers");
> > + return -1;
> > + }
> > +
> > + if (iss.wnr) {
> > + uint8_t data[8];
> > + uint64_t val = reg_index < 31 ? env->xregs[reg_index] : 0ULL;
> > +
> > + val = cpu_to_le64(val);
> > +
> > + memcpy(data, &val, sizeof(val));
> > + ret = mshv_guest_mem_write(gpa, data, len, false);
> > + if (ret < 0) {
> > + error_report("Failed to write guest memory");
> > + return -1;
> > + }
> > + } else {
> > + uint8_t data[8] = { 0 };
> > +
> > + ret = mshv_guest_mem_read(gpa, data, len, false, false);
> > + if (ret < 0) {
> > + error_report("Failed to read guest memory");
> > + return -1;
> > + }
> > +
> > + uint64_t val;
> > + memcpy(&val, data, sizeof(val));
> > +
> > + val = le64_to_cpu(val);
> > +
> > + if (sign_extend) {
> > + uint64_t shift = 64 - (len * 8);
> > + val = (((int64_t)val << shift) >> shift);
> > + if (!iss.sf) {
> > + val &= 0xffffffff;
> > + }
> > + }
> > +
> > + env->xregs[reg_index] = val;
> > + }
> > +
> > + env->pc += (syndrome.il == 1) ? 4 : 2;
> > +
> > + ret = mshv_store_regs(cpu);
> > + if (ret < 0) {
> > + error_report("failed to store registers");
> > + return -1;
> > + }
> > +
> > + return 0;
> > +}
>
> I think this deserves to go to common code somewhere at this point :)
Do you have any ideas as to where this code can go?
>
> > +
> > +static int handle_unmapped_mem(int vm_fd, CPUState *cpu,
> > + const struct hyperv_message *msg,
> > + MshvVmExit *exit_reason)
> > +{
> > + struct hv_arm64_memory_intercept_message info = { 0 };
> > + int ret;
> > +
> > + ret = set_memory_info(msg, &info);
> > + if (ret < 0) {
> > + error_report("failed to convert message to memory info");
> > + return -1;
> > + }
> > +
> > + ret = emulate_with_syndrome(cpu, &info);
> > + if (ret < 0) {
> > + error_report("Failed to emulate with syndrome");
> > + return -1;
> > + }
> > +
> > + *exit_reason = MshvVmExitIgnore;
> > +
> > + return 0;
> > +}
> > +
> > int mshv_run_vcpu(int vm_fd, CPUState *cpu, hv_message *msg, MshvVmExit *exit)
> > {
> > + int ret;
> > + int cpu_fd = mshv_vcpufd(cpu);
> > +
> > + ret = ioctl(cpu_fd, MSHV_RUN_VP, msg);
> > + if (ret < 0) {
> > + *exit = MshvVmExitShutdown;
> > + return ret;
> > + }
> > +
> > + switch (msg->header.message_type) {
> > + case HVMSG_UNRECOVERABLE_EXCEPTION:
> > + *exit = MshvVmExitShutdown;
> > + break;
> > + case HVMSG_UNMAPPED_GPA:
> At least as observed on WHPX you also need to handle GpaIntercept the same way
> for EDK2 to be happy
Interesting, because Cloud Hypervisor doesn't handle GPA_INTERCEPT at
all for arm64 and still works fine with EDK2.
Anyway, I tried adding it but EDK2 still isn't happy. There seems to be
some other issue potentially related to the mshv driver in the kernel.
Thanks,
Anirudh.
> >
> >
> > + ret = handle_unmapped_mem(vm_fd, cpu, msg, exit);
> > + if (ret < 0) {
> > + error_report("failed to handle mmio");
> > + return -1;
> > + }
> > + break;
> > + default:
> > + error_report("Unhandled message type: 0x%x", msg->header.message_type);
> > + return -1;
> > + }
> > +
> > return 0;
> > }
> >
> >
> > --
> > 2.43.0
> >
> >
>
>
© 2016 - 2026 Red Hat, Inc.