diff mbox series

[3/6] Add the aehd-apic device type.

Message ID 20230303022618.4098825-3-hshan@google.com
State New
Headers show
Series Adding the Android Emulator hypervisor driver accelerator | expand

Commit Message

Haitao Shan March 3, 2023, 2:26 a.m. UTC
The aehd-apic device type represents the AEHD in kernel APIC.
The irqchips should be always in kernel when AEHD is used.

Signed-off-by: Haitao Shan <hshan@google.com>
---
 MAINTAINERS                  |   2 +
 hw/i386/aehd/apic.c          | 204 +++++++++++++++++++++++
 hw/i386/aehd/meson.build     |   4 +
 hw/i386/meson.build          |   1 +
 include/hw/core/cpu.h        |   7 +
 include/sysemu/aehd.h        |  52 ++++++
 target/i386/aehd/aehd-all.c  | 315 +++++++++++++++++++++++++++++++++++
 target/i386/aehd/aehd.c      |  88 ++++++++++
 target/i386/aehd/aehd_int.h  |  50 ++++++
 target/i386/aehd/meson.build |   4 +
 target/i386/cpu-sysemu.c     |   3 +
 target/i386/meson.build      |   1 +
 12 files changed, 731 insertions(+)
 create mode 100644 hw/i386/aehd/apic.c
 create mode 100644 hw/i386/aehd/meson.build
 create mode 100644 target/i386/aehd/aehd-all.c
 create mode 100644 target/i386/aehd/aehd.c
 create mode 100644 target/i386/aehd/aehd_int.h
 create mode 100644 target/i386/aehd/meson.build

Comments

Philippe Mathieu-Daudé March 3, 2023, 9:59 a.m. UTC | #1
Hi Haitao,

On 3/3/23 03:26, Haitao Shan wrote:
> The aehd-apic device type represents the AEHD in kernel APIC.
> The irqchips should be always in kernel when AEHD is used.
> 
> Signed-off-by: Haitao Shan <hshan@google.com>
> ---
>   MAINTAINERS                  |   2 +
>   hw/i386/aehd/apic.c          | 204 +++++++++++++++++++++++
>   hw/i386/aehd/meson.build     |   4 +
>   hw/i386/meson.build          |   1 +
>   include/hw/core/cpu.h        |   7 +
>   include/sysemu/aehd.h        |  52 ++++++
>   target/i386/aehd/aehd-all.c  | 315 +++++++++++++++++++++++++++++++++++
>   target/i386/aehd/aehd.c      |  88 ++++++++++
>   target/i386/aehd/aehd_int.h  |  50 ++++++
>   target/i386/aehd/meson.build |   4 +
>   target/i386/cpu-sysemu.c     |   3 +
>   target/i386/meson.build      |   1 +
>   12 files changed, 731 insertions(+)
>   create mode 100644 hw/i386/aehd/apic.c
>   create mode 100644 hw/i386/aehd/meson.build
>   create mode 100644 target/i386/aehd/aehd-all.c
>   create mode 100644 target/i386/aehd/aehd.c
>   create mode 100644 target/i386/aehd/aehd_int.h
>   create mode 100644 target/i386/aehd/meson.build


> diff --git a/include/hw/core/cpu.h b/include/hw/core/cpu.h
> index fb5d9667ca..5ed0600504 100644
> --- a/include/hw/core/cpu.h
> +++ b/include/hw/core/cpu.h
> @@ -400,6 +400,13 @@ struct CPUState {
>       uint32_t kvm_fetch_index;
>       uint64_t dirty_pages;
>   
> +    /* Only used in AEHD */
> +#ifdef _WIN32
> +    HANDLE aehd_fd;
> +    struct AEHDState *aehd_state;
> +    struct aehd_run *aehd_run;
> +#endif

We are difficultly trying to make this structure generic
since 2 years now. Since AEHD won't make it to the 8.0 release,
I'd rather take the time to get this part well done.

IIRC we ought to use a AccelCPUState pointer here, allocated by
AccelCPUClass::cpu_instance_init()

>       /* Use by accel-block: CPU is executing an ioctl() */
>       QemuLockCnt in_ioctl_lock;
>   
> diff --git a/include/sysemu/aehd.h b/include/sysemu/aehd.h
> index 7ba4234f60..87fa2f8362 100644
> --- a/include/sysemu/aehd.h
> +++ b/include/sysemu/aehd.h
> @@ -14,6 +14,12 @@
>   #ifndef QEMU_AEHD_H
>   #define QEMU_AEHD_H
>   
> +#include "qemu/queue.h"
> +#include "qemu/accel.h"
> +#include "hw/core/cpu.h"
> +#include "exec/memattrs.h"
> +#include "hw/irq.h"
> +
>   #ifdef NEED_CPU_H
>   # ifdef CONFIG_AEHD
>   #  define CONFIG_AEHD_IS_POSSIBLE
> @@ -23,3 +29,49 @@
>   #endif
>   
>   #define aehd_enabled()           (0)
> +
> +struct aehd_run;
> +struct aehd_lapic_state;
> +struct aehd_irq_routing_entry;
> +
> +struct AEHDState;
> +
> +#define TYPE_AEHD_ACCEL ACCEL_CLASS_NAME("aehd")
> +typedef struct AEHDState AEHDState;
> +DECLARE_INSTANCE_CHECKER(AEHDState, AEHD_STATE,
> +                         TYPE_AEHD_ACCEL)
> +
> +extern AEHDState *aehd_state;
> +
> +#ifdef NEED_CPU_H
> +#include "cpu.h"
> +
> +/* internal API */
> +
> +int aehd_ioctl(AEHDState *s, int type, void *input, size_t input_size,
> +               void *output, size_t output_size);
> +int aehd_vm_ioctl(AEHDState *s, int type, void *input, size_t input_size,
> +                  void *output, size_t output_size);
> +int aehd_vcpu_ioctl(CPUState *cpu, int type, void *input, size_t input_size,
> +                    void *output, size_t output_size);
> +
> +/* Arch specific hooks */
> +
> +/* Notify arch about newly added MSI routes */
> +int aehd_arch_add_msi_route_post(struct aehd_irq_routing_entry *route,
> +                                 int vector, PCIDevice *dev);
> +/* Notify arch about released MSI routes */
> +int aehd_arch_release_virq_post(int virq);
> +
> +int aehd_set_irq(AEHDState *s, int irq, int level);
> +int aehd_irqchip_send_msi(AEHDState *s, MSIMessage msg);
> +
> +void aehd_put_apic_state(DeviceState *d, struct aehd_lapic_state *kapic);
> +void aehd_get_apic_state(DeviceState *d, struct aehd_lapic_state *kapic);
> +
> +#endif /* NEED_CPU_H */
> +
> +void aehd_irqchip_commit_routes(AEHDState *s);
> +void aehd_irqchip_release_virq(AEHDState *s, int virq);
> +
> +#endif
Haitao Shan March 3, 2023, 6:36 p.m. UTC | #2
On Fri, Mar 3, 2023 at 1:59 AM Philippe Mathieu-Daudé <philmd@linaro.org> wrote:
>
> Hi Haitao,
>
> On 3/3/23 03:26, Haitao Shan wrote:
> > The aehd-apic device type represents the AEHD in kernel APIC.
> > The irqchips should be always in kernel when AEHD is used.
> >
> > Signed-off-by: Haitao Shan <hshan@google.com>
> > ---
> >   MAINTAINERS                  |   2 +
> >   hw/i386/aehd/apic.c          | 204 +++++++++++++++++++++++
> >   hw/i386/aehd/meson.build     |   4 +
> >   hw/i386/meson.build          |   1 +
> >   include/hw/core/cpu.h        |   7 +
> >   include/sysemu/aehd.h        |  52 ++++++
> >   target/i386/aehd/aehd-all.c  | 315 +++++++++++++++++++++++++++++++++++
> >   target/i386/aehd/aehd.c      |  88 ++++++++++
> >   target/i386/aehd/aehd_int.h  |  50 ++++++
> >   target/i386/aehd/meson.build |   4 +
> >   target/i386/cpu-sysemu.c     |   3 +
> >   target/i386/meson.build      |   1 +
> >   12 files changed, 731 insertions(+)
> >   create mode 100644 hw/i386/aehd/apic.c
> >   create mode 100644 hw/i386/aehd/meson.build
> >   create mode 100644 target/i386/aehd/aehd-all.c
> >   create mode 100644 target/i386/aehd/aehd.c
> >   create mode 100644 target/i386/aehd/aehd_int.h
> >   create mode 100644 target/i386/aehd/meson.build
>
>
> > diff --git a/include/hw/core/cpu.h b/include/hw/core/cpu.h
> > index fb5d9667ca..5ed0600504 100644
> > --- a/include/hw/core/cpu.h
> > +++ b/include/hw/core/cpu.h
> > @@ -400,6 +400,13 @@ struct CPUState {
> >       uint32_t kvm_fetch_index;
> >       uint64_t dirty_pages;
> >
> > +    /* Only used in AEHD */
> > +#ifdef _WIN32
> > +    HANDLE aehd_fd;
> > +    struct AEHDState *aehd_state;
> > +    struct aehd_run *aehd_run;
> > +#endif
>
> We are difficultly trying to make this structure generic
> since 2 years now. Since AEHD won't make it to the 8.0 release,
> I'd rather take the time to get this part well done.
Can I know when 8.0 will be released? I don't aim to push my patches
as soon as possible. But understanding the timeline can be helpful.

>
> IIRC we ought to use a AccelCPUState pointer here, allocated by
> AccelCPUClass::cpu_instance_init()
I would agree. Will this be addressed when 8.0 is released?

>
> >       /* Use by accel-block: CPU is executing an ioctl() */
> >       QemuLockCnt in_ioctl_lock;
> >
> > diff --git a/include/sysemu/aehd.h b/include/sysemu/aehd.h
> > index 7ba4234f60..87fa2f8362 100644
> > --- a/include/sysemu/aehd.h
> > +++ b/include/sysemu/aehd.h
> > @@ -14,6 +14,12 @@
> >   #ifndef QEMU_AEHD_H
> >   #define QEMU_AEHD_H
> >
> > +#include "qemu/queue.h"
> > +#include "qemu/accel.h"
> > +#include "hw/core/cpu.h"
> > +#include "exec/memattrs.h"
> > +#include "hw/irq.h"
> > +
> >   #ifdef NEED_CPU_H
> >   # ifdef CONFIG_AEHD
> >   #  define CONFIG_AEHD_IS_POSSIBLE
> > @@ -23,3 +29,49 @@
> >   #endif
> >
> >   #define aehd_enabled()           (0)
> > +
> > +struct aehd_run;
> > +struct aehd_lapic_state;
> > +struct aehd_irq_routing_entry;
> > +
> > +struct AEHDState;
> > +
> > +#define TYPE_AEHD_ACCEL ACCEL_CLASS_NAME("aehd")
> > +typedef struct AEHDState AEHDState;
> > +DECLARE_INSTANCE_CHECKER(AEHDState, AEHD_STATE,
> > +                         TYPE_AEHD_ACCEL)
> > +
> > +extern AEHDState *aehd_state;
> > +
> > +#ifdef NEED_CPU_H
> > +#include "cpu.h"
> > +
> > +/* internal API */
> > +
> > +int aehd_ioctl(AEHDState *s, int type, void *input, size_t input_size,
> > +               void *output, size_t output_size);
> > +int aehd_vm_ioctl(AEHDState *s, int type, void *input, size_t input_size,
> > +                  void *output, size_t output_size);
> > +int aehd_vcpu_ioctl(CPUState *cpu, int type, void *input, size_t input_size,
> > +                    void *output, size_t output_size);
> > +
> > +/* Arch specific hooks */
> > +
> > +/* Notify arch about newly added MSI routes */
> > +int aehd_arch_add_msi_route_post(struct aehd_irq_routing_entry *route,
> > +                                 int vector, PCIDevice *dev);
> > +/* Notify arch about released MSI routes */
> > +int aehd_arch_release_virq_post(int virq);
> > +
> > +int aehd_set_irq(AEHDState *s, int irq, int level);
> > +int aehd_irqchip_send_msi(AEHDState *s, MSIMessage msg);
> > +
> > +void aehd_put_apic_state(DeviceState *d, struct aehd_lapic_state *kapic);
> > +void aehd_get_apic_state(DeviceState *d, struct aehd_lapic_state *kapic);
> > +
> > +#endif /* NEED_CPU_H */
> > +
> > +void aehd_irqchip_commit_routes(AEHDState *s);
> > +void aehd_irqchip_release_virq(AEHDState *s, int virq);
> > +
> > +#endif
>
diff mbox series

Patch

diff --git a/MAINTAINERS b/MAINTAINERS
index 54796da3b4..3db165dd9a 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -535,6 +535,8 @@  W: https://github.com/google/android-emulator-hypervisor-driver
 S: Maintained
 F: include/sysemu/aehd-interface.h
 F: include/sysemu/aehd.h
+F: target/i386/aehd/
+F: hw/i386/aehd/
 
 Hosts
 -----
diff --git a/hw/i386/aehd/apic.c b/hw/i386/aehd/apic.c
new file mode 100644
index 0000000000..33a050ce11
--- /dev/null
+++ b/hw/i386/aehd/apic.c
@@ -0,0 +1,204 @@ 
+/*
+ * AEHD in-kernel APIC support
+ *
+ * Copyright (c) 2011 Siemens AG
+ *
+ * Authors:
+ *  Jan Kiszka          <jan.kiszka@siemens.com>
+ *
+ * This work is licensed under the terms of the GNU GPL version 2.
+ * See the COPYING file in the top-level directory.
+ */
+#include "qemu/osdep.h"
+#include "cpu.h"
+#include "hw/i386/apic_internal.h"
+#include "hw/pci/msi.h"
+#include "sysemu/hw_accel.h"
+#include "sysemu/aehd.h"
+#include "sysemu/aehd-interface.h"
+
+static inline void aehd_apic_set_reg(struct aehd_lapic_state *aapic,
+                                     int reg_id, uint32_t val)
+{
+    *((uint32_t *)(aapic->regs + (reg_id << 4))) = val;
+}
+
+static inline uint32_t aehd_apic_get_reg(struct aehd_lapic_state *aapic,
+                                         int reg_id)
+{
+    return *((uint32_t *)(aapic->regs + (reg_id << 4)));
+}
+
+void aehd_put_apic_state(DeviceState *dev, struct aehd_lapic_state *aapic)
+{
+    APICCommonState *s = APIC_COMMON(dev);
+    int i;
+
+    memset(aapic, 0, sizeof(*aapic));
+    aehd_apic_set_reg(aapic, 0x2, s->id << 24);
+    aehd_apic_set_reg(aapic, 0x8, s->tpr);
+    aehd_apic_set_reg(aapic, 0xd, s->log_dest << 24);
+    aehd_apic_set_reg(aapic, 0xe, s->dest_mode << 28 | 0x0fffffff);
+    aehd_apic_set_reg(aapic, 0xf, s->spurious_vec);
+    for (i = 0; i < 8; i++) {
+        aehd_apic_set_reg(aapic, 0x10 + i, s->isr[i]);
+        aehd_apic_set_reg(aapic, 0x18 + i, s->tmr[i]);
+        aehd_apic_set_reg(aapic, 0x20 + i, s->irr[i]);
+    }
+    aehd_apic_set_reg(aapic, 0x28, s->esr);
+    aehd_apic_set_reg(aapic, 0x30, s->icr[0]);
+    aehd_apic_set_reg(aapic, 0x31, s->icr[1]);
+    for (i = 0; i < APIC_LVT_NB; i++) {
+        aehd_apic_set_reg(aapic, 0x32 + i, s->lvt[i]);
+    }
+    aehd_apic_set_reg(aapic, 0x38, s->initial_count);
+    aehd_apic_set_reg(aapic, 0x3e, s->divide_conf);
+}
+
+void aehd_get_apic_state(DeviceState *dev, struct aehd_lapic_state *aapic)
+{
+    APICCommonState *s = APIC_COMMON(dev);
+    int i, v;
+
+    s->id = aehd_apic_get_reg(aapic, 0x2) >> 24;
+    s->tpr = aehd_apic_get_reg(aapic, 0x8);
+    s->arb_id = aehd_apic_get_reg(aapic, 0x9);
+    s->log_dest = aehd_apic_get_reg(aapic, 0xd) >> 24;
+    s->dest_mode = aehd_apic_get_reg(aapic, 0xe) >> 28;
+    s->spurious_vec = aehd_apic_get_reg(aapic, 0xf);
+    for (i = 0; i < 8; i++) {
+        s->isr[i] = aehd_apic_get_reg(aapic, 0x10 + i);
+        s->tmr[i] = aehd_apic_get_reg(aapic, 0x18 + i);
+        s->irr[i] = aehd_apic_get_reg(aapic, 0x20 + i);
+    }
+    s->esr = aehd_apic_get_reg(aapic, 0x28);
+    s->icr[0] = aehd_apic_get_reg(aapic, 0x30);
+    s->icr[1] = aehd_apic_get_reg(aapic, 0x31);
+    for (i = 0; i < APIC_LVT_NB; i++) {
+        s->lvt[i] = aehd_apic_get_reg(aapic, 0x32 + i);
+    }
+    s->initial_count = aehd_apic_get_reg(aapic, 0x38);
+    s->divide_conf = aehd_apic_get_reg(aapic, 0x3e);
+
+    v = (s->divide_conf & 3) | ((s->divide_conf >> 1) & 4);
+    s->count_shift = (v + 1) & 7;
+
+    s->initial_count_load_time = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL);
+    apic_next_timer(s, s->initial_count_load_time);
+}
+
+static void aehd_apic_set_base(APICCommonState *s, uint64_t val)
+{
+    s->apicbase = val;
+}
+
+static void aehd_apic_set_tpr(APICCommonState *s, uint8_t val)
+{
+    s->tpr = (val & 0x0f) << 4;
+}
+
+static uint8_t aehd_apic_get_tpr(APICCommonState *s)
+{
+    return s->tpr >> 4;
+}
+
+static void aehd_apic_vapic_base_update(APICCommonState *s)
+{
+    /* Not Implemented. This function is only for kvmvapic */
+}
+
+static void do_inject_external_nmi(CPUState *cpu, run_on_cpu_data data)
+{
+    APICCommonState *s = data.host_ptr;
+    uint32_t lvt;
+    int ret;
+
+    cpu_synchronize_state(cpu);
+
+    lvt = s->lvt[APIC_LVT_LINT1];
+    if (!(lvt & APIC_LVT_MASKED) && ((lvt >> 8) & 7) == APIC_DM_NMI) {
+        ret = aehd_vcpu_ioctl(cpu, AEHD_NMI, NULL, 0, NULL, 0);
+        if (ret < 0) {
+            fprintf(stderr, "AEHD: injection failed, NMI lost (%s)\n",
+                    strerror(-ret));
+        }
+    }
+}
+
+static void aehd_apic_external_nmi(APICCommonState *s)
+{
+    run_on_cpu(CPU(s->cpu), do_inject_external_nmi, RUN_ON_CPU_HOST_PTR(s));
+}
+
+static uint64_t aehd_apic_mem_read(void *opaque, hwaddr addr,
+                                   unsigned size)
+{
+    return ~(uint64_t)0;
+}
+
+static void aehd_apic_mem_write(void *opaque, hwaddr addr,
+                                uint64_t data, unsigned size)
+{
+    MSIMessage msg = { .address = addr, .data = data };
+    int ret;
+
+    ret = aehd_irqchip_send_msi(aehd_state, msg);
+    if (ret < 0) {
+        fprintf(stderr, "AEHD: injection failed, MSI lost (%s)\n",
+                strerror(-ret));
+    }
+}
+
+static const MemoryRegionOps aehd_apic_io_ops = {
+    .read = aehd_apic_mem_read,
+    .write = aehd_apic_mem_write,
+    .endianness = DEVICE_NATIVE_ENDIAN,
+};
+
+static void aehd_apic_reset(APICCommonState *s)
+{
+    /* Not used by AEHD, which uses the CPU mp_state instead.  */
+    s->wait_for_sipi = 0;
+}
+
+static void aehd_apic_realize(DeviceState *dev, Error **errp)
+{
+    APICCommonState *s = APIC_COMMON(dev);
+
+    memory_region_init_io(&s->io_memory, OBJECT(s), &aehd_apic_io_ops, s,
+                          "aehd-apic-msi", APIC_SPACE_SIZE);
+
+    msi_nonbroken = true;
+}
+
+static void aehd_apic_unrealize(DeviceState *dev)
+{
+}
+
+static void aehd_apic_class_init(ObjectClass *klass, void *data)
+{
+    APICCommonClass *k = APIC_COMMON_CLASS(klass);
+
+    k->realize = aehd_apic_realize;
+    k->unrealize = aehd_apic_unrealize;
+    k->reset = aehd_apic_reset;
+    k->set_base = aehd_apic_set_base;
+    k->set_tpr = aehd_apic_set_tpr;
+    k->get_tpr = aehd_apic_get_tpr;
+    k->vapic_base_update = aehd_apic_vapic_base_update;
+    k->external_nmi = aehd_apic_external_nmi;
+}
+
+static const TypeInfo aehd_apic_info = {
+    .name = "aehd-apic",
+    .parent = TYPE_APIC_COMMON,
+    .instance_size = sizeof(APICCommonState),
+    .class_init = aehd_apic_class_init,
+};
+
+static void aehd_apic_register_types(void)
+{
+    type_register_static(&aehd_apic_info);
+}
+
+type_init(aehd_apic_register_types)
diff --git a/hw/i386/aehd/meson.build b/hw/i386/aehd/meson.build
new file mode 100644
index 0000000000..1749860954
--- /dev/null
+++ b/hw/i386/aehd/meson.build
@@ -0,0 +1,4 @@ 
+i386_aehd_ss = ss.source_set()
+i386_aehd_ss.add(when: 'CONFIG_APIC', if_true: files('apic.c'))
+
+i386_ss.add_all(when: 'CONFIG_AEHD', if_true: i386_aehd_ss)
diff --git a/hw/i386/meson.build b/hw/i386/meson.build
index 213e2e82b3..eaf61b05fc 100644
--- a/hw/i386/meson.build
+++ b/hw/i386/meson.build
@@ -30,6 +30,7 @@  i386_ss.add(when: 'CONFIG_X86_FW_OVMF', if_true: files('pc_sysfw_ovmf.c'),
                                         if_false: files('pc_sysfw_ovmf-stubs.c'))
 
 subdir('kvm')
+subdir('aehd')
 subdir('xen')
 
 i386_ss.add_all(xenpv_ss)
diff --git a/include/hw/core/cpu.h b/include/hw/core/cpu.h
index fb5d9667ca..5ed0600504 100644
--- a/include/hw/core/cpu.h
+++ b/include/hw/core/cpu.h
@@ -400,6 +400,13 @@  struct CPUState {
     uint32_t kvm_fetch_index;
     uint64_t dirty_pages;
 
+    /* Only used in AEHD */
+#ifdef _WIN32
+    HANDLE aehd_fd;
+    struct AEHDState *aehd_state;
+    struct aehd_run *aehd_run;
+#endif
+
     /* Use by accel-block: CPU is executing an ioctl() */
     QemuLockCnt in_ioctl_lock;
 
diff --git a/include/sysemu/aehd.h b/include/sysemu/aehd.h
index 7ba4234f60..87fa2f8362 100644
--- a/include/sysemu/aehd.h
+++ b/include/sysemu/aehd.h
@@ -14,6 +14,12 @@ 
 #ifndef QEMU_AEHD_H
 #define QEMU_AEHD_H
 
+#include "qemu/queue.h"
+#include "qemu/accel.h"
+#include "hw/core/cpu.h"
+#include "exec/memattrs.h"
+#include "hw/irq.h"
+
 #ifdef NEED_CPU_H
 # ifdef CONFIG_AEHD
 #  define CONFIG_AEHD_IS_POSSIBLE
@@ -23,3 +29,49 @@ 
 #endif
 
 #define aehd_enabled()           (0)
+
+struct aehd_run;
+struct aehd_lapic_state;
+struct aehd_irq_routing_entry;
+
+struct AEHDState;
+
+#define TYPE_AEHD_ACCEL ACCEL_CLASS_NAME("aehd")
+typedef struct AEHDState AEHDState;
+DECLARE_INSTANCE_CHECKER(AEHDState, AEHD_STATE,
+                         TYPE_AEHD_ACCEL)
+
+extern AEHDState *aehd_state;
+
+#ifdef NEED_CPU_H
+#include "cpu.h"
+
+/* internal API */
+
+int aehd_ioctl(AEHDState *s, int type, void *input, size_t input_size,
+               void *output, size_t output_size);
+int aehd_vm_ioctl(AEHDState *s, int type, void *input, size_t input_size,
+                  void *output, size_t output_size);
+int aehd_vcpu_ioctl(CPUState *cpu, int type, void *input, size_t input_size,
+                    void *output, size_t output_size);
+
+/* Arch specific hooks */
+
+/* Notify arch about newly added MSI routes */
+int aehd_arch_add_msi_route_post(struct aehd_irq_routing_entry *route,
+                                 int vector, PCIDevice *dev);
+/* Notify arch about released MSI routes */
+int aehd_arch_release_virq_post(int virq);
+
+int aehd_set_irq(AEHDState *s, int irq, int level);
+int aehd_irqchip_send_msi(AEHDState *s, MSIMessage msg);
+
+void aehd_put_apic_state(DeviceState *d, struct aehd_lapic_state *kapic);
+void aehd_get_apic_state(DeviceState *d, struct aehd_lapic_state *kapic);
+
+#endif /* NEED_CPU_H */
+
+void aehd_irqchip_commit_routes(AEHDState *s);
+void aehd_irqchip_release_virq(AEHDState *s, int virq);
+
+#endif
diff --git a/target/i386/aehd/aehd-all.c b/target/i386/aehd/aehd-all.c
new file mode 100644
index 0000000000..f2eb80ecde
--- /dev/null
+++ b/target/i386/aehd/aehd-all.c
@@ -0,0 +1,315 @@ 
+/*
+ * QEMU AEHD support
+ *
+ * Copyright IBM, Corp. 2008
+ *           Red Hat, Inc. 2008
+ *
+ * Authors:
+ *  Anthony Liguori   <aliguori@us.ibm.com>
+ *  Glauber Costa     <gcosta@redhat.com>
+ *
+ * This work is licensed under the terms of the GNU GPL, version 2 or later.
+ * See the COPYING file in the top-level directory.
+ *
+ */
+
+#include "qemu/osdep.h"
+
+#include "qemu/atomic.h"
+#include "qemu/option.h"
+#include "qemu/config-file.h"
+#include "qemu/error-report.h"
+#include "qapi/error.h"
+#include "hw/hw.h"
+#include "hw/pci/msi.h"
+#include "hw/pci/msix.h"
+#include "exec/gdbstub.h"
+#include "sysemu/runstate.h"
+#include "sysemu/cpus.h"
+#include "qemu/bswap.h"
+#include "exec/memory.h"
+#include "exec/ram_addr.h"
+#include "exec/address-spaces.h"
+#include "qemu/event_notifier.h"
+#include "qemu/main-loop.h"
+#include "trace.h"
+#include "hw/irq.h"
+#include "qapi/visitor.h"
+#include "qapi/qapi-types-common.h"
+#include "qapi/qapi-visit-common.h"
+#include "sysemu/hw_accel.h"
+#include "sysemu/aehd-interface.h"
+#include "aehd_int.h"
+
+#include "hw/boards.h"
+
+#ifdef DEBUG_AEHD
+#define DPRINTF(fmt, ...) \
+    do { fprintf(stderr, fmt, ## __VA_ARGS__); } while (0)
+#else
+#define DPRINTF(fmt, ...) \
+    do { } while (0)
+#endif
+
+AEHDState *aehd_state;
+
+int aehd_set_irq(AEHDState *s, int irq, int level)
+{
+    struct aehd_irq_level event;
+    int ret;
+
+    event.level = level;
+    event.irq = irq;
+    ret = aehd_vm_ioctl(s, AEHD_IRQ_LINE_STATUS, &event, sizeof(event),
+                        &event, sizeof(event));
+
+    if (ret < 0) {
+        perror("aehd_set_irq");
+        abort();
+    }
+
+    return event.status;
+}
+
+typedef struct AEHDMSIRoute {
+    struct aehd_irq_routing_entry kroute;
+    QTAILQ_ENTRY(AEHDMSIRoute) entry;
+} AEHDMSIRoute;
+
+static void set_gsi(AEHDState *s, unsigned int gsi)
+{
+    set_bit(gsi, s->used_gsi_bitmap);
+}
+
+static void clear_gsi(AEHDState *s, unsigned int gsi)
+{
+    clear_bit(gsi, s->used_gsi_bitmap);
+}
+
+void aehd_irqchip_commit_routes(AEHDState *s)
+{
+    int ret;
+    size_t irq_routing_size;
+
+    s->irq_routes->flags = 0;
+    irq_routing_size = sizeof(struct aehd_irq_routing) +
+                       s->irq_routes->nr *
+                       sizeof(struct aehd_irq_routing_entry);
+    ret = aehd_vm_ioctl(s, AEHD_SET_GSI_ROUTING, s->irq_routes,
+                        irq_routing_size, NULL, 0);
+    assert(ret == 0);
+}
+
+static void aehd_add_routing_entry(AEHDState *s,
+                                   struct aehd_irq_routing_entry *entry)
+{
+    struct aehd_irq_routing_entry *new;
+    int n, size;
+
+    if (s->irq_routes->nr == s->nr_allocated_irq_routes) {
+        n = s->nr_allocated_irq_routes * 2;
+        if (n < 64) {
+            n = 64;
+        }
+        size = sizeof(struct aehd_irq_routing);
+        size += n * sizeof(*new);
+        s->irq_routes = g_realloc(s->irq_routes, size);
+        s->nr_allocated_irq_routes = n;
+    }
+    n = s->irq_routes->nr++;
+    new = &s->irq_routes->entries[n];
+
+    *new = *entry;
+
+    set_gsi(s, entry->gsi);
+}
+
+void aehd_irqchip_release_virq(AEHDState *s, int virq)
+{
+    struct aehd_irq_routing_entry *e;
+    int i;
+
+    for (i = 0; i < s->irq_routes->nr; i++) {
+        e = &s->irq_routes->entries[i];
+        if (e->gsi == virq) {
+            s->irq_routes->nr--;
+            *e = s->irq_routes->entries[s->irq_routes->nr];
+        }
+    }
+    clear_gsi(s, virq);
+    aehd_arch_release_virq_post(virq);
+}
+
+static unsigned int aehd_hash_msi(uint32_t data)
+{
+    /*
+     * According to Intel SDM, the lowest byte is an interrupt vector
+     */
+    return data & 0xff;
+}
+
+static void aehd_flush_dynamic_msi_routes(AEHDState *s)
+{
+    AEHDMSIRoute *route, *next;
+    unsigned int hash;
+
+    for (hash = 0; hash < AEHD_MSI_HASHTAB_SIZE; hash++) {
+        QTAILQ_FOREACH_SAFE(route, &s->msi_hashtab[hash], entry, next) {
+            aehd_irqchip_release_virq(s, route->kroute.gsi);
+            QTAILQ_REMOVE(&s->msi_hashtab[hash], route, entry);
+            g_free(route);
+        }
+    }
+}
+
+static int aehd_irqchip_get_virq(AEHDState *s)
+{
+    int next_virq;
+
+    /*
+     * PIC and IOAPIC share the first 16 GSI numbers, thus the available
+     * GSI numbers are more than the number of IRQ route. Allocating a GSI
+     * number can succeed even though a new route entry cannot be added.
+     * When this happens, flush dynamic MSI entries to free IRQ route entries.
+     */
+    if (s->irq_routes->nr == s->gsi_count) {
+        aehd_flush_dynamic_msi_routes(s);
+    }
+
+    /* Return the lowest unused GSI in the bitmap */
+    next_virq = find_first_zero_bit(s->used_gsi_bitmap, s->gsi_count);
+    if (next_virq >= s->gsi_count) {
+        return -ENOSPC;
+    } else {
+        return next_virq;
+    }
+}
+
+static AEHDMSIRoute *aehd_lookup_msi_route(AEHDState *s, MSIMessage msg)
+{
+    unsigned int hash = aehd_hash_msi(msg.data);
+    AEHDMSIRoute *route;
+
+    QTAILQ_FOREACH(route, &s->msi_hashtab[hash], entry) {
+        if (route->kroute.u.msi.address_lo == (uint32_t)msg.address &&
+            route->kroute.u.msi.address_hi == (msg.address >> 32) &&
+            route->kroute.u.msi.data == le32_to_cpu(msg.data)) {
+            return route;
+        }
+    }
+    return NULL;
+}
+
+int aehd_irqchip_send_msi(AEHDState *s, MSIMessage msg)
+{
+    AEHDMSIRoute *route;
+
+    route = aehd_lookup_msi_route(s, msg);
+    if (!route) {
+        int virq;
+
+        virq = aehd_irqchip_get_virq(s);
+        if (virq < 0) {
+            return virq;
+        }
+
+        route = g_malloc0(sizeof(AEHDMSIRoute));
+        route->kroute.gsi = virq;
+        route->kroute.type = AEHD_IRQ_ROUTING_MSI;
+        route->kroute.flags = 0;
+        route->kroute.u.msi.address_lo = (uint32_t)msg.address;
+        route->kroute.u.msi.address_hi = msg.address >> 32;
+        route->kroute.u.msi.data = le32_to_cpu(msg.data);
+
+        aehd_add_routing_entry(s, &route->kroute);
+        aehd_irqchip_commit_routes(s);
+
+        QTAILQ_INSERT_TAIL(&s->msi_hashtab[aehd_hash_msi(msg.data)], route,
+                           entry);
+    }
+
+    assert(route->kroute.type == AEHD_IRQ_ROUTING_MSI);
+
+    return aehd_set_irq(s, route->kroute.gsi, 1);
+}
+
+int aehd_ioctl(AEHDState *s, int type, void *input, size_t input_size,
+               void *output, size_t output_size)
+{
+    int ret;
+    DWORD byteRet;
+
+    ret = DeviceIoControl(s->fd, type, input, input_size,
+                          output, output_size, &byteRet, NULL);
+    if (!ret) {
+        DPRINTF("aehd device IO control %x failed: %lx\n",
+                type, GetLastError());
+        switch (GetLastError()) {
+        case ERROR_MORE_DATA:
+            ret = -E2BIG;
+            break;
+        case ERROR_RETRY:
+            ret = -EAGAIN;
+            break;
+        default:
+            ret = -EFAULT;
+        }
+    } else {
+        ret = 0;
+    }
+    return ret;
+}
+
+int aehd_vm_ioctl(AEHDState *s, int type, void *input, size_t input_size,
+                  void *output, size_t output_size)
+{
+    int ret;
+    DWORD byteRet;
+
+    ret = DeviceIoControl(s->vmfd, type, input, input_size,
+                          output, output_size, &byteRet, NULL);
+    if (!ret) {
+        DPRINTF("aehd VM IO control %x failed: %lx\n",
+                type, GetLastError());
+        switch (GetLastError()) {
+        case ERROR_MORE_DATA:
+            ret = -E2BIG;
+            break;
+        case ERROR_RETRY:
+            ret = -EAGAIN;
+            break;
+        default:
+            ret = -EFAULT;
+        }
+    } else {
+        ret = 0;
+    }
+    return ret;
+}
+
+int aehd_vcpu_ioctl(CPUState *cpu, int type, void *input, size_t input_size,
+                    void *output, size_t output_size)
+{
+    int ret;
+    DWORD byteRet;
+
+    ret = DeviceIoControl(cpu->aehd_fd, type, input, input_size,
+                          output, output_size, &byteRet, NULL);
+    if (!ret) {
+        DPRINTF("aehd VCPU IO control %x failed: %lx\n",
+                type, GetLastError());
+        switch (GetLastError()) {
+        case ERROR_MORE_DATA:
+            ret = -E2BIG;
+            break;
+        case ERROR_RETRY:
+            ret = -EAGAIN;
+            break;
+        default:
+            ret = -EFAULT;
+        }
+    } else {
+        ret = 0;
+    }
+    return ret;
+}
diff --git a/target/i386/aehd/aehd.c b/target/i386/aehd/aehd.c
new file mode 100644
index 0000000000..4890a75553
--- /dev/null
+++ b/target/i386/aehd/aehd.c
@@ -0,0 +1,88 @@ 
+/*
+ * QEMU AEHD support
+ *
+ * Copyright (C) 2006-2008 Qumranet Technologies
+ * Copyright IBM, Corp. 2008
+ *
+ * Authors:
+ *  Anthony Liguori   <aliguori@us.ibm.com>
+ *
+ * This work is licensed under the terms of the GNU GPL, version 2 or later.
+ * See the COPYING file in the top-level directory.
+ *
+ */
+
+#include "qemu/osdep.h"
+#include "qapi/error.h"
+
+#include "cpu.h"
+#include "aehd_int.h"
+#include "sysemu/aehd-interface.h"
+#include "sysemu/sysemu.h"
+#include "sysemu/hw_accel.h"
+#include "sysemu/reset.h"
+#include "sysemu/runstate.h"
+
+#include "exec/gdbstub.h"
+#include "qemu/host-utils.h"
+#include "qemu/main-loop.h"
+#include "qemu/config-file.h"
+#include "qemu/error-report.h"
+#include "qemu/memalign.h"
+#include "hw/i386/x86.h"
+#include "hw/i386/apic.h"
+#include "hw/i386/apic_internal.h"
+#include "hw/i386/apic-msidef.h"
+#include "hw/i386/e820_memory_layout.h"
+
+#include "exec/ioport.h"
+#include "hw/pci/pci.h"
+#include "hw/pci/msi.h"
+#include "migration/blocker.h"
+#include "exec/memattrs.h"
+
+#ifdef DEBUG_AEHD
+#define DPRINTF(fmt, ...) \
+    do { fprintf(stderr, fmt, ## __VA_ARGS__); } while (0)
+#else
+#define DPRINTF(fmt, ...) \
+    do { } while (0)
+#endif
+
+typedef struct MSIRouteEntry MSIRouteEntry;
+
+struct MSIRouteEntry {
+    PCIDevice *dev;             /* Device pointer */
+    int vector;                 /* MSI/MSIX vector index */
+    int virq;                   /* Virtual IRQ index */
+    QLIST_ENTRY(MSIRouteEntry) list;
+};
+
+/* List of used GSI routes */
+static QLIST_HEAD(, MSIRouteEntry) msi_route_list = \
+    QLIST_HEAD_INITIALIZER(msi_route_list);
+
+int aehd_arch_add_msi_route_post(struct aehd_irq_routing_entry *route,
+                                 int vector, PCIDevice *dev)
+{
+    MSIRouteEntry *entry;
+
+    entry = g_new0(MSIRouteEntry, 1);
+    entry->dev = dev;
+    entry->vector = vector;
+    entry->virq = route->gsi;
+    QLIST_INSERT_HEAD(&msi_route_list, entry, list);
+    return 0;
+}
+
+int aehd_arch_release_virq_post(int virq)
+{
+    MSIRouteEntry *entry, *next;
+    QLIST_FOREACH_SAFE(entry, &msi_route_list, list, next) {
+        if (entry->virq == virq) {
+            QLIST_REMOVE(entry, list);
+            break;
+        }
+    }
+    return 0;
+}
diff --git a/target/i386/aehd/aehd_int.h b/target/i386/aehd/aehd_int.h
new file mode 100644
index 0000000000..113f3ebf8e
--- /dev/null
+++ b/target/i386/aehd/aehd_int.h
@@ -0,0 +1,50 @@ 
+/*
+ * Internal definitions for a target's AEHD support
+ *
+ * This work is licensed under the terms of the GNU GPL, version 2 or later.
+ * See the COPYING file in the top-level directory.
+ *
+ */
+
+#ifndef QEMU_AEHD_INT_H
+#define QEMU_AEHD_INT_H
+
+#include "qemu/accel.h"
+#include "sysemu/aehd.h"
+
+typedef struct AEHDSlot {
+    hwaddr start_addr;
+    ram_addr_t memory_size;
+    void *ram;
+    int slot;
+    int flags;
+} AEHDSlot;
+
+typedef struct AEHDMemoryListener {
+    MemoryListener listener;
+    AEHDSlot *slots;
+    int as_id;
+} AEHDMemoryListener;
+
+#define AEHD_MSI_HASHTAB_SIZE    256
+
+struct AEHDState {
+    AccelState parent_obj;
+
+    int nr_slots;
+    HANDLE fd;
+    HANDLE vmfd;
+    GHashTable *gsimap;
+    struct aehd_irq_routing *irq_routes;
+    int nr_allocated_irq_routes;
+    unsigned long *used_gsi_bitmap;
+    unsigned int gsi_count;
+    QTAILQ_HEAD(, AEHDMSIRoute) msi_hashtab[AEHD_MSI_HASHTAB_SIZE];
+    AEHDMemoryListener memory_listener;
+    QLIST_HEAD(, AEHDParkedVcpu) aehd_parked_vcpus;
+};
+
+void aehd_memory_listener_register(AEHDState *s, AEHDMemoryListener *kml,
+                                  AddressSpace *as, int as_id);
+
+#endif
diff --git a/target/i386/aehd/meson.build b/target/i386/aehd/meson.build
new file mode 100644
index 0000000000..50880712db
--- /dev/null
+++ b/target/i386/aehd/meson.build
@@ -0,0 +1,4 @@ 
+i386_softmmu_ss.add(when: 'CONFIG_AEHD', if_true: files(
+  'aehd-all.c',
+  'aehd.c',
+))
diff --git a/target/i386/cpu-sysemu.c b/target/i386/cpu-sysemu.c
index 28115edf44..4d80b58346 100644
--- a/target/i386/cpu-sysemu.c
+++ b/target/i386/cpu-sysemu.c
@@ -21,6 +21,7 @@ 
 #include "cpu.h"
 #include "sysemu/xen.h"
 #include "sysemu/whpx.h"
+#include "sysemu/aehd.h"
 #include "kvm/kvm_i386.h"
 #include "qapi/error.h"
 #include "qapi/qapi-visit-run-state.h"
@@ -258,6 +259,8 @@  APICCommonClass *apic_get_class(Error **errp)
             return NULL;
         }
         apic_type = "kvm-apic";
+    } else if (aehd_enabled()) {
+        apic_type = "aehd-apic";
     } else if (xen_enabled()) {
         apic_type = "xen-apic";
     } else if (whpx_apic_in_platform()) {
diff --git a/target/i386/meson.build b/target/i386/meson.build
index ae38dc9563..76a90b73d5 100644
--- a/target/i386/meson.build
+++ b/target/i386/meson.build
@@ -25,6 +25,7 @@  i386_softmmu_ss.add(when: 'CONFIG_SEV', if_true: files('sev.c'), if_false: files
 i386_user_ss = ss.source_set()
 
 subdir('kvm')
+subdir('aehd')
 subdir('hax')
 subdir('whpx')
 subdir('nvmm')