+#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;