Implement RESET_BASE register which is local to each VP and a write to
it changes VP's reset exception base. Also, add OTHER register to
allow a software running on one VP to access other VP's local registers.

Guest can use this mechanism to specify custom address from which a VP
will start execution.

Signed-off-by: Leon Alrae <leon.al...@imgtec.com>
---
 hw/misc/mips_cmgcr.c         | 54 +++++++++++++++++++++++++++++++++++++++++++-
 include/hw/misc/mips_cmgcr.h | 18 +++++++++++++++
 2 files changed, 71 insertions(+), 1 deletion(-)

diff --git a/hw/misc/mips_cmgcr.c b/hw/misc/mips_cmgcr.c
index e6cf17d..b3ba166 100644
--- a/hw/misc/mips_cmgcr.c
+++ b/hw/misc/mips_cmgcr.c
@@ -59,6 +59,8 @@ static inline void update_gic_base(MIPSGCRState *gcr, 
uint64_t val)
 static uint64_t gcr_read(void *opaque, hwaddr addr, unsigned size)
 {
     MIPSGCRState *gcr = (MIPSGCRState *) opaque;
+    MIPSGCRVPState *current_vps = &gcr->vps[current_cpu->cpu_index];
+    MIPSGCRVPState *other_vps = &gcr->vps[current_vps->other];
 
     switch (addr) {
     /* Global Control Block Register */
@@ -85,8 +87,14 @@ static uint64_t gcr_read(void *opaque, hwaddr addr, unsigned 
size)
     case MIPS_COCB_OFS + GCR_CL_CONFIG_OFS:
         /* Set PVP to # of VPs - 1 */
         return gcr->num_vps - 1;
+    case MIPS_CLCB_OFS + GCR_CL_RESETBASE_OFS:
+        return current_vps->reset_base;
+    case MIPS_COCB_OFS + GCR_CL_RESETBASE_OFS:
+        return other_vps->reset_base;
     case MIPS_CLCB_OFS + GCR_CL_OTHER_OFS:
-        return 0;
+        return current_vps->other;
+    case MIPS_COCB_OFS + GCR_CL_OTHER_OFS:
+        return other_vps->other;
     default:
         qemu_log_mask(LOG_UNIMP, "Read %d bytes at GCR offset 0x%" HWADDR_PRIx
                       "\n", size, addr);
@@ -95,10 +103,18 @@ static uint64_t gcr_read(void *opaque, hwaddr addr, 
unsigned size)
     return 0;
 }
 
+static inline target_ulong get_exception_base(MIPSGCRVPState *vps)
+{
+    /* TODO: BEV_BASE and SELECT_BEV */
+    return (int32_t)(vps->reset_base & GCR_CL_RESET_BASE_RESETBASE_MSK);
+}
+
 /* Write GCR registers */
 static void gcr_write(void *opaque, hwaddr addr, uint64_t data, unsigned size)
 {
     MIPSGCRState *gcr = (MIPSGCRState *)opaque;
+    MIPSGCRVPState *current_vps = &gcr->vps[current_cpu->cpu_index];
+    MIPSGCRVPState *other_vps = &gcr->vps[current_vps->other];
 
     switch (addr) {
     case GCR_GIC_BASE_OFS:
@@ -107,6 +123,26 @@ static void gcr_write(void *opaque, hwaddr addr, uint64_t 
data, unsigned size)
     case GCR_CPC_BASE_OFS:
         update_cpc_base(gcr, data);
         break;
+    case MIPS_CLCB_OFS + GCR_CL_RESETBASE_OFS:
+        current_vps->reset_base = data & GCR_CL_RESET_BASE_MSK;
+        cpu_set_exception_base(current_cpu->cpu_index,
+                               get_exception_base(current_vps));
+        break;
+    case MIPS_COCB_OFS + GCR_CL_RESETBASE_OFS:
+        other_vps->reset_base = data & GCR_CL_RESET_BASE_MSK;
+        cpu_set_exception_base(current_vps->other,
+                               get_exception_base(other_vps));
+        break;
+    case MIPS_CLCB_OFS + GCR_CL_OTHER_OFS:
+        if ((data & GCR_CL_OTHER_MSK) < gcr->num_vps) {
+            current_vps->other = data & GCR_CL_OTHER_MSK;
+        }
+        break;
+    case MIPS_COCB_OFS + GCR_CL_OTHER_OFS:
+        if ((data & GCR_CL_OTHER_MSK) < gcr->num_vps) {
+            other_vps->other = data & GCR_CL_OTHER_MSK;
+        }
+        break;
     default:
         qemu_log_mask(LOG_UNIMP, "Write %d bytes at GCR offset 0x%" HWADDR_PRIx
                       " 0x%" PRIx64 "\n", size, addr, data);
@@ -148,9 +184,16 @@ static void mips_gcr_init(Object *obj)
 static void mips_gcr_reset(DeviceState *dev)
 {
     MIPSGCRState *s = MIPS_GCR(dev);
+    int i;
 
     update_gic_base(s, 0);
     update_cpc_base(s, 0);
+
+    for (i = 0; i < s->num_vps; i++) {
+        s->vps[i].other = 0;
+        s->vps[i].reset_base = 0xBFC00000 & GCR_CL_RESET_BASE_MSK;
+        cpu_set_exception_base(i, get_exception_base(&s->vps[i]));
+    }
 }
 
 static const VMStateDescription vmstate_mips_gcr = {
@@ -170,12 +213,21 @@ static Property mips_gcr_properties[] = {
     DEFINE_PROP_END_OF_LIST(),
 };
 
+static void mips_gcr_realize(DeviceState *dev, Error **errp)
+{
+    MIPSGCRState *s = MIPS_GCR(dev);
+
+    /* Create local set of registers for each VP */
+    s->vps = g_new(MIPSGCRVPState, s->num_vps);
+}
+
 static void mips_gcr_class_init(ObjectClass *klass, void *data)
 {
     DeviceClass *dc = DEVICE_CLASS(klass);
     dc->props = mips_gcr_properties;
     dc->vmsd = &vmstate_mips_gcr;
     dc->reset = mips_gcr_reset;
+    dc->realize = mips_gcr_realize;
 }
 
 static const TypeInfo mips_gcr_info = {
diff --git a/include/hw/misc/mips_cmgcr.h b/include/hw/misc/mips_cmgcr.h
index 5b90e94..690e1d6 100644
--- a/include/hw/misc/mips_cmgcr.h
+++ b/include/hw/misc/mips_cmgcr.h
@@ -35,6 +35,7 @@
 /* Core Local and Core Other Block Register Map */
 #define GCR_CL_CONFIG_OFS   0x0010
 #define GCR_CL_OTHER_OFS    0x0018
+#define GCR_CL_RESETBASE_OFS 0x0020
 
 /* GCR_L2_CONFIG register fields */
 #define GCR_L2_CONFIG_BYPASS_SHF    20
@@ -50,6 +51,20 @@
 #define GCR_CPC_BASE_CPCBASE_MSK 0xFFFFFFFF8000ULL
 #define GCR_CPC_BASE_MSK (GCR_CPC_BASE_CPCEN_MSK | GCR_CPC_BASE_CPCBASE_MSK)
 
+/* GCR_CL_OTHER_OFS register fields */
+#define GCR_CL_OTHER_VPOTHER_MSK 0x7
+#define GCR_CL_OTHER_MSK GCR_CL_OTHER_VPOTHER_MSK
+
+/* GCR_CL_RESETBASE_OFS register fields */
+#define GCR_CL_RESET_BASE_RESETBASE_MSK 0xFFFFF000U
+#define GCR_CL_RESET_BASE_MSK GCR_CL_RESET_BASE_RESETBASE_MSK
+
+typedef struct MIPSGCRVPState MIPSGCRVPState;
+struct MIPSGCRVPState {
+    uint32_t other;
+    uint64_t reset_base;
+};
+
 typedef struct MIPSGCRState MIPSGCRState;
 struct MIPSGCRState {
     SysBusDevice parent_obj;
@@ -63,6 +78,9 @@ struct MIPSGCRState {
 
     uint64_t cpc_base;
     uint64_t gic_base;
+
+    /* VP Local/Other Registers */
+    MIPSGCRVPState *vps;
 };
 
 #endif /* _MIPS_GCR_H */
-- 
2.7.4


Reply via email to