[PATCH 11/14] target/arm/mshv: add vCPU run loop

Anirudh Rayabharam (Microsoft) posted 14 patches 3 weeks, 6 days ago
Maintainers: Magnus Kulke <magnuskulke@linux.microsoft.com>, Wei Liu <wei.liu@kernel.org>, Peter Maydell <peter.maydell@linaro.org>, Anirudh Rayabharam <anirudh@anirudhrb.com>, Aastha Rawat <aastharawat@linux.microsoft.com>, Paolo Bonzini <pbonzini@redhat.com>, "Marc-André Lureau" <marcandre.lureau@redhat.com>, "Daniel P. Berrangé" <berrange@redhat.com>, "Philippe Mathieu-Daudé" <philmd@linaro.org>
There is a newer version of this series
[PATCH 11/14] target/arm/mshv: add vCPU run loop
Posted by Anirudh Rayabharam (Microsoft) 3 weeks, 6 days ago
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
Re: [PATCH 11/14] target/arm/mshv: add vCPU run loop
Posted by Mohamed Mediouni 3 weeks, 6 days ago

> 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
> 
> 
Re: [PATCH 11/14] target/arm/mshv: add vCPU run loop
Posted by Anirudh Rayabharam 3 weeks ago
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
> > 
> > 
> 
>