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