Add the shared CSR window and backing register storage. Keep mutable
state in a resettable CSR bank device so later controllers can route
their registers through it.
---
hw/mips/octeon.c | 146 ++++++++++++++++++++++++++++++++++++++
hw/mips/octeon_internal.h | 7 ++
2 files changed, 153 insertions(+)
diff --git a/hw/mips/octeon.c b/hw/mips/octeon.c
index 638d96559f..0caf088adf 100644
--- a/hw/mips/octeon.c
+++ b/hw/mips/octeon.c
@@ -41,6 +41,8 @@ OBJECT_DECLARE_TYPE(OcteonPeripheralState,
OcteonPeripheralClass,
OBJECT_DECLARE_SIMPLE_TYPE(OcteonRstState, OCTEON_RST)
#define TYPE_OCTEON_INTC "octeon-intc"
OBJECT_DECLARE_SIMPLE_TYPE(OcteonIntcState, OCTEON_INTC)
+#define TYPE_OCTEON_CSR_BANK "octeon-csr-bank"
+OBJECT_DECLARE_SIMPLE_TYPE(OcteonCsrBankState, OCTEON_CSR_BANK)
#define OCTEON_CSR_64BIT_SIZE 8
@@ -120,6 +122,8 @@ OBJECT_DECLARE_SIMPLE_TYPE(OcteonIntcState, OCTEON_INTC)
#define OCTEON_CIU3_ISC_SIZE (OCTEON_CIU3_NINTSN * 8)
#define OCTEON_CIU3_MBOX_INTSN(cpu) ((cpu) + 0x4000U)
#define OCTEON_CIU3_UART0_INTSN 0x8000
+#define OCTEON_CSR_BASE 0x1180000000000ULL
+#define OCTEON_CSR_SIZE 0x100000000ULL
/*
* QEMU-only backing for per-core CVMSEG. Keep it outside guest DRAM;
* the guest sees the virtual CVMSEG window, not this physical address.
@@ -187,6 +191,24 @@ struct OcteonIntcState {
uint8_t ciu3_irq_line[OCTEON_MAX_CPUS][OCTEON_CIU3_CP0_IRQ_COUNT];
};
+struct OcteonCsrBankState {
+ OcteonPeripheralState parent_obj;
+ MemoryRegion csr;
+ GHashTable *values;
+};
+
+guint octeon_uint64_hash(gconstpointer v)
+{
+ uint64_t value = *(const uint64_t *)v;
+
+ return value ^ (value >> 32);
+}
+
+gboolean octeon_uint64_equal(gconstpointer a, gconstpointer b)
+{
+ return *(const uint64_t *)a == *(const uint64_t *)b;
+}
+
uint64_t octeon_read64(uint64_t value, hwaddr addr, unsigned size)
{
if (size == 4) {
@@ -248,6 +270,38 @@ static void octeon_validate_clocks(OcteonMachineState *oms)
}
}
+bool octeon_reg_lookup(GHashTable *regs, uint64_t reg, uint64_t *value)
+{
+ uint64_t *stored = g_hash_table_lookup(regs, ®);
+
+ if (!stored) {
+ return false;
+ }
+
+ *value = *stored;
+ return true;
+}
+
+void octeon_reg_store(GHashTable *regs, uint64_t reg, uint64_t value)
+{
+ uint64_t *key = g_new(uint64_t, 1);
+ uint64_t *stored = g_new(uint64_t, 1);
+
+ *key = reg;
+ *stored = value;
+ g_hash_table_replace(regs, key, stored);
+}
+
+bool octeon_csr_lookup(OcteonState *s, uint64_t reg, uint64_t *value)
+{
+ return octeon_reg_lookup(s->csr_bank->values, reg, value);
+}
+
+static void octeon_csr_store(OcteonState *s, uint64_t reg, uint64_t value)
+{
+ octeon_reg_store(s->csr_bank->values, reg, value);
+}
+
static bool octeon_ciu3_source_from_intsn(uint32_t intsn,
unsigned int *source)
{
@@ -1022,6 +1076,42 @@ static const MemoryRegionOps octeon_ciu3_ops = {
},
};
+static uint64_t octeon_csr_read(void *opaque, hwaddr addr, unsigned size)
+{
+ OcteonState *s = opaque;
+ hwaddr reg = addr & ~7ULL;
+ uint64_t value;
+
+ if (!octeon_csr_lookup(s, reg, &value)) {
+ value = 0;
+ }
+ return octeon_read64(value, addr, size);
+}
+
+static void octeon_csr_write(void *opaque, hwaddr addr,
+ uint64_t value, unsigned size)
+{
+ OcteonState *s = opaque;
+ hwaddr reg = addr & ~7ULL;
+ uint64_t old;
+
+ if (!octeon_csr_lookup(s, reg, &old)) {
+ old = 0;
+ }
+ value = octeon_write64(old, addr, value, size);
+ octeon_csr_store(s, reg, value);
+}
+
+static const MemoryRegionOps octeon_csr_ops = {
+ .read = octeon_csr_read,
+ .write = octeon_csr_write,
+ .endianness = DEVICE_BIG_ENDIAN,
+ .valid = {
+ .min_access_size = 4,
+ .max_access_size = 8,
+ },
+};
+
void octeon_irq_set(void *opaque, int irq, int level)
{
OcteonState *s = opaque;
@@ -1294,6 +1384,18 @@ static void octeon_intc_reset_hold(Object *obj,
ResetType type)
octeon_intc_update_all_cpus(s);
}
+static void octeon_csr_bank_reset_hold(Object *obj, ResetType type)
+{
+ OcteonPeripheralClass *opc = OCTEON_PERIPHERAL_GET_CLASS(obj);
+ OcteonCsrBankState *bank = OCTEON_CSR_BANK(obj);
+
+ if (opc->parent_phases.hold) {
+ opc->parent_phases.hold(obj, type);
+ }
+
+ g_hash_table_remove_all(bank->values);
+}
+
static void octeon_peripheral_class_init(ObjectClass *klass,
const void *data)
{
@@ -1302,6 +1404,23 @@ static void octeon_peripheral_class_init(ObjectClass
*klass,
dc->user_creatable = false;
}
+static GHashTable *octeon_reg_table_new(void)
+{
+ return g_hash_table_new_full(octeon_uint64_hash,
+ octeon_uint64_equal,
+ g_free, g_free);
+}
+
+static void octeon_csr_bank_init(Object *obj)
+{
+ OCTEON_CSR_BANK(obj)->values = octeon_reg_table_new();
+}
+
+static void octeon_csr_bank_finalize(Object *obj)
+{
+ g_hash_table_destroy(OCTEON_CSR_BANK(obj)->values);
+}
+
static void octeon_rst_class_init(ObjectClass *klass, const void *data)
{
OcteonPeripheralClass *opc = OCTEON_PERIPHERAL_CLASS(klass);
@@ -1320,6 +1439,16 @@ static void octeon_intc_class_init(ObjectClass *klass,
const void *data)
NULL, &opc->parent_phases);
}
+static void octeon_csr_bank_class_init(ObjectClass *klass, const void *data)
+{
+ OcteonPeripheralClass *opc = OCTEON_PERIPHERAL_CLASS(klass);
+ ResettableClass *rc = RESETTABLE_CLASS(klass);
+
+ resettable_class_set_parent_phases(rc, NULL,
+ octeon_csr_bank_reset_hold, NULL,
+ &opc->parent_phases);
+}
+
static OcteonPeripheralState *octeon_new_peripheral(OcteonState *s,
const char *type,
unsigned int index)
@@ -1336,6 +1465,8 @@ static void octeon_create_peripherals(OcteonState *s)
{
s->rst = OCTEON_RST(octeon_new_peripheral(s, TYPE_OCTEON_RST, 0));
s->intc = OCTEON_INTC(octeon_new_peripheral(s, TYPE_OCTEON_INTC, 0));
+ s->csr_bank = OCTEON_CSR_BANK(octeon_new_peripheral(
+ s, TYPE_OCTEON_CSR_BANK, 0));
}
static void octeon_realize_peripheral(OcteonPeripheralState *dev)
@@ -1347,6 +1478,7 @@ static void octeon_realize_peripherals(OcteonState *s)
{
octeon_realize_peripheral(&s->intc->parent_obj);
octeon_realize_peripheral(&s->rst->parent_obj);
+ octeon_realize_peripheral(&s->csr_bank->parent_obj);
}
static void mips_octeon_init(MachineState *machine)
@@ -1386,6 +1518,12 @@ static void mips_octeon_init(MachineState *machine)
memory_region_add_subregion(get_system_memory(), OCTEON_CIU3_BASE,
&s->intc->ciu3);
+ memory_region_init_io(&s->csr_bank->csr, OBJECT(s->csr_bank),
+ &octeon_csr_ops, s,
+ "octeon.csr", OCTEON_CSR_SIZE);
+ memory_region_add_subregion_overlap(get_system_memory(), OCTEON_CSR_BASE,
+ &s->csr_bank->csr, -1);
+
octeon_realize_peripherals(s);
}
@@ -1464,6 +1602,14 @@ static const TypeInfo octeon_machine_types[] = {
.instance_size = sizeof(OcteonIntcState),
.class_init = octeon_intc_class_init,
},
+ {
+ .name = TYPE_OCTEON_CSR_BANK,
+ .parent = TYPE_OCTEON_PERIPHERAL,
+ .instance_size = sizeof(OcteonCsrBankState),
+ .instance_init = octeon_csr_bank_init,
+ .instance_finalize = octeon_csr_bank_finalize,
+ .class_init = octeon_csr_bank_class_init,
+ },
{
.name = TYPE_OCTEON_MACHINE,
.parent = TYPE_MACHINE,
diff --git a/hw/mips/octeon_internal.h b/hw/mips/octeon_internal.h
index cd810f3dd5..6327103f52 100644
--- a/hw/mips/octeon_internal.h
+++ b/hw/mips/octeon_internal.h
@@ -32,6 +32,7 @@ typedef enum OcteonCiu3Source {
typedef struct OcteonState OcteonState;
typedef struct OcteonRstState OcteonRstState;
typedef struct OcteonIntcState OcteonIntcState;
+typedef struct OcteonCsrBankState OcteonCsrBankState;
typedef struct OcteonCPUState {
OcteonState *board;
@@ -58,11 +59,17 @@ struct OcteonState {
MemoryRegion cvmseg;
OcteonRstState *rst;
OcteonIntcState *intc;
+ OcteonCsrBankState *csr_bank;
};
+guint octeon_uint64_hash(gconstpointer v);
+gboolean octeon_uint64_equal(gconstpointer a, gconstpointer b);
uint64_t octeon_read64(uint64_t value, hwaddr addr, unsigned size);
uint64_t octeon_write64(uint64_t old, hwaddr addr,
uint64_t value, unsigned size);
+bool octeon_reg_lookup(GHashTable *regs, uint64_t reg, uint64_t *value);
+void octeon_reg_store(GHashTable *regs, uint64_t reg, uint64_t value);
+bool octeon_csr_lookup(OcteonState *s, uint64_t reg, uint64_t *value);
void octeon_irq_set(void *opaque, int irq, int level);
#endif
--
2.54.0