[PATCH] target/riscv/kvm: Use the host AIA MSI ID limit

Qingwei Hu posted 1 patch 4 days, 6 hours ago
hw/riscv/virt-acpi-build.c   |  4 +--
hw/riscv/virt.c              | 28 ++++++++++++-----
include/hw/riscv/virt.h      |  1 +
target/riscv/kvm/kvm-cpu.c   | 59 +++++++++++++++++++++++-------------
target/riscv/kvm/kvm_riscv.h |  9 +++---
5 files changed, 66 insertions(+), 35 deletions(-)
[PATCH] target/riscv/kvm: Use the host AIA MSI ID limit
Posted by Qingwei Hu 4 days, 6 hours ago
KVM initializes CONFIG_IDS to the number of guest MSI IDs supported by
the host. The virt machine currently overwrites it with the fixed
VIRT_IRQCHIP_NUM_MSIS value of 255, which can be inconsistent with the
host-supported guest MSI ID count.

Create the KVM AIA device before realizing the per-socket IMSICs and
read the host-provided CONFIG_IDS value. Use it to configure the QEMU
IMSIC devices and the guest-visible DT and ACPI descriptions. Complete
the remaining KVM AIA setup after all vCPUs have been created, as the
IMSIC address configuration depends on them.

Keep VIRT_IRQCHIP_NUM_MSIS as the default for non-KVM machines.

Fixes: 9634ef7eda5f ("target/riscv: Create an KVM AIA irqchip")
Signed-off-by: Qingwei Hu <qingwei.hu@bytedance.com>
---
 hw/riscv/virt-acpi-build.c   |  4 +--
 hw/riscv/virt.c              | 28 ++++++++++++-----
 include/hw/riscv/virt.h      |  1 +
 target/riscv/kvm/kvm-cpu.c   | 59 +++++++++++++++++++++++-------------
 target/riscv/kvm/kvm_riscv.h |  9 +++---
 5 files changed, 66 insertions(+), 35 deletions(-)

diff --git a/hw/riscv/virt-acpi-build.c b/hw/riscv/virt-acpi-build.c
index 8e516ec114..0ebfa10a02 100644
--- a/hw/riscv/virt-acpi-build.c
+++ b/hw/riscv/virt-acpi-build.c
@@ -576,9 +576,9 @@ static void build_madt(GArray *table_data,
         build_append_int_noprefix(table_data, 0, 1);        /* Reserved */
         build_append_int_noprefix(table_data, 0, 4);        /* Flags */
         /* Number of supervisor mode Interrupt Identities */
-        build_append_int_noprefix(table_data, VIRT_IRQCHIP_NUM_MSIS, 2);
+        build_append_int_noprefix(table_data, s->num_msis, 2);
         /* Number of guest mode Interrupt Identities */
-        build_append_int_noprefix(table_data, VIRT_IRQCHIP_NUM_MSIS, 2);
+        build_append_int_noprefix(table_data, s->num_msis, 2);
         /* Guest Index Bits */
         build_append_int_noprefix(table_data, guest_index_bits, 1);
         /* Hart Index Bits */
diff --git a/hw/riscv/virt.c b/hw/riscv/virt.c
index d4b501802f..ba0e74ce83 100644
--- a/hw/riscv/virt.c
+++ b/hw/riscv/virt.c
@@ -277,7 +277,7 @@ static void create_fdt_sockets(RISCVVirtState *s,
             .imsic_m_base = !kvm_enabled() ? s->memmap[VIRT_IMSIC_M].base : 0,
             .imsic_s_base = s->memmap[VIRT_IMSIC_S].base,
             .imsic_group_max_size = VIRT_IMSIC_GROUP_MAX_SIZE,
-            .irqchip_num_msis = VIRT_IRQCHIP_NUM_MSIS,
+            .irqchip_num_msis = s->num_msis,
             .aia_guests = s->aia_guests
         };
 
@@ -718,6 +718,7 @@ static void virt_machine_init(MachineState *machine)
     DeviceState *mmio_irqchip, *virtio_irqchip, *pcie_irqchip;
     int i, base_hartid, hart_count;
     int socket_count = riscv_socket_count(machine);
+    int aia_fd = -1;
 
     s->memmap = virt_memmap;
 
@@ -733,6 +734,16 @@ static void virt_machine_init(MachineState *machine)
         exit(1);
     }
 
+    /*
+     * Create the KVM AIA device and query the host MSI ID count before
+     * realizing the per-socket IMSICs. Complete AIA initialization after
+     * all vCPUs have been created.
+     */
+    if (kvm_enabled() && virt_use_kvm_aia_aplic_imsic(s->aia_type)) {
+        aia_fd = kvm_riscv_aia_create(VIRT_IRQCHIP_NUM_SOURCES,
+                                      &s->num_msis);
+    }
+
     /* Initialize sockets */
     mmio_irqchip = virtio_irqchip = pcie_irqchip = NULL;
     for (i = 0; i < socket_count; i++) {
@@ -826,7 +837,7 @@ static void virt_machine_init(MachineState *machine)
                                              &s->memmap[VIRT_IMSIC_M],
                                              &s->memmap[VIRT_IMSIC_S],
                                              i, base_hartid, hart_count,
-                                             VIRT_IRQCHIP_NUM_MSIS,
+                                             s->num_msis,
                                              VIRT_IRQCHIP_NUM_PRIO_BITS);
         }
 
@@ -845,12 +856,12 @@ static void virt_machine_init(MachineState *machine)
         }
     }
 
-    if (kvm_enabled() && virt_use_kvm_aia_aplic_imsic(s->aia_type)) {
-        kvm_riscv_aia_create(machine, IMSIC_MMIO_GROUP_MIN_SHIFT,
-                             VIRT_IRQCHIP_NUM_SOURCES, VIRT_IRQCHIP_NUM_MSIS,
-                             s->memmap[VIRT_APLIC_S].base,
-                             s->memmap[VIRT_IMSIC_S].base,
-                             s->aia_guests);
+    if (aia_fd >= 0) {
+        kvm_riscv_aia_init(machine, aia_fd, IMSIC_MMIO_GROUP_MIN_SHIFT,
+                           VIRT_IRQCHIP_NUM_SOURCES,
+                           s->memmap[VIRT_APLIC_S].base,
+                           s->memmap[VIRT_IMSIC_S].base,
+                           s->aia_guests);
     }
 
     if (riscv_is_32bit(&s->soc[0])) {
@@ -972,6 +983,7 @@ static void virt_machine_instance_init(Object *obj)
     s->acpi = ON_OFF_AUTO_AUTO;
     s->iommu_sys = ON_OFF_AUTO_AUTO;
     s->num_sources = VIRT_IRQCHIP_NUM_SOURCES;
+    s->num_msis = VIRT_IRQCHIP_NUM_MSIS;
 }
 
 static char *virt_get_aia_guests(Object *obj, Error **errp)
diff --git a/include/hw/riscv/virt.h b/include/hw/riscv/virt.h
index e516898f9a..f64a2a3448 100644
--- a/include/hw/riscv/virt.h
+++ b/include/hw/riscv/virt.h
@@ -66,6 +66,7 @@ struct RISCVVirtState {
     OnOffAuto iommu_sys;
     uint16_t pci_iommu_bdf;
     uint16_t num_sources;
+    uint32_t num_msis;
 };
 
 enum {
diff --git a/target/riscv/kvm/kvm-cpu.c b/target/riscv/kvm/kvm-cpu.c
index 68e1501b21..02af4e7723 100644
--- a/target/riscv/kvm/kvm-cpu.c
+++ b/target/riscv/kvm/kvm-cpu.c
@@ -1859,19 +1859,11 @@ void kvm_arch_accel_class_init(ObjectClass *oc)
                                     "auto");
 }
 
-void kvm_riscv_aia_create(MachineState *machine, uint64_t group_shift,
-                          uint64_t aia_irq_num, uint64_t aia_msi_num,
-                          uint64_t aplic_base, uint64_t imsic_base,
-                          uint64_t guest_num)
+int kvm_riscv_aia_create(uint64_t aia_irq_num, uint32_t *aia_msi_num)
 {
-    int ret, i;
-    int aia_fd = -1;
+    int ret;
+    int aia_fd;
     uint64_t default_aia_mode;
-    uint64_t socket_count = riscv_socket_count(machine);
-    uint64_t max_hart_per_socket = 0;
-    uint64_t socket, base_hart, hart_count, socket_imsic_base, imsic_addr;
-    uint64_t socket_bits, hart_bits, guest_bits;
-    uint64_t max_group_id;
 
     aia_fd = kvm_create_device(kvm_state, KVM_DEV_TYPE_RISCV_AIA, false);
 
@@ -1903,6 +1895,41 @@ void kvm_riscv_aia_create(MachineState *machine, uint64_t group_shift,
         }
     }
 
+    /*
+     * Get the maximum MSI ID supported by the host from the CONFIG_IDS
+     * attribute and use it for the guest-visible interrupt controller
+     * configuration.
+     */
+    ret = kvm_device_access(aia_fd, KVM_DEV_RISCV_AIA_GRP_CONFIG,
+                            KVM_DEV_RISCV_AIA_CONFIG_IDS,
+                            aia_msi_num, false, NULL);
+    if (ret < 0) {
+        error_report("KVM AIA: failed to get number of MSI IDs");
+        exit(1);
+    }
+
+    if (!kvm_kernel_irqchip_split() && *aia_msi_num < aia_irq_num) {
+        error_report("KVM AIA: host supports only %" PRIu32 " MSI IDs, "
+                     "but the virt machine requires at least %" PRIu64,
+                     *aia_msi_num, aia_irq_num);
+        exit(1);
+    }
+
+    return aia_fd;
+}
+
+void kvm_riscv_aia_init(MachineState *machine, int aia_fd,
+                        uint64_t group_shift, uint64_t aia_irq_num,
+                        uint64_t aplic_base, uint64_t imsic_base,
+                        uint64_t guest_num)
+{
+    int ret, i;
+    uint64_t socket_count = riscv_socket_count(machine);
+    uint64_t max_hart_per_socket = 0;
+    uint64_t socket, base_hart, hart_count, socket_imsic_base, imsic_addr;
+    uint64_t socket_bits, hart_bits, guest_bits;
+    uint64_t max_group_id;
+
     /*
      * Skip APLIC creation in KVM if we're running split mode.
      * This is done by leaving KVM_DEV_RISCV_AIA_CONFIG_SRCS
@@ -1927,15 +1954,6 @@ void kvm_riscv_aia_create(MachineState *machine, uint64_t group_shift,
         }
      }
 
-    ret = kvm_device_access(aia_fd, KVM_DEV_RISCV_AIA_GRP_CONFIG,
-                            KVM_DEV_RISCV_AIA_CONFIG_IDS,
-                            &aia_msi_num, true, NULL);
-    if (ret < 0) {
-        error_report("KVM AIA: failed to set number of msi");
-        exit(1);
-    }
-
-
     if (socket_count > 1) {
         max_group_id = socket_count - 1;
         socket_bits = find_last_bit(&max_group_id, BITS_PER_LONG) + 1;
@@ -1987,7 +2005,6 @@ void kvm_riscv_aia_create(MachineState *machine, uint64_t group_shift,
         }
     }
 
-
     if (max_hart_per_socket > 1) {
         max_hart_per_socket--;
         hart_bits = find_last_bit(&max_hart_per_socket, BITS_PER_LONG) + 1;
diff --git a/target/riscv/kvm/kvm_riscv.h b/target/riscv/kvm/kvm_riscv.h
index 61eaa12443..1499fb4487 100644
--- a/target/riscv/kvm/kvm_riscv.h
+++ b/target/riscv/kvm/kvm_riscv.h
@@ -23,10 +23,11 @@
 
 void kvm_riscv_reset_vcpu(RISCVCPU *cpu);
 void kvm_riscv_set_irq(RISCVCPU *cpu, int irq, int level);
-void kvm_riscv_aia_create(MachineState *machine, uint64_t group_shift,
-                          uint64_t aia_irq_num, uint64_t aia_msi_num,
-                          uint64_t aplic_base, uint64_t imsic_base,
-                          uint64_t guest_num);
+int kvm_riscv_aia_create(uint64_t aia_irq_num, uint32_t *aia_msi_num);
+void kvm_riscv_aia_init(MachineState *machine, int aia_fd,
+                        uint64_t group_shift, uint64_t aia_irq_num,
+                        uint64_t aplic_base, uint64_t imsic_base,
+                        uint64_t guest_num);
 void riscv_kvm_aplic_request(void *opaque, int irq, int level);
 bool kvm_riscv_has_mp_state(void);
 void riscv_kvm_cpu_finalize_features(RISCVCPU *cpu, Error **errp);
-- 
2.39.5
Re: [PATCH] target/riscv/kvm: Use the host AIA MSI ID limit
Posted by Daniel Henrique Barboza 23 hours ago
Hello,

If we try to build this patch in a non-KVM enviroment we'll have this error:

FAILED: libstubs_riscv.a.p/target_riscv_kvm_kvm-stub.c.o
cc -m64 -Ilibstubs_riscv.a.p -I. -I.. -I../common-user/host/x86_64 -I../linux-user/include/host/x86_64 -I../linux-user/include -Itarget/riscv -I../target/riscv -Iqapi -Itrace -Iui -Iui/shader -I/usr/include/glib-2.0 -I/usr/lib/x86_64-linux-gnu/glib-2.0/include -fdiagnostics-color=auto -Wall -Winvalid-pch -Werror -std=gnu11 -O0 -g -mcx16 -msse2 -D_GNU_SOURCE -D_FILE_OFFSET_BITS=64 -D_LARGEFILE_SOURCE -fno-strict-aliasing -fno-common -fwrapv -ftrivial-auto-var-init=zero 
-fzero-call-used-regs=used-gpr -fstack-protector-strong -Wempty-body -Wendif-labels -Wexpansion-to-defined -Wformat-overflow=2 -Wformat-security -Wformat-y2k -Wignored-qualifiers -Wimplicit-fallthrough=2 -Winit-self -Wmissing-format-attribute -Wmissing-prototypes -Wnested-externs -Wold-style-declaration -Wold-style-definition -Wredundant-decls -Wshadow=local -Wstrict-prototypes -Wtype-limits -Wundef -Wvla -Wwrite-strings -Wno-missing-include-dirs -Wno-psabi -Wno-shift-negative-value -isystem 
/local/mnt/workspace/qemu/linux-headers -isystem linux-headers -iquote . -iquote /local/mnt/workspace/qemu -iquote /local/mnt/workspace/qemu/include -iquote /local/mnt/workspace/qemu/host/include/x86_64 -iquote /local/mnt/workspace/qemu/host/include/generic -iquote /local/mnt/workspace/qemu/tcg/x86_64 -pthread -fPIE -DCPU_DEFS_H -DCOMPILING_SYSTEM_VS_USER -DCONFIG_SOFTMMU -MD -MQ libstubs_riscv.a.p/target_riscv_kvm_kvm-stub.c.o -MF libstubs_riscv.a.p/target_riscv_kvm_kvm-stub.c.o.d -o 
libstubs_riscv.a.p/target_riscv_kvm_kvm-stub.c.o -c ../target/riscv/kvm/kvm-stub.c
../target/riscv/kvm/kvm-stub.c:12:6: error: conflicting types for ‘kvm_riscv_aia_create’; have ‘void(MachineState *, uint64_t,  uint64_t,  uint64_t,  uint64_t,  uint64_t,  uint64_t)’ {aka ‘void(MachineState *, long unsigned int,  long unsigned int,  long unsigned int,  long unsigned int,  long unsigned int,  long unsigned int)’}
    12 | void kvm_riscv_aia_create(MachineState *machine, uint64_t group_shift,
       |      ^~~~~~~~~~~~~~~~~~~~
In file included from ../target/riscv/kvm/kvm-stub.c:10:
/local/mnt/workspace/qemu/target/riscv/kvm/kvm_riscv.h:26:5: note: previous declaration of ‘kvm_riscv_aia_create’ with type
  int(uint64_t,  uint32_t *)’ {aka ‘int(long unsigned int,  unsigned int *)’}
    26 | int kvm_riscv_aia_create(uint64_t aia_irq_num, uint32_t *aia_msi_num);
       |     ^~~~~~~~~~~~~~~~~~~~
[1958/3154] Compiling C object libqemu-riscv64-softmmu.a.p/target_riscv_tcg_debug.c.o


The reason is, down there ...


On 9/22/2026 9:22 AM, Qingwei Hu wrote:
> KVM initializes CONFIG_IDS to the number of guest MSI IDs supported by
> the host. The virt machine currently overwrites it with the fixed
> VIRT_IRQCHIP_NUM_MSIS value of 255, which can be inconsistent with the
> host-supported guest MSI ID count.
> 
> Create the KVM AIA device before realizing the per-socket IMSICs and
> read the host-provided CONFIG_IDS value. Use it to configure the QEMU
> IMSIC devices and the guest-visible DT and ACPI descriptions. Complete
> the remaining KVM AIA setup after all vCPUs have been created, as the
> IMSIC address configuration depends on them.
> 
> Keep VIRT_IRQCHIP_NUM_MSIS as the default for non-KVM machines.
> 
> Fixes: 9634ef7eda5f ("target/riscv: Create an KVM AIA irqchip")
> Signed-off-by: Qingwei Hu <qingwei.hu@bytedance.com>
> ---
>   hw/riscv/virt-acpi-build.c   |  4 +--
>   hw/riscv/virt.c              | 28 ++++++++++++-----
>   include/hw/riscv/virt.h      |  1 +
>   target/riscv/kvm/kvm-cpu.c   | 59 +++++++++++++++++++++++-------------
>   target/riscv/kvm/kvm_riscv.h |  9 +++---
>   5 files changed, 66 insertions(+), 35 deletions(-)
> 
> diff --git a/hw/riscv/virt-acpi-build.c b/hw/riscv/virt-acpi-build.c
> index 8e516ec114..0ebfa10a02 100644
> --- a/hw/riscv/virt-acpi-build.c
> +++ b/hw/riscv/virt-acpi-build.c
> @@ -576,9 +576,9 @@ static void build_madt(GArray *table_data,
>           build_append_int_noprefix(table_data, 0, 1);        /* Reserved */
>           build_append_int_noprefix(table_data, 0, 4);        /* Flags */
>           /* Number of supervisor mode Interrupt Identities */
> -        build_append_int_noprefix(table_data, VIRT_IRQCHIP_NUM_MSIS, 2);
> +        build_append_int_noprefix(table_data, s->num_msis, 2);
>           /* Number of guest mode Interrupt Identities */
> -        build_append_int_noprefix(table_data, VIRT_IRQCHIP_NUM_MSIS, 2);
> +        build_append_int_noprefix(table_data, s->num_msis, 2);
>           /* Guest Index Bits */
>           build_append_int_noprefix(table_data, guest_index_bits, 1);
>           /* Hart Index Bits */
> diff --git a/hw/riscv/virt.c b/hw/riscv/virt.c
> index d4b501802f..ba0e74ce83 100644
> --- a/hw/riscv/virt.c
> +++ b/hw/riscv/virt.c
> @@ -277,7 +277,7 @@ static void create_fdt_sockets(RISCVVirtState *s,
>               .imsic_m_base = !kvm_enabled() ? s->memmap[VIRT_IMSIC_M].base : 0,
>               .imsic_s_base = s->memmap[VIRT_IMSIC_S].base,
>               .imsic_group_max_size = VIRT_IMSIC_GROUP_MAX_SIZE,
> -            .irqchip_num_msis = VIRT_IRQCHIP_NUM_MSIS,
> +            .irqchip_num_msis = s->num_msis,
>               .aia_guests = s->aia_guests
>           };
>   
> @@ -718,6 +718,7 @@ static void virt_machine_init(MachineState *machine)
>       DeviceState *mmio_irqchip, *virtio_irqchip, *pcie_irqchip;
>       int i, base_hartid, hart_count;
>       int socket_count = riscv_socket_count(machine);
> +    int aia_fd = -1;
>   
>       s->memmap = virt_memmap;
>   
> @@ -733,6 +734,16 @@ static void virt_machine_init(MachineState *machine)
>           exit(1);
>       }
>   
> +    /*
> +     * Create the KVM AIA device and query the host MSI ID count before
> +     * realizing the per-socket IMSICs. Complete AIA initialization after
> +     * all vCPUs have been created.
> +     */
> +    if (kvm_enabled() && virt_use_kvm_aia_aplic_imsic(s->aia_type)) {
> +        aia_fd = kvm_riscv_aia_create(VIRT_IRQCHIP_NUM_SOURCES,
> +                                      &s->num_msis);
> +    }
> +
>       /* Initialize sockets */
>       mmio_irqchip = virtio_irqchip = pcie_irqchip = NULL;
>       for (i = 0; i < socket_count; i++) {
> @@ -826,7 +837,7 @@ static void virt_machine_init(MachineState *machine)
>                                                &s->memmap[VIRT_IMSIC_M],
>                                                &s->memmap[VIRT_IMSIC_S],
>                                                i, base_hartid, hart_count,
> -                                             VIRT_IRQCHIP_NUM_MSIS,
> +                                             s->num_msis,
>                                                VIRT_IRQCHIP_NUM_PRIO_BITS);
>           }
>   
> @@ -845,12 +856,12 @@ static void virt_machine_init(MachineState *machine)
>           }
>       }
>   
> -    if (kvm_enabled() && virt_use_kvm_aia_aplic_imsic(s->aia_type)) {
> -        kvm_riscv_aia_create(machine, IMSIC_MMIO_GROUP_MIN_SHIFT,
> -                             VIRT_IRQCHIP_NUM_SOURCES, VIRT_IRQCHIP_NUM_MSIS,
> -                             s->memmap[VIRT_APLIC_S].base,
> -                             s->memmap[VIRT_IMSIC_S].base,
> -                             s->aia_guests);

This call of kvm_riscv_aia_create(), even when guarded with a kvm_enabled(), needs to be
visible to the compiler in a TCG only build.  We have a kvm-stub.c file that declares it
like so:

void kvm_riscv_aia_create(MachineState *machine, uint64_t group_shift,
                           uint64_t aia_irq_num, uint64_t aia_msi_num,
                           uint64_t aplic_base, uint64_t imsic_base,
                           uint64_t guest_num)
{
     g_assert_not_reached();
}

Thus changing kvm_riscv_aia_create() requires a change in its kvm-stub as well.

> +    if (aia_fd >= 0) {
> +        kvm_riscv_aia_init(machine, aia_fd, IMSIC_MMIO_GROUP_MIN_SHIFT,
> +                           VIRT_IRQCHIP_NUM_SOURCES,
> +                           s->memmap[VIRT_APLIC_S].base,
> +                           s->memmap[VIRT_IMSIC_S].base,
> +                           s->aia_guests);
>       }

And a similar thing is needed here for kvm_riscv_aia_init(). We need a kvm-stub entry and it
needs to be guarded with kvm_enabled() as well.

We need something like this diff:

$ git diff
diff --git a/hw/riscv/virt.c b/hw/riscv/virt.c
index ba0e74ce83..4df8d7d441 100644
--- a/hw/riscv/virt.c
+++ b/hw/riscv/virt.c
@@ -856,7 +856,7 @@ static void virt_machine_init(MachineState *machine)
          }
      }

-    if (aia_fd >= 0) {
+    if (kvm_enabled() && aia_fd >= 0) {
          kvm_riscv_aia_init(machine, aia_fd, IMSIC_MMIO_GROUP_MIN_SHIFT,
                             VIRT_IRQCHIP_NUM_SOURCES,
                             s->memmap[VIRT_APLIC_S].base,
diff --git a/target/riscv/kvm/kvm-stub.c b/target/riscv/kvm/kvm-stub.c
index 64e39c96d8..b7e6504f5f 100644
--- a/target/riscv/kvm/kvm-stub.c
+++ b/target/riscv/kvm/kvm-stub.c
@@ -9,10 +9,15 @@
  #include "qemu/osdep.h"
  #include "target/riscv/kvm/kvm_riscv.h"

-void kvm_riscv_aia_create(MachineState *machine, uint64_t group_shift,
-                          uint64_t aia_irq_num, uint64_t aia_msi_num,
-                          uint64_t aplic_base, uint64_t imsic_base,
-                          uint64_t guest_num)
+int kvm_riscv_aia_create(uint64_t aia_irq_num, uint32_t *aia_msi_num)
+{
+    g_assert_not_reached();
+}
+
+void kvm_riscv_aia_init(MachineState *machine, int aia_fd,
+                        uint64_t group_shift, uint64_t aia_irq_num,
+                        uint64_t aplic_base, uint64_t imsic_base,
+                        uint64_t guest_num)
  {
      g_assert_not_reached();
  }


Code LGTM otherwise.  Booted a KVM aia guest and play with it a little bit
without any hicups.


Thanks,
Daniel
  >
>       if (riscv_is_32bit(&s->soc[0])) {
> @@ -972,6 +983,7 @@ static void virt_machine_instance_init(Object *obj)
>       s->acpi = ON_OFF_AUTO_AUTO;
>       s->iommu_sys = ON_OFF_AUTO_AUTO;
>       s->num_sources = VIRT_IRQCHIP_NUM_SOURCES;
> +    s->num_msis = VIRT_IRQCHIP_NUM_MSIS;
>   }
>   
>   static char *virt_get_aia_guests(Object *obj, Error **errp)
> diff --git a/include/hw/riscv/virt.h b/include/hw/riscv/virt.h
> index e516898f9a..f64a2a3448 100644
> --- a/include/hw/riscv/virt.h
> +++ b/include/hw/riscv/virt.h
> @@ -66,6 +66,7 @@ struct RISCVVirtState {
>       OnOffAuto iommu_sys;
>       uint16_t pci_iommu_bdf;
>       uint16_t num_sources;
> +    uint32_t num_msis;
>   };
>   
>   enum {
> diff --git a/target/riscv/kvm/kvm-cpu.c b/target/riscv/kvm/kvm-cpu.c
> index 68e1501b21..02af4e7723 100644
> --- a/target/riscv/kvm/kvm-cpu.c
> +++ b/target/riscv/kvm/kvm-cpu.c
> @@ -1859,19 +1859,11 @@ void kvm_arch_accel_class_init(ObjectClass *oc)
>                                       "auto");
>   }
>   
> -void kvm_riscv_aia_create(MachineState *machine, uint64_t group_shift,
> -                          uint64_t aia_irq_num, uint64_t aia_msi_num,
> -                          uint64_t aplic_base, uint64_t imsic_base,
> -                          uint64_t guest_num)
> +int kvm_riscv_aia_create(uint64_t aia_irq_num, uint32_t *aia_msi_num)
>   {
> -    int ret, i;
> -    int aia_fd = -1;
> +    int ret;
> +    int aia_fd;
>       uint64_t default_aia_mode;
> -    uint64_t socket_count = riscv_socket_count(machine);
> -    uint64_t max_hart_per_socket = 0;
> -    uint64_t socket, base_hart, hart_count, socket_imsic_base, imsic_addr;
> -    uint64_t socket_bits, hart_bits, guest_bits;
> -    uint64_t max_group_id;
>   
>       aia_fd = kvm_create_device(kvm_state, KVM_DEV_TYPE_RISCV_AIA, false);
>   
> @@ -1903,6 +1895,41 @@ void kvm_riscv_aia_create(MachineState *machine, uint64_t group_shift,
>           }
>       }
>   
> +    /*
> +     * Get the maximum MSI ID supported by the host from the CONFIG_IDS
> +     * attribute and use it for the guest-visible interrupt controller
> +     * configuration.
> +     */
> +    ret = kvm_device_access(aia_fd, KVM_DEV_RISCV_AIA_GRP_CONFIG,
> +                            KVM_DEV_RISCV_AIA_CONFIG_IDS,
> +                            aia_msi_num, false, NULL);
> +    if (ret < 0) {
> +        error_report("KVM AIA: failed to get number of MSI IDs");
> +        exit(1);
> +    }
> +
> +    if (!kvm_kernel_irqchip_split() && *aia_msi_num < aia_irq_num) {
> +        error_report("KVM AIA: host supports only %" PRIu32 " MSI IDs, "
> +                     "but the virt machine requires at least %" PRIu64,
> +                     *aia_msi_num, aia_irq_num);
> +        exit(1);
> +    }
> +
> +    return aia_fd;
> +}
> +
> +void kvm_riscv_aia_init(MachineState *machine, int aia_fd,
> +                        uint64_t group_shift, uint64_t aia_irq_num,
> +                        uint64_t aplic_base, uint64_t imsic_base,
> +                        uint64_t guest_num)
> +{
> +    int ret, i;
> +    uint64_t socket_count = riscv_socket_count(machine);
> +    uint64_t max_hart_per_socket = 0;
> +    uint64_t socket, base_hart, hart_count, socket_imsic_base, imsic_addr;
> +    uint64_t socket_bits, hart_bits, guest_bits;
> +    uint64_t max_group_id;
> +
>       /*
>        * Skip APLIC creation in KVM if we're running split mode.
>        * This is done by leaving KVM_DEV_RISCV_AIA_CONFIG_SRCS
> @@ -1927,15 +1954,6 @@ void kvm_riscv_aia_create(MachineState *machine, uint64_t group_shift,
>           }
>        }
>   
> -    ret = kvm_device_access(aia_fd, KVM_DEV_RISCV_AIA_GRP_CONFIG,
> -                            KVM_DEV_RISCV_AIA_CONFIG_IDS,
> -                            &aia_msi_num, true, NULL);
> -    if (ret < 0) {
> -        error_report("KVM AIA: failed to set number of msi");
> -        exit(1);
> -    }
> -
> -
>       if (socket_count > 1) {
>           max_group_id = socket_count - 1;
>           socket_bits = find_last_bit(&max_group_id, BITS_PER_LONG) + 1;
> @@ -1987,7 +2005,6 @@ void kvm_riscv_aia_create(MachineState *machine, uint64_t group_shift,
>           }
>       }
>   
> -
>       if (max_hart_per_socket > 1) {
>           max_hart_per_socket--;
>           hart_bits = find_last_bit(&max_hart_per_socket, BITS_PER_LONG) + 1;
> diff --git a/target/riscv/kvm/kvm_riscv.h b/target/riscv/kvm/kvm_riscv.h
> index 61eaa12443..1499fb4487 100644
> --- a/target/riscv/kvm/kvm_riscv.h
> +++ b/target/riscv/kvm/kvm_riscv.h
> @@ -23,10 +23,11 @@
>   
>   void kvm_riscv_reset_vcpu(RISCVCPU *cpu);
>   void kvm_riscv_set_irq(RISCVCPU *cpu, int irq, int level);
> -void kvm_riscv_aia_create(MachineState *machine, uint64_t group_shift,
> -                          uint64_t aia_irq_num, uint64_t aia_msi_num,
> -                          uint64_t aplic_base, uint64_t imsic_base,
> -                          uint64_t guest_num);
> +int kvm_riscv_aia_create(uint64_t aia_irq_num, uint32_t *aia_msi_num);
> +void kvm_riscv_aia_init(MachineState *machine, int aia_fd,
> +                        uint64_t group_shift, uint64_t aia_irq_num,
> +                        uint64_t aplic_base, uint64_t imsic_base,
> +                        uint64_t guest_num);
>   void riscv_kvm_aplic_request(void *opaque, int irq, int level);
>   bool kvm_riscv_has_mp_state(void);
>   void riscv_kvm_cpu_finalize_features(RISCVCPU *cpu, Error **errp);