diff mbox series

[RFC,1/1] hw/intc/loongarch_extioi: Add virt extension support

Message ID 20240116022526.498613-1-gaosong@loongson.cn
State New
Headers show
Series [RFC,1/1] hw/intc/loongarch_extioi: Add virt extension support | expand

Commit Message

Song Gao Jan. 16, 2024, 2:25 a.m. UTC
With hardware extioi, irq can be routed to four vcpus with hardware
extioi. This patch adds virt extension support, sot that irq can
be routed to 256 vcpus.

Signed-off-by: Song Gao <gaosong@loongson.cn>
---
 include/hw/intc/loongarch_extioi.h |  24 +++++-
 include/hw/loongarch/virt.h        |   6 ++
 target/loongarch/cpu.h             |   1 +
 hw/intc/loongarch_extioi.c         | 100 +++++++++++++++++++++++-
 hw/loongarch/virt.c                | 119 ++++++++++++++++++++++++++---
 5 files changed, 233 insertions(+), 17 deletions(-)

Comments

maobibo Jan. 18, 2024, 11:37 a.m. UTC | #1
On 2024/1/16 上午10:25, Song Gao wrote:
> With hardware extioi, irq can be routed to four vcpus with hardware
> extioi. This patch adds virt extension support, sot that irq can
> be routed to 256 vcpus.
> 
> Signed-off-by: Song Gao <gaosong@loongson.cn>
> ---
>   include/hw/intc/loongarch_extioi.h |  24 +++++-
>   include/hw/loongarch/virt.h        |   6 ++
>   target/loongarch/cpu.h             |   1 +
>   hw/intc/loongarch_extioi.c         | 100 +++++++++++++++++++++++-
>   hw/loongarch/virt.c                | 119 ++++++++++++++++++++++++++---
>   5 files changed, 233 insertions(+), 17 deletions(-)
> 
> diff --git a/include/hw/intc/loongarch_extioi.h b/include/hw/intc/loongarch_extioi.h
> index a0a46b888c..1044e8911a 100644
> --- a/include/hw/intc/loongarch_extioi.h
> +++ b/include/hw/intc/loongarch_extioi.h
> @@ -36,10 +36,29 @@
>   #define EXTIOI_ISR_START             (0x700 - APIC_OFFSET)
>   #define EXTIOI_ISR_END               (0x720 - APIC_OFFSET)
>   #define EXTIOI_COREISR_START         (0x800 - APIC_OFFSET)
> -#define EXTIOI_COREISR_END           (0xB20 - APIC_OFFSET)
> +#define EXTIOI_COREISR_END           (0x820 - APIC_OFFSET)
>   #define EXTIOI_COREMAP_START         (0xC00 - APIC_OFFSET)
>   #define EXTIOI_COREMAP_END           (0xD00 - APIC_OFFSET)
>   
> +#define EXTIOI_VIRT_BASE             (0x40000000)
> +#define EXTIOI_VIRT_SIZE             (0x1000)
> +#define EXTIOI_VIRT_FEATURES         (0x0)
> +#define  EXTIOI_HAS_VIRT_EXTENSION     (0)
> +#define  EXTIOI_HAS_ENABLE_OPTION      (1)
> +#define  EXTIOI_HAS_INT_ENCODE         (2)
> +#define  EXTIOI_HAS_IRQ_ENCODE         (3)
> +#define  EXTIOI_HAS_CPUID_ENCODE       (4)
> +#define  EXTIOI_VIRT_HAS_FEATURES      (BIT(EXTIOI_HAS_VIRT_EXTENSION) \
> +                                        | BIT(EXTIOI_HAS_INT_ENCODE)   \
> +                                        | BIT(EXTIOI_HAS_IRQ_ENCODE))
> +#define EXTIOI_VIRT_CONFIG           (0x4)
> +#define  EXTIOI_ENABLE                 (1)
> +#define  EXTIOI_ENABLE_INT_ENCODE      (2)
> +#define  EXTIOI_ENABLE_IRQ_ENCODE      (3)
Could you add some notations about EXTIOI_ENABLE_INT_ENCODE and 
EXTIOI_ENABLE_IRQ_ENCODE?
How about renaming EXTIOI_ENABLE_IRQ_ENCODE with 
EXTIOI_ENABLE_COREMAP_ENCODE?


> +#define  EXTIOI_ENABLE_CPUID_ENCODE    (4)
> +#define EXTIOI_VIRT_COREMAP_START    (0x40)
> +#define EXTIOI_VIRT_COREMAP_END      (0x240)
> +
>   typedef struct ExtIOICore {
>       uint32_t coreisr[EXTIOI_IRQS_GROUP_COUNT];
>       DECLARE_BITMAP(sw_isr[LS3A_INTC_IP], EXTIOI_IRQS);
> @@ -51,6 +70,8 @@ OBJECT_DECLARE_SIMPLE_TYPE(LoongArchExtIOI, LOONGARCH_EXTIOI)
>   struct LoongArchExtIOI {
>       SysBusDevice parent_obj;
>       uint32_t num_cpu;
> +    uint32_t features;
> +    uint32_t status;
>       /* hardware state */
>       uint32_t nodetype[EXTIOI_IRQS_NODETYPE_COUNT / 2];
>       uint32_t bounce[EXTIOI_IRQS_GROUP_COUNT];
> @@ -64,5 +85,6 @@ struct LoongArchExtIOI {
>       qemu_irq irq[EXTIOI_IRQS];
>       ExtIOICore *cpu;
>       MemoryRegion extioi_system_mem;
> +    MemoryRegion virt_extend;
>   };
>   #endif /* LOONGARCH_EXTIOI_H */
> diff --git a/include/hw/loongarch/virt.h b/include/hw/loongarch/virt.h
> index 6ef9a92394..afd63cfd5e 100644
> --- a/include/hw/loongarch/virt.h
> +++ b/include/hw/loongarch/virt.h
> @@ -30,6 +30,9 @@
>   #define VIRT_GED_MEM_ADDR       (VIRT_GED_EVT_ADDR + ACPI_GED_EVT_SEL_LEN)
>   #define VIRT_GED_REG_ADDR       (VIRT_GED_MEM_ADDR + MEMORY_HOTPLUG_IO_LEN)
>   
> +/* board device features */
> +#define VIRT_HAS_V_EIOINTC        BIT(0)
> +
>   struct LoongArchMachineState {
>       /*< private >*/
>       MachineState parent_obj;
> @@ -38,6 +41,7 @@ struct LoongArchMachineState {
>       MemoryRegion highmem;
>       MemoryRegion bios;
>       bool         bios_loaded;
> +    bool         v_eiointc;
>       /* State for other subsystems/APIs: */
>       FWCfgState  *fw_cfg;
>       Notifier     machine_done;
> @@ -48,11 +52,13 @@ struct LoongArchMachineState {
>       DeviceState  *acpi_ged;
>       int          fdt_size;
>       DeviceState *platform_bus_dev;
> +    DeviceState *extioi;
>       PCIBus       *pci_bus;
>       PFlashCFI01  *flash;
>       MemoryRegion system_iocsr;
>       MemoryRegion iocsr_mem;
>       AddressSpace as_iocsr;
> +    int          features;
>   };
>   
>   #define TYPE_LOONGARCH_MACHINE  MACHINE_TYPE_NAME("virt")
> diff --git a/target/loongarch/cpu.h b/target/loongarch/cpu.h
> index 0fa5e0ca93..339a4832f0 100644
> --- a/target/loongarch/cpu.h
> +++ b/target/loongarch/cpu.h
> @@ -36,6 +36,7 @@
>   #define CPUNAME_REG             0x20
>   #define MISC_FUNC_REG           0x420
>   #define IOCSRM_EXTIOI_EN        48
> +#define IOCSRM_EXTIOI_INT_ENCODE  49
>   
>   #define IOCSR_MEM_SIZE          0x428
>   
> diff --git a/hw/intc/loongarch_extioi.c b/hw/intc/loongarch_extioi.c
> index bdfa3b481e..a22a33b849 100644
> --- a/hw/intc/loongarch_extioi.c
> +++ b/hw/intc/loongarch_extioi.c
> @@ -55,6 +55,11 @@ static void extioi_update_irq(LoongArchExtIOI *s, int irq, int level)
>   static void extioi_setirq(void *opaque, int irq, int level)
>   {
>       LoongArchExtIOI *s = LOONGARCH_EXTIOI(opaque);
> +
> +    if (s->status & BIT(EXTIOI_ENABLE)) {
> +        return;
> +    }
> +
what is logic here? Is there is typo like this?
   if !(s->status & BIT(EXTIOI_ENABLE)
       return;

>       trace_loongarch_extioi_setirq(irq, level);
>       if (level) {
>           /*
> @@ -143,10 +148,13 @@ static inline void extioi_update_sw_coremap(LoongArchExtIOI *s, int irq,
>   
>       for (i = 0; i < 4; i++) {
>           cpu = val & 0xff;
> -        cpu = ctz32(cpu);
> -        cpu = (cpu >= 4) ? 0 : cpu;
>           val = val >> 8;
>   
> +        if (!(s->status & BIT(EXTIOI_ENABLE_IRQ_ENCODE))) {
> +            cpu = ctz32(cpu);
> +            cpu = (cpu >= 4) ? 0 : cpu;
> +        }
> +
>           if (s->sw_coremap[irq + i] == cpu) {
>               continue;
>           }
> @@ -177,8 +185,12 @@ static inline void extioi_update_sw_ipmap(LoongArchExtIOI *s, int index,
>       val = cpu_to_le64(val);
>       for (i = 0; i < 4; i++) {
>           ipnum = val & 0xff;
> -        ipnum = ctz32(ipnum);
> -        ipnum = (ipnum >= 4) ? 0 : ipnum;
> +        if (s->status & EXTIOI_ENABLE_INT_ENCODE) {
> +            ipnum = (ipnum >= 8) ? 0 : ipnum;
> +        } else {
> +            ipnum = ctz32(ipnum);
> +            ipnum = (ipnum >= 4) ? 0 : ipnum;
> +        }
>           s->sw_ipmap[index * 4 + i] = ipnum;
>           val = val >> 8;
>       }
> @@ -265,6 +277,63 @@ static const MemoryRegionOps extioi_ops = {
>       .endianness = DEVICE_LITTLE_ENDIAN,
>   };
>   
> +static MemTxResult extioi_virt_readw(void *opaque, hwaddr addr, uint64_t *data,
> +                                     unsigned size, MemTxAttrs attrs)
> +{
> +    LoongArchExtIOI *s = LOONGARCH_EXTIOI(opaque);
> +
> +    switch (addr) {
> +    case EXTIOI_VIRT_FEATURES:
> +        *data = s->features;
> +        break;
> +    case EXTIOI_VIRT_CONFIG:
> +        *data = s->status;
> +        break;
> +    default:
> +        break;
> +    }
> +
> +    return MEMTX_OK;
> +}
> +
> +static MemTxResult extioi_virt_writew(void *opaque, hwaddr addr,
> +                          uint64_t val, unsigned size,
> +                          MemTxAttrs attrs)
> +{
> +    LoongArchExtIOI *s = LOONGARCH_EXTIOI(opaque);
> +
> +    switch (addr) {
> +    case EXTIOI_VIRT_FEATURES:
> +        return MEMTX_ACCESS_ERROR;
> +
> +    case EXTIOI_VIRT_CONFIG:
> +        val &= s->features & ~s->status;
> +
> +        /*
> +         * extioi features can only be set at disabled status
> +         */
> +        if ((s->status & BIT(EXTIOI_ENABLE)) && val) {
> +            return MEMTX_ACCESS_ERROR;
> +        }
> +
> +        s->status |= val;
> +        break;
> +    default:
> +        break;
> +    }
> +    return MEMTX_OK;
> +}
> +
> +static const MemoryRegionOps extioi_virt_ops = {
> +    .read_with_attrs = extioi_virt_readw,
> +    .write_with_attrs = extioi_virt_writew,
> +    .impl.min_access_size = 4,
> +    .impl.max_access_size = 4,
> +    .valid.min_access_size = 4,
> +    .valid.max_access_size = 8,
> +    .endianness = DEVICE_LITTLE_ENDIAN,
> +};
> +
>   static void loongarch_extioi_realize(DeviceState *dev, Error **errp)
>   {
>       LoongArchExtIOI *s = LOONGARCH_EXTIOI(dev);
> @@ -284,6 +353,16 @@ static void loongarch_extioi_realize(DeviceState *dev, Error **errp)
>       memory_region_init_io(&s->extioi_system_mem, OBJECT(s), &extioi_ops,
>                             s, "extioi_system_mem", 0x900);
>       sysbus_init_mmio(sbd, &s->extioi_system_mem);
> +
> +    if (s->features & BIT(EXTIOI_HAS_VIRT_EXTENSION)) {
> +        memory_region_init_io(&s->virt_extend, OBJECT(s), &extioi_virt_ops,
> +                              s, "extioi_virt", EXTIOI_VIRT_SIZE);
> +        sysbus_init_mmio(sbd, &s->virt_extend);
> +        s->features |= EXTIOI_VIRT_HAS_FEATURES;
> +    } else {
> +        s->status |= BIT(EXTIOI_ENABLE);
> +    }
> +
>       s->cpu = g_new0(ExtIOICore, s->num_cpu);
>       if (s->cpu == NULL) {
>           error_setg(errp, "Memory allocation for ExtIOICore faile");
> @@ -304,6 +383,14 @@ static void loongarch_extioi_finalize(Object *obj)
>       g_free(s->cpu);
>   }
>   
> +static void loongarch_extioi_reset(DeviceState *d)
> +{
> +    LoongArchExtIOI *s = LOONGARCH_EXTIOI(d);
> +
> +    /* use legacy interrupt routing method by default */
> +    s->status = 0;
> +}
> +
>   static int vmstate_extioi_post_load(void *opaque, int version_id)
>   {
>       LoongArchExtIOI *s = LOONGARCH_EXTIOI(opaque);
> @@ -347,12 +434,16 @@ static const VMStateDescription vmstate_loongarch_extioi = {
>   
>           VMSTATE_STRUCT_VARRAY_POINTER_UINT32(cpu, LoongArchExtIOI, num_cpu,
>                            vmstate_extioi_core, ExtIOICore),
> +        VMSTATE_UINT32(features, LoongArchExtIOI),
> +        VMSTATE_UINT32(status, LoongArchExtIOI),
>           VMSTATE_END_OF_LIST()
>       }
>   };
>   
>   static Property extioi_properties[] = {
>       DEFINE_PROP_UINT32("num-cpu", LoongArchExtIOI, num_cpu, 1),
> +    DEFINE_PROP_BIT("has-virtualization-extension", LoongArchExtIOI, features,
> +                    EXTIOI_HAS_VIRT_EXTENSION, 0),
>       DEFINE_PROP_END_OF_LIST(),
>   };
>   
> @@ -361,6 +452,7 @@ static void loongarch_extioi_class_init(ObjectClass *klass, void *data)
>       DeviceClass *dc = DEVICE_CLASS(klass);
>   
>       dc->realize = loongarch_extioi_realize;
> +    dc->reset   = loongarch_extioi_reset;
>       device_class_set_props(dc, extioi_properties);
>       dc->vmsd = &vmstate_loongarch_extioi;
>   }
> diff --git a/hw/loongarch/virt.c b/hw/loongarch/virt.c
> index c9a680e61a..3cfab2cf86 100644
> --- a/hw/loongarch/virt.c
> +++ b/hw/loongarch/virt.c
> @@ -15,6 +15,7 @@
>   #include "sysemu/runstate.h"
>   #include "sysemu/reset.h"
>   #include "sysemu/rtc.h"
> +#include "sysemu/kvm.h"
>   #include "hw/loongarch/virt.h"
>   #include "exec/address-spaces.h"
>   #include "hw/irq.h"
> @@ -581,11 +582,21 @@ static void loongarch_irq_init(LoongArchMachineState *lams)
>       }
>   
>       /* Create EXTIOI device */
> +    lams->features = lams->v_eiointc ? VIRT_HAS_V_EIOINTC : 0 ;
>       extioi = qdev_new(TYPE_LOONGARCH_EXTIOI);
>       qdev_prop_set_uint32(extioi, "num-cpu", ms->smp.cpus);
> +    if (lams->features & VIRT_HAS_V_EIOINTC) {
> +        qdev_prop_set_bit(extioi, "has-virtualization-extension", true);
> +    }
>       sysbus_realize_and_unref(SYS_BUS_DEVICE(extioi), &error_fatal);
> +
>       memory_region_add_subregion(&lams->system_iocsr, APIC_BASE,
>                      sysbus_mmio_get_region(SYS_BUS_DEVICE(extioi), 0));
> +    if (lams->features & VIRT_HAS_V_EIOINTC) {
> +        memory_region_add_subregion(&lams->system_iocsr, EXTIOI_VIRT_BASE,
> +                   sysbus_mmio_get_region(SYS_BUS_DEVICE(extioi), 1));
> +    }
> +    lams->extioi = extioi;
>   
>       /*
>        * connect ext irq to the cpu irq
> @@ -731,32 +742,87 @@ static void loongarch_direct_kernel_boot(LoongArchMachineState *lams,
>       }
>   }
>   
> -static void loongarch_qemu_write(void *opaque, hwaddr addr,
> -                                 uint64_t val, unsigned size)
> +static MemTxResult loongarch_qemu_write(void *opaque, hwaddr addr, uint64_t val,
> +                                        unsigned size, MemTxAttrs attrs)
>   {
> +    LoongArchMachineState *lams = LOONGARCH_MACHINE(opaque);
> +    uint64_t features;
> +
> +    switch (addr) {
> +    case MISC_FUNC_REG:
> +        if (!(lams->features & 1)) {
> +            return MEMTX_OK;
> +        }
> +
> +        features = address_space_ldl(&lams->as_iocsr,
> +                                     EXTIOI_VIRT_BASE + EXTIOI_VIRT_CONFIG,
> +                                     attrs, NULL);
> +        if (val & BIT_ULL(IOCSRM_EXTIOI_EN)) {
> +            features |= BIT(EXTIOI_ENABLE);
> +        }
> +        if (val & BIT_ULL(IOCSRM_EXTIOI_INT_ENCODE)) {
> +            features |= BIT(EXTIOI_ENABLE_INT_ENCODE);
> +        }
> +
> +        address_space_stl(&lams->as_iocsr,
> +                          EXTIOI_VIRT_BASE + EXTIOI_VIRT_CONFIG,
> +                          features, attrs, NULL);
> +    }
> +
> +    return MEMTX_OK;
>   }
>   
> -static uint64_t loongarch_qemu_read(void *opaque, hwaddr addr, unsigned size)
> +static MemTxResult loongarch_qemu_read(void *opaque, hwaddr addr,
> +                                       uint64_t *data,
> +                                       unsigned size, MemTxAttrs attrs)
>   {
> +    LoongArchMachineState *lams = LOONGARCH_MACHINE(opaque);
> +    uint64_t ret = 0;
> +    int features;
> +
>       switch (addr) {
>       case VERSION_REG:
> -        return 0x11ULL;
> +        ret = 0x11ULL;
> +        break;
>       case FEATURE_REG:
> -        return 1ULL << IOCSRF_MSI | 1ULL << IOCSRF_EXTIOI |
> -               1ULL << IOCSRF_CSRIPI;
> +        ret = 1ULL << IOCSRF_MSI | 1ULL << IOCSRF_EXTIOI |
> +              1ULL << IOCSRF_CSRIPI;
> +        if (kvm_enabled()) {
> +            ret |= 1ULL << IOCSRF_VM;
> +        }
> +        break;
>       case VENDOR_REG:
> -        return 0x6e6f73676e6f6f4cULL; /* "Loongson" */
> +        ret = 0x6e6f73676e6f6f4cULL; /* "Loongson" */
> +        break;
>       case CPUNAME_REG:
> -        return 0x303030354133ULL;     /* "3A5000" */
> +        ret = 0x303030354133ULL;     /* "3A5000" */
> +        break;
>       case MISC_FUNC_REG:
> -        return 1ULL << IOCSRM_EXTIOI_EN;
> +        if (!(lams->features & 1)) {
> +            ret |= BIT_ULL(IOCSRM_EXTIOI_EN);
> +            break;
> +        }
> +
> +        features = address_space_ldl(&lams->as_iocsr,
> +                                     EXTIOI_VIRT_BASE + EXTIOI_VIRT_CONFIG,
> +                                     attrs, NULL);
> +        if (features & BIT(EXTIOI_ENABLE)) {
> +            ret |= BIT_ULL(IOCSRM_EXTIOI_EN);
> +        }
> +
> +        if (features & BIT(EXTIOI_ENABLE_INT_ENCODE)) {
> +            ret |= BIT_ULL(IOCSRM_EXTIOI_INT_ENCODE);
> +        }
> +        break;
>       }
> -    return 0ULL;
> +
> +    *data = ret;
> +    return MEMTX_OK;
>   }
>   
>   static const MemoryRegionOps loongarch_qemu_ops = {
> -    .read = loongarch_qemu_read,
> -    .write = loongarch_qemu_write,
> +    .read_with_attrs  = loongarch_qemu_read,
> +    .write_with_attrs = loongarch_qemu_write,
>       .endianness = DEVICE_LITTLE_ENDIAN,
>       .valid = {
>           .min_access_size = 4,
> @@ -791,6 +857,14 @@ static void loongarch_init(MachineState *machine)
>           cpu_model = LOONGARCH_CPU_TYPE_NAME("la464");
>       }
>   
> +    if (lams->v_eiointc && !kvm_enabled()) {
> +        error_report("loongarch_init: %s does not support providing "
> +                     "v-eiointc to the guest CPU",
> +                     current_accel_name());
> +        exit(1);
> +    }
> +
> +
>       if (ram_size < 1 * GiB) {
>           error_report("ram_size must be greater than 1G.");
>           exit(1);
> @@ -961,6 +1035,7 @@ static void loongarch_machine_initfn(Object *obj)
>   {
>       LoongArchMachineState *lams = LOONGARCH_MACHINE(obj);
>   
> +    lams->v_eiointc = false;
>       lams->acpi = ON_OFF_AUTO_AUTO;
>       lams->oem_id = g_strndup(ACPI_BUILD_APPNAME6, 6);
>       lams->oem_table_id = g_strndup(ACPI_BUILD_APPNAME8, 8);
> @@ -1114,6 +1189,20 @@ static int64_t virt_get_default_cpu_node_id(const MachineState *ms, int idx)
>       return nidx;
>   }
>   
> +static bool virt_get_v_eiointc(Object *obj, Error **errp)
> +{
> +    LoongArchMachineState *lams = LOONGARCH_MACHINE(obj);
> +
> +    return lams->v_eiointc;
> +}
> +
> +static void virt_set_v_eiointc(Object *obj, bool value, Error **errp)
> +{
> +    LoongArchMachineState *lams = LOONGARCH_MACHINE(obj);
> +
> +    lams->v_eiointc = value;
It seems that there is duplicated definition between v_eiointc and 
features & VIRT_HAS_V_EIOINTC.

How about removing v_eiointc and using features only here, such as
if (value)
     lams->features |= VIRT_HAS_V_EIOINTC;

> +}
> +
>   static void loongarch_class_init(ObjectClass *oc, void *data)
>   {
>       MachineClass *mc = MACHINE_CLASS(oc);
> @@ -1152,6 +1241,12 @@ static void loongarch_class_init(ObjectClass *oc, void *data)
>   #ifdef CONFIG_TPM
>       machine_class_allow_dynamic_sysbus_dev(mc, TYPE_TPM_TIS_SYSBUS);
>   #endif
> +
> +    object_class_property_add_bool(oc, "v-eiointc", virt_get_v_eiointc,
> +                               virt_set_v_eiointc);
> +    object_class_property_set_description(oc, "v-eiointc",
> +                            "Set on/off to enable/disable The virt"
> +                            "LoongArch Extend I/O Interrupt Controller. ");
>   }
Could you split this patch into two? one for extioi extend features and 
the other for v-eiointc property definition.

Regards
Bibo Mao

>   
>   static const TypeInfo loongarch_machine_types[] = {
>
diff mbox series

Patch

diff --git a/include/hw/intc/loongarch_extioi.h b/include/hw/intc/loongarch_extioi.h
index a0a46b888c..1044e8911a 100644
--- a/include/hw/intc/loongarch_extioi.h
+++ b/include/hw/intc/loongarch_extioi.h
@@ -36,10 +36,29 @@ 
 #define EXTIOI_ISR_START             (0x700 - APIC_OFFSET)
 #define EXTIOI_ISR_END               (0x720 - APIC_OFFSET)
 #define EXTIOI_COREISR_START         (0x800 - APIC_OFFSET)
-#define EXTIOI_COREISR_END           (0xB20 - APIC_OFFSET)
+#define EXTIOI_COREISR_END           (0x820 - APIC_OFFSET)
 #define EXTIOI_COREMAP_START         (0xC00 - APIC_OFFSET)
 #define EXTIOI_COREMAP_END           (0xD00 - APIC_OFFSET)
 
+#define EXTIOI_VIRT_BASE             (0x40000000)
+#define EXTIOI_VIRT_SIZE             (0x1000)
+#define EXTIOI_VIRT_FEATURES         (0x0)
+#define  EXTIOI_HAS_VIRT_EXTENSION     (0)
+#define  EXTIOI_HAS_ENABLE_OPTION      (1)
+#define  EXTIOI_HAS_INT_ENCODE         (2)
+#define  EXTIOI_HAS_IRQ_ENCODE         (3)
+#define  EXTIOI_HAS_CPUID_ENCODE       (4)
+#define  EXTIOI_VIRT_HAS_FEATURES      (BIT(EXTIOI_HAS_VIRT_EXTENSION) \
+                                        | BIT(EXTIOI_HAS_INT_ENCODE)   \
+                                        | BIT(EXTIOI_HAS_IRQ_ENCODE))
+#define EXTIOI_VIRT_CONFIG           (0x4)
+#define  EXTIOI_ENABLE                 (1)
+#define  EXTIOI_ENABLE_INT_ENCODE      (2)
+#define  EXTIOI_ENABLE_IRQ_ENCODE      (3)
+#define  EXTIOI_ENABLE_CPUID_ENCODE    (4)
+#define EXTIOI_VIRT_COREMAP_START    (0x40)
+#define EXTIOI_VIRT_COREMAP_END      (0x240)
+
 typedef struct ExtIOICore {
     uint32_t coreisr[EXTIOI_IRQS_GROUP_COUNT];
     DECLARE_BITMAP(sw_isr[LS3A_INTC_IP], EXTIOI_IRQS);
@@ -51,6 +70,8 @@  OBJECT_DECLARE_SIMPLE_TYPE(LoongArchExtIOI, LOONGARCH_EXTIOI)
 struct LoongArchExtIOI {
     SysBusDevice parent_obj;
     uint32_t num_cpu;
+    uint32_t features;
+    uint32_t status;
     /* hardware state */
     uint32_t nodetype[EXTIOI_IRQS_NODETYPE_COUNT / 2];
     uint32_t bounce[EXTIOI_IRQS_GROUP_COUNT];
@@ -64,5 +85,6 @@  struct LoongArchExtIOI {
     qemu_irq irq[EXTIOI_IRQS];
     ExtIOICore *cpu;
     MemoryRegion extioi_system_mem;
+    MemoryRegion virt_extend;
 };
 #endif /* LOONGARCH_EXTIOI_H */
diff --git a/include/hw/loongarch/virt.h b/include/hw/loongarch/virt.h
index 6ef9a92394..afd63cfd5e 100644
--- a/include/hw/loongarch/virt.h
+++ b/include/hw/loongarch/virt.h
@@ -30,6 +30,9 @@ 
 #define VIRT_GED_MEM_ADDR       (VIRT_GED_EVT_ADDR + ACPI_GED_EVT_SEL_LEN)
 #define VIRT_GED_REG_ADDR       (VIRT_GED_MEM_ADDR + MEMORY_HOTPLUG_IO_LEN)
 
+/* board device features */
+#define VIRT_HAS_V_EIOINTC        BIT(0)
+
 struct LoongArchMachineState {
     /*< private >*/
     MachineState parent_obj;
@@ -38,6 +41,7 @@  struct LoongArchMachineState {
     MemoryRegion highmem;
     MemoryRegion bios;
     bool         bios_loaded;
+    bool         v_eiointc;
     /* State for other subsystems/APIs: */
     FWCfgState  *fw_cfg;
     Notifier     machine_done;
@@ -48,11 +52,13 @@  struct LoongArchMachineState {
     DeviceState  *acpi_ged;
     int          fdt_size;
     DeviceState *platform_bus_dev;
+    DeviceState *extioi;
     PCIBus       *pci_bus;
     PFlashCFI01  *flash;
     MemoryRegion system_iocsr;
     MemoryRegion iocsr_mem;
     AddressSpace as_iocsr;
+    int          features;
 };
 
 #define TYPE_LOONGARCH_MACHINE  MACHINE_TYPE_NAME("virt")
diff --git a/target/loongarch/cpu.h b/target/loongarch/cpu.h
index 0fa5e0ca93..339a4832f0 100644
--- a/target/loongarch/cpu.h
+++ b/target/loongarch/cpu.h
@@ -36,6 +36,7 @@ 
 #define CPUNAME_REG             0x20
 #define MISC_FUNC_REG           0x420
 #define IOCSRM_EXTIOI_EN        48
+#define IOCSRM_EXTIOI_INT_ENCODE  49
 
 #define IOCSR_MEM_SIZE          0x428
 
diff --git a/hw/intc/loongarch_extioi.c b/hw/intc/loongarch_extioi.c
index bdfa3b481e..a22a33b849 100644
--- a/hw/intc/loongarch_extioi.c
+++ b/hw/intc/loongarch_extioi.c
@@ -55,6 +55,11 @@  static void extioi_update_irq(LoongArchExtIOI *s, int irq, int level)
 static void extioi_setirq(void *opaque, int irq, int level)
 {
     LoongArchExtIOI *s = LOONGARCH_EXTIOI(opaque);
+
+    if (s->status & BIT(EXTIOI_ENABLE)) {
+        return;
+    }
+
     trace_loongarch_extioi_setirq(irq, level);
     if (level) {
         /*
@@ -143,10 +148,13 @@  static inline void extioi_update_sw_coremap(LoongArchExtIOI *s, int irq,
 
     for (i = 0; i < 4; i++) {
         cpu = val & 0xff;
-        cpu = ctz32(cpu);
-        cpu = (cpu >= 4) ? 0 : cpu;
         val = val >> 8;
 
+        if (!(s->status & BIT(EXTIOI_ENABLE_IRQ_ENCODE))) {
+            cpu = ctz32(cpu);
+            cpu = (cpu >= 4) ? 0 : cpu;
+        }
+
         if (s->sw_coremap[irq + i] == cpu) {
             continue;
         }
@@ -177,8 +185,12 @@  static inline void extioi_update_sw_ipmap(LoongArchExtIOI *s, int index,
     val = cpu_to_le64(val);
     for (i = 0; i < 4; i++) {
         ipnum = val & 0xff;
-        ipnum = ctz32(ipnum);
-        ipnum = (ipnum >= 4) ? 0 : ipnum;
+        if (s->status & EXTIOI_ENABLE_INT_ENCODE) {
+            ipnum = (ipnum >= 8) ? 0 : ipnum;
+        } else {
+            ipnum = ctz32(ipnum);
+            ipnum = (ipnum >= 4) ? 0 : ipnum;
+        }
         s->sw_ipmap[index * 4 + i] = ipnum;
         val = val >> 8;
     }
@@ -265,6 +277,63 @@  static const MemoryRegionOps extioi_ops = {
     .endianness = DEVICE_LITTLE_ENDIAN,
 };
 
+static MemTxResult extioi_virt_readw(void *opaque, hwaddr addr, uint64_t *data,
+                                     unsigned size, MemTxAttrs attrs)
+{
+    LoongArchExtIOI *s = LOONGARCH_EXTIOI(opaque);
+
+    switch (addr) {
+    case EXTIOI_VIRT_FEATURES:
+        *data = s->features;
+        break;
+    case EXTIOI_VIRT_CONFIG:
+        *data = s->status;
+        break;
+    default:
+        break;
+    }
+
+    return MEMTX_OK;
+}
+
+static MemTxResult extioi_virt_writew(void *opaque, hwaddr addr,
+                          uint64_t val, unsigned size,
+                          MemTxAttrs attrs)
+{
+    LoongArchExtIOI *s = LOONGARCH_EXTIOI(opaque);
+
+    switch (addr) {
+    case EXTIOI_VIRT_FEATURES:
+        return MEMTX_ACCESS_ERROR;
+
+    case EXTIOI_VIRT_CONFIG:
+        val &= s->features & ~s->status;
+
+        /*
+         * extioi features can only be set at disabled status
+         */
+        if ((s->status & BIT(EXTIOI_ENABLE)) && val) {
+            return MEMTX_ACCESS_ERROR;
+        }
+
+        s->status |= val;
+        break;
+    default:
+        break;
+    }
+    return MEMTX_OK;
+}
+
+static const MemoryRegionOps extioi_virt_ops = {
+    .read_with_attrs = extioi_virt_readw,
+    .write_with_attrs = extioi_virt_writew,
+    .impl.min_access_size = 4,
+    .impl.max_access_size = 4,
+    .valid.min_access_size = 4,
+    .valid.max_access_size = 8,
+    .endianness = DEVICE_LITTLE_ENDIAN,
+};
+
 static void loongarch_extioi_realize(DeviceState *dev, Error **errp)
 {
     LoongArchExtIOI *s = LOONGARCH_EXTIOI(dev);
@@ -284,6 +353,16 @@  static void loongarch_extioi_realize(DeviceState *dev, Error **errp)
     memory_region_init_io(&s->extioi_system_mem, OBJECT(s), &extioi_ops,
                           s, "extioi_system_mem", 0x900);
     sysbus_init_mmio(sbd, &s->extioi_system_mem);
+
+    if (s->features & BIT(EXTIOI_HAS_VIRT_EXTENSION)) {
+        memory_region_init_io(&s->virt_extend, OBJECT(s), &extioi_virt_ops,
+                              s, "extioi_virt", EXTIOI_VIRT_SIZE);
+        sysbus_init_mmio(sbd, &s->virt_extend);
+        s->features |= EXTIOI_VIRT_HAS_FEATURES;
+    } else {
+        s->status |= BIT(EXTIOI_ENABLE);
+    }
+
     s->cpu = g_new0(ExtIOICore, s->num_cpu);
     if (s->cpu == NULL) {
         error_setg(errp, "Memory allocation for ExtIOICore faile");
@@ -304,6 +383,14 @@  static void loongarch_extioi_finalize(Object *obj)
     g_free(s->cpu);
 }
 
+static void loongarch_extioi_reset(DeviceState *d)
+{
+    LoongArchExtIOI *s = LOONGARCH_EXTIOI(d);
+
+    /* use legacy interrupt routing method by default */
+    s->status = 0;
+}
+
 static int vmstate_extioi_post_load(void *opaque, int version_id)
 {
     LoongArchExtIOI *s = LOONGARCH_EXTIOI(opaque);
@@ -347,12 +434,16 @@  static const VMStateDescription vmstate_loongarch_extioi = {
 
         VMSTATE_STRUCT_VARRAY_POINTER_UINT32(cpu, LoongArchExtIOI, num_cpu,
                          vmstate_extioi_core, ExtIOICore),
+        VMSTATE_UINT32(features, LoongArchExtIOI),
+        VMSTATE_UINT32(status, LoongArchExtIOI),
         VMSTATE_END_OF_LIST()
     }
 };
 
 static Property extioi_properties[] = {
     DEFINE_PROP_UINT32("num-cpu", LoongArchExtIOI, num_cpu, 1),
+    DEFINE_PROP_BIT("has-virtualization-extension", LoongArchExtIOI, features,
+                    EXTIOI_HAS_VIRT_EXTENSION, 0),
     DEFINE_PROP_END_OF_LIST(),
 };
 
@@ -361,6 +452,7 @@  static void loongarch_extioi_class_init(ObjectClass *klass, void *data)
     DeviceClass *dc = DEVICE_CLASS(klass);
 
     dc->realize = loongarch_extioi_realize;
+    dc->reset   = loongarch_extioi_reset;
     device_class_set_props(dc, extioi_properties);
     dc->vmsd = &vmstate_loongarch_extioi;
 }
diff --git a/hw/loongarch/virt.c b/hw/loongarch/virt.c
index c9a680e61a..3cfab2cf86 100644
--- a/hw/loongarch/virt.c
+++ b/hw/loongarch/virt.c
@@ -15,6 +15,7 @@ 
 #include "sysemu/runstate.h"
 #include "sysemu/reset.h"
 #include "sysemu/rtc.h"
+#include "sysemu/kvm.h"
 #include "hw/loongarch/virt.h"
 #include "exec/address-spaces.h"
 #include "hw/irq.h"
@@ -581,11 +582,21 @@  static void loongarch_irq_init(LoongArchMachineState *lams)
     }
 
     /* Create EXTIOI device */
+    lams->features = lams->v_eiointc ? VIRT_HAS_V_EIOINTC : 0 ;
     extioi = qdev_new(TYPE_LOONGARCH_EXTIOI);
     qdev_prop_set_uint32(extioi, "num-cpu", ms->smp.cpus);
+    if (lams->features & VIRT_HAS_V_EIOINTC) {
+        qdev_prop_set_bit(extioi, "has-virtualization-extension", true);
+    }
     sysbus_realize_and_unref(SYS_BUS_DEVICE(extioi), &error_fatal);
+
     memory_region_add_subregion(&lams->system_iocsr, APIC_BASE,
                    sysbus_mmio_get_region(SYS_BUS_DEVICE(extioi), 0));
+    if (lams->features & VIRT_HAS_V_EIOINTC) {
+        memory_region_add_subregion(&lams->system_iocsr, EXTIOI_VIRT_BASE,
+                   sysbus_mmio_get_region(SYS_BUS_DEVICE(extioi), 1));
+    }
+    lams->extioi = extioi;
 
     /*
      * connect ext irq to the cpu irq
@@ -731,32 +742,87 @@  static void loongarch_direct_kernel_boot(LoongArchMachineState *lams,
     }
 }
 
-static void loongarch_qemu_write(void *opaque, hwaddr addr,
-                                 uint64_t val, unsigned size)
+static MemTxResult loongarch_qemu_write(void *opaque, hwaddr addr, uint64_t val,
+                                        unsigned size, MemTxAttrs attrs)
 {
+    LoongArchMachineState *lams = LOONGARCH_MACHINE(opaque);
+    uint64_t features;
+
+    switch (addr) {
+    case MISC_FUNC_REG:
+        if (!(lams->features & 1)) {
+            return MEMTX_OK;
+        }
+
+        features = address_space_ldl(&lams->as_iocsr,
+                                     EXTIOI_VIRT_BASE + EXTIOI_VIRT_CONFIG,
+                                     attrs, NULL);
+        if (val & BIT_ULL(IOCSRM_EXTIOI_EN)) {
+            features |= BIT(EXTIOI_ENABLE);
+        }
+        if (val & BIT_ULL(IOCSRM_EXTIOI_INT_ENCODE)) {
+            features |= BIT(EXTIOI_ENABLE_INT_ENCODE);
+        }
+
+        address_space_stl(&lams->as_iocsr,
+                          EXTIOI_VIRT_BASE + EXTIOI_VIRT_CONFIG,
+                          features, attrs, NULL);
+    }
+
+    return MEMTX_OK;
 }
 
-static uint64_t loongarch_qemu_read(void *opaque, hwaddr addr, unsigned size)
+static MemTxResult loongarch_qemu_read(void *opaque, hwaddr addr,
+                                       uint64_t *data,
+                                       unsigned size, MemTxAttrs attrs)
 {
+    LoongArchMachineState *lams = LOONGARCH_MACHINE(opaque);
+    uint64_t ret = 0;
+    int features;
+
     switch (addr) {
     case VERSION_REG:
-        return 0x11ULL;
+        ret = 0x11ULL;
+        break;
     case FEATURE_REG:
-        return 1ULL << IOCSRF_MSI | 1ULL << IOCSRF_EXTIOI |
-               1ULL << IOCSRF_CSRIPI;
+        ret = 1ULL << IOCSRF_MSI | 1ULL << IOCSRF_EXTIOI |
+              1ULL << IOCSRF_CSRIPI;
+        if (kvm_enabled()) {
+            ret |= 1ULL << IOCSRF_VM;
+        }
+        break;
     case VENDOR_REG:
-        return 0x6e6f73676e6f6f4cULL; /* "Loongson" */
+        ret = 0x6e6f73676e6f6f4cULL; /* "Loongson" */
+        break;
     case CPUNAME_REG:
-        return 0x303030354133ULL;     /* "3A5000" */
+        ret = 0x303030354133ULL;     /* "3A5000" */
+        break;
     case MISC_FUNC_REG:
-        return 1ULL << IOCSRM_EXTIOI_EN;
+        if (!(lams->features & 1)) {
+            ret |= BIT_ULL(IOCSRM_EXTIOI_EN);
+            break;
+        }
+
+        features = address_space_ldl(&lams->as_iocsr,
+                                     EXTIOI_VIRT_BASE + EXTIOI_VIRT_CONFIG,
+                                     attrs, NULL);
+        if (features & BIT(EXTIOI_ENABLE)) {
+            ret |= BIT_ULL(IOCSRM_EXTIOI_EN);
+        }
+
+        if (features & BIT(EXTIOI_ENABLE_INT_ENCODE)) {
+            ret |= BIT_ULL(IOCSRM_EXTIOI_INT_ENCODE);
+        }
+        break;
     }
-    return 0ULL;
+
+    *data = ret;
+    return MEMTX_OK;
 }
 
 static const MemoryRegionOps loongarch_qemu_ops = {
-    .read = loongarch_qemu_read,
-    .write = loongarch_qemu_write,
+    .read_with_attrs  = loongarch_qemu_read,
+    .write_with_attrs = loongarch_qemu_write,
     .endianness = DEVICE_LITTLE_ENDIAN,
     .valid = {
         .min_access_size = 4,
@@ -791,6 +857,14 @@  static void loongarch_init(MachineState *machine)
         cpu_model = LOONGARCH_CPU_TYPE_NAME("la464");
     }
 
+    if (lams->v_eiointc && !kvm_enabled()) {
+        error_report("loongarch_init: %s does not support providing "
+                     "v-eiointc to the guest CPU",
+                     current_accel_name());
+        exit(1);
+    }
+
+
     if (ram_size < 1 * GiB) {
         error_report("ram_size must be greater than 1G.");
         exit(1);
@@ -961,6 +1035,7 @@  static void loongarch_machine_initfn(Object *obj)
 {
     LoongArchMachineState *lams = LOONGARCH_MACHINE(obj);
 
+    lams->v_eiointc = false;
     lams->acpi = ON_OFF_AUTO_AUTO;
     lams->oem_id = g_strndup(ACPI_BUILD_APPNAME6, 6);
     lams->oem_table_id = g_strndup(ACPI_BUILD_APPNAME8, 8);
@@ -1114,6 +1189,20 @@  static int64_t virt_get_default_cpu_node_id(const MachineState *ms, int idx)
     return nidx;
 }
 
+static bool virt_get_v_eiointc(Object *obj, Error **errp)
+{
+    LoongArchMachineState *lams = LOONGARCH_MACHINE(obj);
+
+    return lams->v_eiointc;
+}
+
+static void virt_set_v_eiointc(Object *obj, bool value, Error **errp)
+{
+    LoongArchMachineState *lams = LOONGARCH_MACHINE(obj);
+
+    lams->v_eiointc = value;
+}
+
 static void loongarch_class_init(ObjectClass *oc, void *data)
 {
     MachineClass *mc = MACHINE_CLASS(oc);
@@ -1152,6 +1241,12 @@  static void loongarch_class_init(ObjectClass *oc, void *data)
 #ifdef CONFIG_TPM
     machine_class_allow_dynamic_sysbus_dev(mc, TYPE_TPM_TIS_SYSBUS);
 #endif
+
+    object_class_property_add_bool(oc, "v-eiointc", virt_get_v_eiointc,
+                               virt_set_v_eiointc);
+    object_class_property_set_description(oc, "v-eiointc",
+                            "Set on/off to enable/disable The virt"
+                            "LoongArch Extend I/O Interrupt Controller. ");
 }
 
 static const TypeInfo loongarch_machine_types[] = {