target/riscv: Create an KVM AIA irqchip
We create a vAIA chip by using the KVM_DEV_TYPE_RISCV_AIA and then set up the chip with the KVM_DEV_RISCV_AIA_GRP_* APIs. We also extend KVM accelerator to specify the KVM AIA mode. The "riscv-aia" parameter is passed along with --accel in QEMU command-line. 1) "riscv-aia=emul": IMSIC is emulated by hypervisor 2) "riscv-aia=hwaccel": use hardware guest IMSIC 3) "riscv-aia=auto": use the hardware guest IMSICs whenever available otherwise we fallback to software emulation. Signed-off-by: Yong-Xuan Wang <yongxuan.wang@sifive.com> Reviewed-by: Jim Shu <jim.shu@sifive.com> Reviewed-by: Daniel Henrique Barboza <dbarboza@ventanamicro.com> Reviewed-by: Andrew Jones <ajones@ventanamicro.com> Message-ID: <20230727102439.22554-4-yongxuan.wang@sifive.com> Signed-off-by: Alistair Francis <alistair.francis@wdc.com>
This commit is contained in:
parent
97b9f5ef14
commit
9634ef7eda
@ -36,6 +36,7 @@
|
||||
#include "exec/address-spaces.h"
|
||||
#include "hw/boards.h"
|
||||
#include "hw/irq.h"
|
||||
#include "hw/intc/riscv_imsic.h"
|
||||
#include "qemu/log.h"
|
||||
#include "hw/loader.h"
|
||||
#include "kvm_riscv.h"
|
||||
@ -43,6 +44,7 @@
|
||||
#include "chardev/char-fe.h"
|
||||
#include "migration/migration.h"
|
||||
#include "sysemu/runstate.h"
|
||||
#include "hw/riscv/numa.h"
|
||||
|
||||
static uint64_t kvm_riscv_reg_id(CPURISCVState *env, uint64_t type,
|
||||
uint64_t idx)
|
||||
@ -1035,6 +1037,190 @@ bool kvm_arch_cpu_check_are_resettable(void)
|
||||
return true;
|
||||
}
|
||||
|
||||
static int aia_mode;
|
||||
|
||||
static const char *kvm_aia_mode_str(uint64_t mode)
|
||||
{
|
||||
switch (mode) {
|
||||
case KVM_DEV_RISCV_AIA_MODE_EMUL:
|
||||
return "emul";
|
||||
case KVM_DEV_RISCV_AIA_MODE_HWACCEL:
|
||||
return "hwaccel";
|
||||
case KVM_DEV_RISCV_AIA_MODE_AUTO:
|
||||
default:
|
||||
return "auto";
|
||||
};
|
||||
}
|
||||
|
||||
static char *riscv_get_kvm_aia(Object *obj, Error **errp)
|
||||
{
|
||||
return g_strdup(kvm_aia_mode_str(aia_mode));
|
||||
}
|
||||
|
||||
static void riscv_set_kvm_aia(Object *obj, const char *val, Error **errp)
|
||||
{
|
||||
if (!strcmp(val, "emul")) {
|
||||
aia_mode = KVM_DEV_RISCV_AIA_MODE_EMUL;
|
||||
} else if (!strcmp(val, "hwaccel")) {
|
||||
aia_mode = KVM_DEV_RISCV_AIA_MODE_HWACCEL;
|
||||
} else if (!strcmp(val, "auto")) {
|
||||
aia_mode = KVM_DEV_RISCV_AIA_MODE_AUTO;
|
||||
} else {
|
||||
error_setg(errp, "Invalid KVM AIA mode");
|
||||
error_append_hint(errp, "Valid values are emul, hwaccel, and auto.\n");
|
||||
}
|
||||
}
|
||||
|
||||
void kvm_arch_accel_class_init(ObjectClass *oc)
|
||||
{
|
||||
object_class_property_add_str(oc, "riscv-aia", riscv_get_kvm_aia,
|
||||
riscv_set_kvm_aia);
|
||||
object_class_property_set_description(oc, "riscv-aia",
|
||||
"Set KVM AIA mode. Valid values are "
|
||||
"emul, hwaccel, and auto. Default "
|
||||
"is auto.");
|
||||
object_property_set_default_str(object_class_property_find(oc, "riscv-aia"),
|
||||
"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 ret, i;
|
||||
int aia_fd = -1;
|
||||
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;
|
||||
|
||||
aia_fd = kvm_create_device(kvm_state, KVM_DEV_TYPE_RISCV_AIA, false);
|
||||
|
||||
if (aia_fd < 0) {
|
||||
error_report("Unable to create in-kernel irqchip");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
ret = kvm_device_access(aia_fd, KVM_DEV_RISCV_AIA_GRP_CONFIG,
|
||||
KVM_DEV_RISCV_AIA_CONFIG_MODE,
|
||||
&default_aia_mode, false, NULL);
|
||||
if (ret < 0) {
|
||||
error_report("KVM AIA: failed to get current KVM AIA mode");
|
||||
exit(1);
|
||||
}
|
||||
qemu_log("KVM AIA: default mode is %s\n",
|
||||
kvm_aia_mode_str(default_aia_mode));
|
||||
|
||||
if (default_aia_mode != aia_mode) {
|
||||
ret = kvm_device_access(aia_fd, KVM_DEV_RISCV_AIA_GRP_CONFIG,
|
||||
KVM_DEV_RISCV_AIA_CONFIG_MODE,
|
||||
&aia_mode, true, NULL);
|
||||
if (ret < 0)
|
||||
warn_report("KVM AIA: failed to set KVM AIA mode");
|
||||
else
|
||||
qemu_log("KVM AIA: set current mode to %s\n",
|
||||
kvm_aia_mode_str(aia_mode));
|
||||
}
|
||||
|
||||
ret = kvm_device_access(aia_fd, KVM_DEV_RISCV_AIA_GRP_CONFIG,
|
||||
KVM_DEV_RISCV_AIA_CONFIG_SRCS,
|
||||
&aia_irq_num, true, NULL);
|
||||
if (ret < 0) {
|
||||
error_report("KVM AIA: failed to set number of input irq lines");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
socket_bits = find_last_bit(&socket_count, BITS_PER_LONG) + 1;
|
||||
ret = kvm_device_access(aia_fd, KVM_DEV_RISCV_AIA_GRP_CONFIG,
|
||||
KVM_DEV_RISCV_AIA_CONFIG_GROUP_BITS,
|
||||
&socket_bits, true, NULL);
|
||||
if (ret < 0) {
|
||||
error_report("KVM AIA: failed to set group_bits");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
ret = kvm_device_access(aia_fd, KVM_DEV_RISCV_AIA_GRP_CONFIG,
|
||||
KVM_DEV_RISCV_AIA_CONFIG_GROUP_SHIFT,
|
||||
&group_shift, true, NULL);
|
||||
if (ret < 0) {
|
||||
error_report("KVM AIA: failed to set group_shift");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
guest_bits = guest_num == 0 ? 0 :
|
||||
find_last_bit(&guest_num, BITS_PER_LONG) + 1;
|
||||
ret = kvm_device_access(aia_fd, KVM_DEV_RISCV_AIA_GRP_CONFIG,
|
||||
KVM_DEV_RISCV_AIA_CONFIG_GUEST_BITS,
|
||||
&guest_bits, true, NULL);
|
||||
if (ret < 0) {
|
||||
error_report("KVM AIA: failed to set guest_bits");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
ret = kvm_device_access(aia_fd, KVM_DEV_RISCV_AIA_GRP_ADDR,
|
||||
KVM_DEV_RISCV_AIA_ADDR_APLIC,
|
||||
&aplic_base, true, NULL);
|
||||
if (ret < 0) {
|
||||
error_report("KVM AIA: failed to set the base address of APLIC");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
for (socket = 0; socket < socket_count; socket++) {
|
||||
socket_imsic_base = imsic_base + socket * (1U << group_shift);
|
||||
hart_count = riscv_socket_hart_count(machine, socket);
|
||||
base_hart = riscv_socket_first_hartid(machine, socket);
|
||||
|
||||
if (max_hart_per_socket < hart_count) {
|
||||
max_hart_per_socket = hart_count;
|
||||
}
|
||||
|
||||
for (i = 0; i < hart_count; i++) {
|
||||
imsic_addr = socket_imsic_base + i * IMSIC_HART_SIZE(guest_bits);
|
||||
ret = kvm_device_access(aia_fd, KVM_DEV_RISCV_AIA_GRP_ADDR,
|
||||
KVM_DEV_RISCV_AIA_ADDR_IMSIC(i + base_hart),
|
||||
&imsic_addr, true, NULL);
|
||||
if (ret < 0) {
|
||||
error_report("KVM AIA: failed to set the IMSIC address for hart %d", i);
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
hart_bits = find_last_bit(&max_hart_per_socket, BITS_PER_LONG) + 1;
|
||||
ret = kvm_device_access(aia_fd, KVM_DEV_RISCV_AIA_GRP_CONFIG,
|
||||
KVM_DEV_RISCV_AIA_CONFIG_HART_BITS,
|
||||
&hart_bits, true, NULL);
|
||||
if (ret < 0) {
|
||||
error_report("KVM AIA: failed to set hart_bits");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
if (kvm_has_gsi_routing()) {
|
||||
for (uint64_t idx = 0; idx < aia_irq_num + 1; ++idx) {
|
||||
/* KVM AIA only has one APLIC instance */
|
||||
kvm_irqchip_add_irq_route(kvm_state, idx, 0, idx);
|
||||
}
|
||||
kvm_gsi_routing_allowed = true;
|
||||
kvm_irqchip_commit_routes(kvm_state);
|
||||
}
|
||||
|
||||
ret = kvm_device_access(aia_fd, KVM_DEV_RISCV_AIA_GRP_CTRL,
|
||||
KVM_DEV_RISCV_AIA_CTRL_INIT,
|
||||
NULL, true, NULL);
|
||||
if (ret < 0) {
|
||||
error_report("KVM AIA: initialized fail");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
kvm_msi_via_irqfd_allowed = kvm_irqfds_enabled();
|
||||
}
|
||||
|
@ -22,5 +22,9 @@
|
||||
void kvm_riscv_init_user_properties(Object *cpu_obj);
|
||||
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);
|
||||
|
||||
#endif
|
||||
|
Loading…
x
Reference in New Issue
Block a user