MIPS patches 2016-03-29

Changes:
 * add initial MIPS CPS support
 * implement ITU block
 * implement MAAR
 -----BEGIN PGP SIGNATURE-----
 
 iQEcBAABAgAGBQJW+43VAAoJEFIRjjwLKdprLmoH/1iWT4WsUJDF+9KX7PpFANbQ
 DT+QSDBJr6K+jCenLlqfvB30txS+NDRFzmW65J8hlawVOwhamg1X+pcQTbAYy0sm
 Du3Wexye0uw5YKUmqK2oCrgLJCKm3AqsmraaITE8q1URlkrQpuOuzazlIx5UA+RW
 RgF/DsPAlit8TkZMHwaVIOeXUl8vl8152fU26QvwOGAT6J3lV+lQJ+gMPGRSAWOw
 dcuVGNOTV0g3+kzOWisiqZc/V0Wp2Yu5IPezEVkFjZ4iyTTpnR8gkzuTNebzoRZo
 Zmws4mqaZAX6ijveevd+ueh5sR+AX+mFBunoXQVSSBvRFlqBcEEL2nrwb9wOUX4=
 =Ns6n
 -----END PGP SIGNATURE-----

Merge remote-tracking branch 'remotes/lalrae/tags/mips-20160329-2' into staging

MIPS patches 2016-03-29

Changes:
* add initial MIPS CPS support
* implement ITU block
* implement MAAR

# gpg: Signature made Wed 30 Mar 2016 09:27:01 BST using RSA key ID 0B29DA6B
# gpg: Good signature from "Leon Alrae <leon.alrae@imgtec.com>"

* remotes/lalrae/tags/mips-20160329-2: (21 commits)
  target-mips: add MAAR, MAARI register
  target-mips: use CP0_CHECK for gen_m{f|t}hc0
  hw/mips/cps: enable ITU for multithreading processors
  target-mips: make ITC Configuration Tags accessible to the CPU
  target-mips: check CP0 enabled for CACHE instruction also in R6
  hw/mips: implement ITC Storage - Bypass View
  hw/mips: implement ITC Storage - P/V Sync and Try Views
  hw/mips: implement ITC Storage - Empty/Full Sync and Try Views
  hw/mips: implement ITC Storage - Control View
  hw/mips: implement ITC Configuration Tags and Storage Cells
  target-mips: enable CM GCR in MIPS64R6-generic CPU
  hw/mips_malta: add CPS to Malta board
  hw/mips_malta: move CPU creation to a separate function
  hw/mips_malta: remove redundant irq and clock init
  hw/mips_malta: remove CPUMIPSState from the write_bootloader()
  hw/mips/cps: create CPC block inside CPS
  hw/mips: add initial Cluster Power Controller support
  hw/mips/cps: create GCR block inside CPS
  hw/mips: add initial Global Config Register support
  target-mips: add CMGCRBase register
  ...

Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
This commit is contained in:
Peter Maydell 2016-03-30 16:06:44 +01:00
commit 489ef4c810
18 changed files with 1623 additions and 79 deletions

View File

@ -30,3 +30,5 @@ CONFIG_I8259=y
CONFIG_MC146818RTC=y
CONFIG_ISA_TESTDEV=y
CONFIG_EMPTY_SLOT=y
CONFIG_MIPS_CPS=y
CONFIG_MIPS_ITU=y

View File

@ -3,3 +3,4 @@ obj-y += addr.o cputimer.o mips_int.o
obj-$(CONFIG_JAZZ) += mips_jazz.o
obj-$(CONFIG_FULONG) += mips_fulong2e.o
obj-y += gt64xxx_pci.o
obj-$(CONFIG_MIPS_CPS) += cps.o

180
hw/mips/cps.c Normal file
View File

@ -0,0 +1,180 @@
/*
* Coherent Processing System emulation.
*
* Copyright (c) 2016 Imagination Technologies
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, see <http://www.gnu.org/licenses/>.
*/
#include "qemu/osdep.h"
#include "qapi/error.h"
#include "hw/mips/cps.h"
#include "hw/mips/mips.h"
#include "hw/mips/cpudevs.h"
#include "sysemu/kvm.h"
qemu_irq get_cps_irq(MIPSCPSState *s, int pin_number)
{
MIPSCPU *cpu = MIPS_CPU(first_cpu);
CPUMIPSState *env = &cpu->env;
assert(pin_number < s->num_irq);
/* TODO: return GIC pins once implemented */
return env->irq[pin_number];
}
static void mips_cps_init(Object *obj)
{
SysBusDevice *sbd = SYS_BUS_DEVICE(obj);
MIPSCPSState *s = MIPS_CPS(obj);
/* Cover entire address space as there do not seem to be any
* constraints for the base address of CPC and GIC. */
memory_region_init(&s->container, obj, "mips-cps-container", UINT64_MAX);
sysbus_init_mmio(sbd, &s->container);
}
static void main_cpu_reset(void *opaque)
{
MIPSCPU *cpu = opaque;
CPUState *cs = CPU(cpu);
cpu_reset(cs);
/* All VPs are halted on reset. Leave powering up to CPC. */
cs->halted = 1;
}
static bool cpu_mips_itu_supported(CPUMIPSState *env)
{
bool is_mt = (env->CP0_Config5 & (1 << CP0C5_VP)) ||
(env->CP0_Config3 & (1 << CP0C3_MT));
return is_mt && !kvm_enabled();
}
static void mips_cps_realize(DeviceState *dev, Error **errp)
{
MIPSCPSState *s = MIPS_CPS(dev);
CPUMIPSState *env;
MIPSCPU *cpu;
int i;
Error *err = NULL;
target_ulong gcr_base;
bool itu_present = false;
for (i = 0; i < s->num_vp; i++) {
cpu = cpu_mips_init(s->cpu_model);
if (cpu == NULL) {
error_setg(errp, "%s: CPU initialization failed\n", __func__);
return;
}
env = &cpu->env;
/* Init internal devices */
cpu_mips_irq_init_cpu(env);
cpu_mips_clock_init(env);
if (cpu_mips_itu_supported(env)) {
itu_present = true;
/* Attach ITC Tag to the VP */
env->itc_tag = mips_itu_get_tag_region(&s->itu);
}
qemu_register_reset(main_cpu_reset, cpu);
}
cpu = MIPS_CPU(first_cpu);
env = &cpu->env;
/* Inter-Thread Communication Unit */
if (itu_present) {
object_initialize(&s->itu, sizeof(s->itu), TYPE_MIPS_ITU);
qdev_set_parent_bus(DEVICE(&s->itu), sysbus_get_default());
object_property_set_int(OBJECT(&s->itu), 16, "num-fifo", &err);
object_property_set_int(OBJECT(&s->itu), 16, "num-semaphores", &err);
object_property_set_bool(OBJECT(&s->itu), true, "realized", &err);
if (err != NULL) {
error_propagate(errp, err);
return;
}
memory_region_add_subregion(&s->container, 0,
sysbus_mmio_get_region(SYS_BUS_DEVICE(&s->itu), 0));
}
/* Cluster Power Controller */
object_initialize(&s->cpc, sizeof(s->cpc), TYPE_MIPS_CPC);
qdev_set_parent_bus(DEVICE(&s->cpc), sysbus_get_default());
object_property_set_int(OBJECT(&s->cpc), s->num_vp, "num-vp", &err);
object_property_set_int(OBJECT(&s->cpc), 1, "vp-start-running", &err);
object_property_set_bool(OBJECT(&s->cpc), true, "realized", &err);
if (err != NULL) {
error_propagate(errp, err);
return;
}
memory_region_add_subregion(&s->container, 0,
sysbus_mmio_get_region(SYS_BUS_DEVICE(&s->cpc), 0));
/* Global Configuration Registers */
gcr_base = env->CP0_CMGCRBase << 4;
object_initialize(&s->gcr, sizeof(s->gcr), TYPE_MIPS_GCR);
qdev_set_parent_bus(DEVICE(&s->gcr), sysbus_get_default());
object_property_set_int(OBJECT(&s->gcr), s->num_vp, "num-vp", &err);
object_property_set_int(OBJECT(&s->gcr), 0x800, "gcr-rev", &err);
object_property_set_int(OBJECT(&s->gcr), gcr_base, "gcr-base", &err);
object_property_set_link(OBJECT(&s->gcr), OBJECT(&s->cpc.mr), "cpc", &err);
object_property_set_bool(OBJECT(&s->gcr), true, "realized", &err);
if (err != NULL) {
error_propagate(errp, err);
return;
}
memory_region_add_subregion(&s->container, gcr_base,
sysbus_mmio_get_region(SYS_BUS_DEVICE(&s->gcr), 0));
}
static Property mips_cps_properties[] = {
DEFINE_PROP_UINT32("num-vp", MIPSCPSState, num_vp, 1),
DEFINE_PROP_UINT32("num-irq", MIPSCPSState, num_irq, 8),
DEFINE_PROP_STRING("cpu-model", MIPSCPSState, cpu_model),
DEFINE_PROP_END_OF_LIST()
};
static void mips_cps_class_init(ObjectClass *klass, void *data)
{
DeviceClass *dc = DEVICE_CLASS(klass);
dc->realize = mips_cps_realize;
dc->props = mips_cps_properties;
}
static const TypeInfo mips_cps_info = {
.name = TYPE_MIPS_CPS,
.parent = TYPE_SYS_BUS_DEVICE,
.instance_size = sizeof(MIPSCPSState),
.instance_init = mips_cps_init,
.class_init = mips_cps_class_init,
};
static void mips_cps_register_types(void)
{
type_register_static(&mips_cps_info);
}
type_init(mips_cps_register_types)

View File

@ -57,6 +57,7 @@
#include "hw/empty_slot.h"
#include "sysemu/kvm.h"
#include "exec/semihost.h"
#include "hw/mips/cps.h"
//#define DEBUG_BOARD_INIT
@ -95,6 +96,7 @@ typedef struct {
typedef struct {
SysBusDevice parent_obj;
MIPSCPSState *cps;
qemu_irq *i8259;
} MaltaState;
@ -608,8 +610,8 @@ static void network_init(PCIBus *pci_bus)
a3 - RAM size in bytes
*/
static void write_bootloader (CPUMIPSState *env, uint8_t *base,
int64_t run_addr, int64_t kernel_entry)
static void write_bootloader(uint8_t *base, int64_t run_addr,
int64_t kernel_entry)
{
uint32_t *p;
@ -908,12 +910,81 @@ static void main_cpu_reset(void *opaque)
}
}
static void create_cpu_without_cps(const char *cpu_model,
qemu_irq *cbus_irq, qemu_irq *i8259_irq)
{
CPUMIPSState *env;
MIPSCPU *cpu;
int i;
for (i = 0; i < smp_cpus; i++) {
cpu = cpu_mips_init(cpu_model);
if (cpu == NULL) {
fprintf(stderr, "Unable to find CPU definition\n");
exit(1);
}
env = &cpu->env;
/* Init internal devices */
cpu_mips_irq_init_cpu(env);
cpu_mips_clock_init(env);
qemu_register_reset(main_cpu_reset, cpu);
}
cpu = MIPS_CPU(first_cpu);
env = &cpu->env;
*i8259_irq = env->irq[2];
*cbus_irq = env->irq[4];
}
static void create_cps(MaltaState *s, const char *cpu_model,
qemu_irq *cbus_irq, qemu_irq *i8259_irq)
{
Error *err = NULL;
s->cps = g_new0(MIPSCPSState, 1);
object_initialize(s->cps, sizeof(MIPSCPSState), TYPE_MIPS_CPS);
qdev_set_parent_bus(DEVICE(s->cps), sysbus_get_default());
object_property_set_str(OBJECT(s->cps), cpu_model, "cpu-model", &err);
object_property_set_int(OBJECT(s->cps), smp_cpus, "num-vp", &err);
object_property_set_bool(OBJECT(s->cps), true, "realized", &err);
if (err != NULL) {
error_report("%s", error_get_pretty(err));
exit(1);
}
sysbus_mmio_map_overlap(SYS_BUS_DEVICE(s->cps), 0, 0, 1);
/* FIXME: When GIC is present then we should use GIC's IRQ 3.
Until then CPS exposes CPU's IRQs thus use the default IRQ 2. */
*i8259_irq = get_cps_irq(s->cps, 2);
*cbus_irq = NULL;
}
static void create_cpu(MaltaState *s, const char *cpu_model,
qemu_irq *cbus_irq, qemu_irq *i8259_irq)
{
if (cpu_model == NULL) {
#ifdef TARGET_MIPS64
cpu_model = "20Kc";
#else
cpu_model = "24Kf";
#endif
}
if ((smp_cpus > 1) && cpu_supports_cps_smp(cpu_model)) {
create_cps(s, cpu_model, cbus_irq, i8259_irq);
} else {
create_cpu_without_cps(cpu_model, cbus_irq, i8259_irq);
}
}
static
void mips_malta_init(MachineState *machine)
{
ram_addr_t ram_size = machine->ram_size;
ram_addr_t ram_low_size;
const char *cpu_model = machine->cpu_model;
const char *kernel_filename = machine->kernel_filename;
const char *kernel_cmdline = machine->kernel_cmdline;
const char *initrd_filename = machine->initrd_filename;
@ -930,9 +1001,8 @@ void mips_malta_init(MachineState *machine)
int64_t kernel_entry, bootloader_run_addr;
PCIBus *pci_bus;
ISABus *isa_bus;
MIPSCPU *cpu;
CPUMIPSState *env;
qemu_irq *isa_irq;
qemu_irq cbus_irq, i8259_irq;
int piix4_devfn;
I2CBus *smbus;
int i;
@ -962,30 +1032,8 @@ void mips_malta_init(MachineState *machine)
}
}
/* init CPUs */
if (cpu_model == NULL) {
#ifdef TARGET_MIPS64
cpu_model = "20Kc";
#else
cpu_model = "24Kf";
#endif
}
for (i = 0; i < smp_cpus; i++) {
cpu = cpu_mips_init(cpu_model);
if (cpu == NULL) {
fprintf(stderr, "Unable to find CPU definition\n");
exit(1);
}
env = &cpu->env;
/* Init internal devices */
cpu_mips_irq_init_cpu(env);
cpu_mips_clock_init(env);
qemu_register_reset(main_cpu_reset, cpu);
}
cpu = MIPS_CPU(first_cpu);
env = &cpu->env;
/* create CPU */
create_cpu(s, machine->cpu_model, &cbus_irq, &i8259_irq);
/* allocate RAM */
if (ram_size > (2048u << 20)) {
@ -1026,7 +1074,7 @@ void mips_malta_init(MachineState *machine)
#endif
/* FPGA */
/* The CBUS UART is attached to the MIPS CPU INT2 pin, ie interrupt 4 */
malta_fpga_init(system_memory, FPGA_ADDRESS, env->irq[4], serial_hds[2]);
malta_fpga_init(system_memory, FPGA_ADDRESS, cbus_irq, serial_hds[2]);
/* Load firmware in flash / BIOS. */
dinfo = drive_get(IF_PFLASH, 0, fl_idx);
@ -1063,11 +1111,11 @@ void mips_malta_init(MachineState *machine)
loaderparams.initrd_filename = initrd_filename;
kernel_entry = load_kernel();
write_bootloader(env, memory_region_get_ram_ptr(bios),
write_bootloader(memory_region_get_ram_ptr(bios),
bootloader_run_addr, kernel_entry);
if (kvm_enabled()) {
/* Write the bootloader code @ the end of RAM, 1MB reserved */
write_bootloader(env, memory_region_get_ram_ptr(ram_low_preio) +
write_bootloader(memory_region_get_ram_ptr(ram_low_preio) +
ram_low_size,
bootloader_run_addr, kernel_entry);
}
@ -1135,10 +1183,6 @@ void mips_malta_init(MachineState *machine)
/* Board ID = 0x420 (Malta Board with CoreLV) */
stl_p(memory_region_get_ram_ptr(bios_copy) + 0x10, 0x00000420);
/* Init internal devices */
cpu_mips_irq_init_cpu(env);
cpu_mips_clock_init(env);
/*
* We have a circular dependency problem: pci_bus depends on isa_irq,
* isa_irq is provided by i8259, i8259 depends on ISA, ISA depends
@ -1158,7 +1202,7 @@ void mips_malta_init(MachineState *machine)
/* Interrupt controller */
/* The 8259 is attached to the MIPS CPU INT0 pin, ie interrupt 2 */
s->i8259 = i8259_init(isa_bus, env->irq[2]);
s->i8259 = i8259_init(isa_bus, i8259_irq);
isa_bus_irqs(isa_bus, s->i8259);
pci_piix4_ide_init(pci_bus, hd, piix4_devfn + 1);

View File

@ -43,6 +43,9 @@ obj-$(CONFIG_SLAVIO) += slavio_misc.o
obj-$(CONFIG_ZYNQ) += zynq_slcr.o
obj-$(CONFIG_ZYNQ) += zynq-xadc.o
obj-$(CONFIG_STM32F2XX_SYSCFG) += stm32f2xx_syscfg.o
obj-$(CONFIG_MIPS_CPS) += mips_cmgcr.o
obj-$(CONFIG_MIPS_CPS) += mips_cpc.o
obj-$(CONFIG_MIPS_ITU) += mips_itu.o
obj-$(CONFIG_PVPANIC) += pvpanic.o
obj-$(CONFIG_EDU) += edu.o

160
hw/misc/mips_cmgcr.c Normal file
View File

@ -0,0 +1,160 @@
/*
* This file is subject to the terms and conditions of the GNU General Public
* License. See the file "COPYING" in the main directory of this archive
* for more details.
*
* Copyright (C) 2012 MIPS Technologies, Inc. All rights reserved.
* Authors: Sanjay Lal <sanjayl@kymasys.com>
*
* Copyright (C) 2015 Imagination Technologies
*/
#include "qemu/osdep.h"
#include "qapi/error.h"
#include "hw/hw.h"
#include "hw/sysbus.h"
#include "sysemu/sysemu.h"
#include "hw/misc/mips_cmgcr.h"
#include "hw/misc/mips_cpc.h"
static inline bool is_cpc_connected(MIPSGCRState *s)
{
return s->cpc_mr != NULL;
}
static inline void update_cpc_base(MIPSGCRState *gcr, uint64_t val)
{
if (is_cpc_connected(gcr)) {
gcr->cpc_base = val & GCR_CPC_BASE_MSK;
memory_region_transaction_begin();
memory_region_set_address(gcr->cpc_mr,
gcr->cpc_base & GCR_CPC_BASE_CPCBASE_MSK);
memory_region_set_enabled(gcr->cpc_mr,
gcr->cpc_base & GCR_CPC_BASE_CPCEN_MSK);
memory_region_transaction_commit();
}
}
/* Read GCR registers */
static uint64_t gcr_read(void *opaque, hwaddr addr, unsigned size)
{
MIPSGCRState *gcr = (MIPSGCRState *) opaque;
switch (addr) {
/* Global Control Block Register */
case GCR_CONFIG_OFS:
/* Set PCORES to 0 */
return 0;
case GCR_BASE_OFS:
return gcr->gcr_base;
case GCR_REV_OFS:
return gcr->gcr_rev;
case GCR_CPC_BASE_OFS:
return gcr->cpc_base;
case GCR_CPC_STATUS_OFS:
return is_cpc_connected(gcr);
case GCR_L2_CONFIG_OFS:
/* L2 BYPASS */
return GCR_L2_CONFIG_BYPASS_MSK;
/* Core-Local and Core-Other Control Blocks */
case MIPS_CLCB_OFS + GCR_CL_CONFIG_OFS:
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_OTHER_OFS:
return 0;
default:
qemu_log_mask(LOG_UNIMP, "Read %d bytes at GCR offset 0x%" HWADDR_PRIx
"\n", size, addr);
return 0;
}
return 0;
}
/* Write GCR registers */
static void gcr_write(void *opaque, hwaddr addr, uint64_t data, unsigned size)
{
MIPSGCRState *gcr = (MIPSGCRState *)opaque;
switch (addr) {
case GCR_CPC_BASE_OFS:
update_cpc_base(gcr, data);
break;
default:
qemu_log_mask(LOG_UNIMP, "Write %d bytes at GCR offset 0x%" HWADDR_PRIx
" 0x%" PRIx64 "\n", size, addr, data);
break;
}
}
static const MemoryRegionOps gcr_ops = {
.read = gcr_read,
.write = gcr_write,
.endianness = DEVICE_NATIVE_ENDIAN,
.impl = {
.max_access_size = 8,
},
};
static void mips_gcr_init(Object *obj)
{
SysBusDevice *sbd = SYS_BUS_DEVICE(obj);
MIPSGCRState *s = MIPS_GCR(obj);
object_property_add_link(obj, "cpc", TYPE_MEMORY_REGION,
(Object **)&s->cpc_mr,
qdev_prop_allow_set_link_before_realize,
OBJ_PROP_LINK_UNREF_ON_RELEASE,
&error_abort);
memory_region_init_io(&s->iomem, OBJECT(s), &gcr_ops, s,
"mips-gcr", GCR_ADDRSPACE_SZ);
sysbus_init_mmio(sbd, &s->iomem);
}
static void mips_gcr_reset(DeviceState *dev)
{
MIPSGCRState *s = MIPS_GCR(dev);
update_cpc_base(s, 0);
}
static const VMStateDescription vmstate_mips_gcr = {
.name = "mips-gcr",
.version_id = 0,
.minimum_version_id = 0,
.fields = (VMStateField[]) {
VMSTATE_UINT64(cpc_base, MIPSGCRState),
VMSTATE_END_OF_LIST()
},
};
static Property mips_gcr_properties[] = {
DEFINE_PROP_INT32("num-vp", MIPSGCRState, num_vps, 1),
DEFINE_PROP_INT32("gcr-rev", MIPSGCRState, gcr_rev, 0x800),
DEFINE_PROP_UINT64("gcr-base", MIPSGCRState, gcr_base, GCR_BASE_ADDR),
DEFINE_PROP_END_OF_LIST(),
};
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;
}
static const TypeInfo mips_gcr_info = {
.name = TYPE_MIPS_GCR,
.parent = TYPE_SYS_BUS_DEVICE,
.instance_size = sizeof(MIPSGCRState),
.instance_init = mips_gcr_init,
.class_init = mips_gcr_class_init,
};
static void mips_gcr_register_types(void)
{
type_register_static(&mips_gcr_info);
}
type_init(mips_gcr_register_types)

177
hw/misc/mips_cpc.c Normal file
View File

@ -0,0 +1,177 @@
/*
* Cluster Power Controller emulation
*
* Copyright (c) 2016 Imagination Technologies
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, see <http://www.gnu.org/licenses/>.
*/
#include "qemu/osdep.h"
#include "qapi/error.h"
#include "hw/sysbus.h"
#include "hw/misc/mips_cpc.h"
static inline uint64_t cpc_vp_run_mask(MIPSCPCState *cpc)
{
return (1ULL << cpc->num_vp) - 1;
}
static void cpc_run_vp(MIPSCPCState *cpc, uint64_t vp_run)
{
CPUState *cs = first_cpu;
CPU_FOREACH(cs) {
uint64_t i = 1ULL << cs->cpu_index;
if (i & vp_run & ~cpc->vp_running) {
cpu_interrupt(cs, CPU_INTERRUPT_WAKE);
cpc->vp_running |= i;
}
}
}
static void cpc_stop_vp(MIPSCPCState *cpc, uint64_t vp_stop)
{
CPUState *cs = first_cpu;
CPU_FOREACH(cs) {
uint64_t i = 1ULL << cs->cpu_index;
if (i & vp_stop & cpc->vp_running) {
cs->halted = 1;
cpu_reset_interrupt(cs, CPU_INTERRUPT_WAKE);
cpc->vp_running &= ~i;
}
}
}
static void cpc_write(void *opaque, hwaddr offset, uint64_t data,
unsigned size)
{
MIPSCPCState *s = opaque;
switch (offset) {
case CPC_CL_BASE_OFS + CPC_VP_RUN_OFS:
case CPC_CO_BASE_OFS + CPC_VP_RUN_OFS:
cpc_run_vp(s, data & cpc_vp_run_mask(s));
break;
case CPC_CL_BASE_OFS + CPC_VP_STOP_OFS:
case CPC_CO_BASE_OFS + CPC_VP_STOP_OFS:
cpc_stop_vp(s, data & cpc_vp_run_mask(s));
break;
default:
qemu_log_mask(LOG_UNIMP,
"%s: Bad offset 0x%x\n", __func__, (int)offset);
break;
}
return;
}
static uint64_t cpc_read(void *opaque, hwaddr offset, unsigned size)
{
MIPSCPCState *s = opaque;
switch (offset) {
case CPC_CL_BASE_OFS + CPC_VP_RUNNING_OFS:
case CPC_CO_BASE_OFS + CPC_VP_RUNNING_OFS:
return s->vp_running;
default:
qemu_log_mask(LOG_UNIMP,
"%s: Bad offset 0x%x\n", __func__, (int)offset);
return 0;
}
}
static const MemoryRegionOps cpc_ops = {
.read = cpc_read,
.write = cpc_write,
.endianness = DEVICE_NATIVE_ENDIAN,
.impl = {
.max_access_size = 8,
},
};
static void mips_cpc_init(Object *obj)
{
SysBusDevice *sbd = SYS_BUS_DEVICE(obj);
MIPSCPCState *s = MIPS_CPC(obj);
memory_region_init_io(&s->mr, OBJECT(s), &cpc_ops, s, "mips-cpc",
CPC_ADDRSPACE_SZ);
sysbus_init_mmio(sbd, &s->mr);
}
static void mips_cpc_realize(DeviceState *dev, Error **errp)
{
MIPSCPCState *s = MIPS_CPC(dev);
if (s->vp_start_running > cpc_vp_run_mask(s)) {
error_setg(errp,
"incorrect vp_start_running 0x%" PRIx64 " for num_vp = %d",
s->vp_running, s->num_vp);
return;
}
}
static void mips_cpc_reset(DeviceState *dev)
{
MIPSCPCState *s = MIPS_CPC(dev);
/* Reflect the fact that all VPs are halted on reset */
s->vp_running = 0;
/* Put selected VPs into run state */
cpc_run_vp(s, s->vp_start_running);
}
static const VMStateDescription vmstate_mips_cpc = {
.name = "mips-cpc",
.version_id = 0,
.minimum_version_id = 0,
.fields = (VMStateField[]) {
VMSTATE_UINT64(vp_running, MIPSCPCState),
VMSTATE_END_OF_LIST()
},
};
static Property mips_cpc_properties[] = {
DEFINE_PROP_UINT32("num-vp", MIPSCPCState, num_vp, 0x1),
DEFINE_PROP_UINT64("vp-start-running", MIPSCPCState, vp_start_running, 0x1),
DEFINE_PROP_END_OF_LIST(),
};
static void mips_cpc_class_init(ObjectClass *klass, void *data)
{
DeviceClass *dc = DEVICE_CLASS(klass);
dc->realize = mips_cpc_realize;
dc->reset = mips_cpc_reset;
dc->vmsd = &vmstate_mips_cpc;
dc->props = mips_cpc_properties;
}
static const TypeInfo mips_cpc_info = {
.name = TYPE_MIPS_CPC,
.parent = TYPE_SYS_BUS_DEVICE,
.instance_size = sizeof(MIPSCPCState),
.instance_init = mips_cpc_init,
.class_init = mips_cpc_class_init,
};
static void mips_cpc_register_types(void)
{
type_register_static(&mips_cpc_info);
}
type_init(mips_cpc_register_types)

526
hw/misc/mips_itu.c Normal file
View File

@ -0,0 +1,526 @@
/*
* Inter-Thread Communication Unit emulation.
*
* Copyright (c) 2016 Imagination Technologies
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, see <http://www.gnu.org/licenses/>.
*/
#include "qemu/osdep.h"
#include "qapi/error.h"
#include "hw/hw.h"
#include "hw/sysbus.h"
#include "sysemu/sysemu.h"
#include "hw/misc/mips_itu.h"
#define ITC_TAG_ADDRSPACE_SZ (ITC_ADDRESSMAP_NUM * 8)
/* Initialize as 4kB area to fit all 32 cells with default 128B grain.
Storage may be resized by the software. */
#define ITC_STORAGE_ADDRSPACE_SZ 0x1000
#define ITC_FIFO_NUM_MAX 16
#define ITC_SEMAPH_NUM_MAX 16
#define ITC_AM1_NUMENTRIES_OFS 20
#define ITC_CELL_PV_MAX_VAL 0xFFFF
#define ITC_CELL_TAG_FIFO_DEPTH 28
#define ITC_CELL_TAG_FIFO_PTR 18
#define ITC_CELL_TAG_FIFO 17
#define ITC_CELL_TAG_T 16
#define ITC_CELL_TAG_F 1
#define ITC_CELL_TAG_E 0
#define ITC_AM0_BASE_ADDRESS_MASK 0xFFFFFC00ULL
#define ITC_AM0_EN_MASK 0x1
#define ITC_AM1_ADDR_MASK_MASK 0x1FC00
#define ITC_AM1_ENTRY_GRAIN_MASK 0x7
typedef enum ITCView {
ITCVIEW_BYPASS = 0,
ITCVIEW_CONTROL = 1,
ITCVIEW_EF_SYNC = 2,
ITCVIEW_EF_TRY = 3,
ITCVIEW_PV_SYNC = 4,
ITCVIEW_PV_TRY = 5
} ITCView;
MemoryRegion *mips_itu_get_tag_region(MIPSITUState *itu)
{
return &itu->tag_io;
}
static uint64_t itc_tag_read(void *opaque, hwaddr addr, unsigned size)
{
MIPSITUState *tag = (MIPSITUState *)opaque;
uint64_t index = addr >> 3;
uint64_t ret = 0;
switch (index) {
case 0 ... ITC_ADDRESSMAP_NUM:
ret = tag->ITCAddressMap[index];
break;
default:
qemu_log_mask(LOG_GUEST_ERROR, "Read 0x%" PRIx64 "\n", addr);
break;
}
return ret;
}
static void itc_reconfigure(MIPSITUState *tag)
{
uint64_t *am = &tag->ITCAddressMap[0];
MemoryRegion *mr = &tag->storage_io;
hwaddr address = am[0] & ITC_AM0_BASE_ADDRESS_MASK;
uint64_t size = (1 << 10) + (am[1] & ITC_AM1_ADDR_MASK_MASK);
bool is_enabled = (am[0] & ITC_AM0_EN_MASK) != 0;
memory_region_transaction_begin();
if (!(size & (size - 1))) {
memory_region_set_size(mr, size);
}
memory_region_set_address(mr, address);
memory_region_set_enabled(mr, is_enabled);
memory_region_transaction_commit();
}
static void itc_tag_write(void *opaque, hwaddr addr,
uint64_t data, unsigned size)
{
MIPSITUState *tag = (MIPSITUState *)opaque;
uint64_t *am = &tag->ITCAddressMap[0];
uint64_t am_old, mask;
uint64_t index = addr >> 3;
switch (index) {
case 0:
mask = ITC_AM0_BASE_ADDRESS_MASK | ITC_AM0_EN_MASK;
break;
case 1:
mask = ITC_AM1_ADDR_MASK_MASK | ITC_AM1_ENTRY_GRAIN_MASK;
break;
default:
qemu_log_mask(LOG_GUEST_ERROR, "Bad write 0x%" PRIx64 "\n", addr);
return;
}
am_old = am[index];
am[index] = (data & mask) | (am_old & ~mask);
if (am_old != am[index]) {
itc_reconfigure(tag);
}
}
static const MemoryRegionOps itc_tag_ops = {
.read = itc_tag_read,
.write = itc_tag_write,
.impl = {
.max_access_size = 8,
},
.endianness = DEVICE_NATIVE_ENDIAN,
};
static inline uint32_t get_num_cells(MIPSITUState *s)
{
return s->num_fifo + s->num_semaphores;
}
static inline ITCView get_itc_view(hwaddr addr)
{
return (addr >> 3) & 0xf;
}
static inline int get_cell_stride_shift(const MIPSITUState *s)
{
/* Minimum interval (for EntryGain = 0) is 128 B */
return 7 + (s->ITCAddressMap[1] & ITC_AM1_ENTRY_GRAIN_MASK);
}
static inline ITCStorageCell *get_cell(MIPSITUState *s,
hwaddr addr)
{
uint32_t cell_idx = addr >> get_cell_stride_shift(s);
uint32_t num_cells = get_num_cells(s);
if (cell_idx >= num_cells) {
cell_idx = num_cells - 1;
}
return &s->cell[cell_idx];
}
static void wake_blocked_threads(ITCStorageCell *c)
{
CPUState *cs;
CPU_FOREACH(cs) {
if (cs->halted && (c->blocked_threads & (1ULL << cs->cpu_index))) {
cpu_interrupt(cs, CPU_INTERRUPT_WAKE);
}
}
c->blocked_threads = 0;
}
static void QEMU_NORETURN block_thread_and_exit(ITCStorageCell *c)
{
c->blocked_threads |= 1ULL << current_cpu->cpu_index;
cpu_restore_state(current_cpu, current_cpu->mem_io_pc);
current_cpu->halted = 1;
current_cpu->exception_index = EXCP_HLT;
cpu_loop_exit(current_cpu);
}
/* ITC Bypass View */
static inline uint64_t view_bypass_read(ITCStorageCell *c)
{
if (c->tag.FIFO) {
return c->data[c->fifo_out];
} else {
return c->data[0];
}
}
static inline void view_bypass_write(ITCStorageCell *c, uint64_t val)
{
if (c->tag.FIFO && (c->tag.FIFOPtr > 0)) {
int idx = (c->fifo_out + c->tag.FIFOPtr - 1) % ITC_CELL_DEPTH;
c->data[idx] = val;
}
/* ignore a write to the semaphore cell */
}
/* ITC Control View */
static inline uint64_t view_control_read(ITCStorageCell *c)
{
return ((uint64_t)c->tag.FIFODepth << ITC_CELL_TAG_FIFO_DEPTH) |
(c->tag.FIFOPtr << ITC_CELL_TAG_FIFO_PTR) |
(c->tag.FIFO << ITC_CELL_TAG_FIFO) |
(c->tag.T << ITC_CELL_TAG_T) |
(c->tag.E << ITC_CELL_TAG_E) |
(c->tag.F << ITC_CELL_TAG_F);
}
static inline void view_control_write(ITCStorageCell *c, uint64_t val)
{
c->tag.T = (val >> ITC_CELL_TAG_T) & 1;
c->tag.E = (val >> ITC_CELL_TAG_E) & 1;
c->tag.F = (val >> ITC_CELL_TAG_F) & 1;
if (c->tag.E) {
c->tag.FIFOPtr = 0;
}
}
/* ITC Empty/Full View */
static uint64_t view_ef_common_read(ITCStorageCell *c, bool blocking)
{
uint64_t ret = 0;
if (!c->tag.FIFO) {
return 0;
}
c->tag.F = 0;
if (blocking && c->tag.E) {
block_thread_and_exit(c);
}
if (c->blocked_threads) {
wake_blocked_threads(c);
}
if (c->tag.FIFOPtr > 0) {
ret = c->data[c->fifo_out];
c->fifo_out = (c->fifo_out + 1) % ITC_CELL_DEPTH;
c->tag.FIFOPtr--;
}
if (c->tag.FIFOPtr == 0) {
c->tag.E = 1;
}
return ret;
}
static uint64_t view_ef_sync_read(ITCStorageCell *c)
{
return view_ef_common_read(c, true);
}
static uint64_t view_ef_try_read(ITCStorageCell *c)
{
return view_ef_common_read(c, false);
}
static inline void view_ef_common_write(ITCStorageCell *c, uint64_t val,
bool blocking)
{
if (!c->tag.FIFO) {
return;
}
c->tag.E = 0;
if (blocking && c->tag.F) {
block_thread_and_exit(c);
}
if (c->blocked_threads) {
wake_blocked_threads(c);
}
if (c->tag.FIFOPtr < ITC_CELL_DEPTH) {
int idx = (c->fifo_out + c->tag.FIFOPtr) % ITC_CELL_DEPTH;
c->data[idx] = val;
c->tag.FIFOPtr++;
}
if (c->tag.FIFOPtr == ITC_CELL_DEPTH) {
c->tag.F = 1;
}
}
static void view_ef_sync_write(ITCStorageCell *c, uint64_t val)
{
view_ef_common_write(c, val, true);
}
static void view_ef_try_write(ITCStorageCell *c, uint64_t val)
{
view_ef_common_write(c, val, false);
}
/* ITC P/V View */
static uint64_t view_pv_common_read(ITCStorageCell *c, bool blocking)
{
uint64_t ret = c->data[0];
if (c->tag.FIFO) {
return 0;
}
if (c->data[0] > 0) {
c->data[0]--;
} else if (blocking) {
block_thread_and_exit(c);
}
return ret;
}
static uint64_t view_pv_sync_read(ITCStorageCell *c)
{
return view_pv_common_read(c, true);
}
static uint64_t view_pv_try_read(ITCStorageCell *c)
{
return view_pv_common_read(c, false);
}
static inline void view_pv_common_write(ITCStorageCell *c)
{
if (c->tag.FIFO) {
return;
}
if (c->data[0] < ITC_CELL_PV_MAX_VAL) {
c->data[0]++;
}
if (c->blocked_threads) {
wake_blocked_threads(c);
}
}
static void view_pv_sync_write(ITCStorageCell *c)
{
view_pv_common_write(c);
}
static void view_pv_try_write(ITCStorageCell *c)
{
view_pv_common_write(c);
}
static uint64_t itc_storage_read(void *opaque, hwaddr addr, unsigned size)
{
MIPSITUState *s = (MIPSITUState *)opaque;
ITCStorageCell *cell = get_cell(s, addr);
ITCView view = get_itc_view(addr);
uint64_t ret = -1;
switch (view) {
case ITCVIEW_BYPASS:
ret = view_bypass_read(cell);
break;
case ITCVIEW_CONTROL:
ret = view_control_read(cell);
break;
case ITCVIEW_EF_SYNC:
ret = view_ef_sync_read(cell);
break;
case ITCVIEW_EF_TRY:
ret = view_ef_try_read(cell);
break;
case ITCVIEW_PV_SYNC:
ret = view_pv_sync_read(cell);
break;
case ITCVIEW_PV_TRY:
ret = view_pv_try_read(cell);
break;
default:
qemu_log_mask(LOG_GUEST_ERROR,
"itc_storage_read: Bad ITC View %d\n", (int)view);
break;
}
return ret;
}
static void itc_storage_write(void *opaque, hwaddr addr, uint64_t data,
unsigned size)
{
MIPSITUState *s = (MIPSITUState *)opaque;
ITCStorageCell *cell = get_cell(s, addr);
ITCView view = get_itc_view(addr);
switch (view) {
case ITCVIEW_BYPASS:
view_bypass_write(cell, data);
break;
case ITCVIEW_CONTROL:
view_control_write(cell, data);
break;
case ITCVIEW_EF_SYNC:
view_ef_sync_write(cell, data);
break;
case ITCVIEW_EF_TRY:
view_ef_try_write(cell, data);
break;
case ITCVIEW_PV_SYNC:
view_pv_sync_write(cell);
break;
case ITCVIEW_PV_TRY:
view_pv_try_write(cell);
break;
default:
qemu_log_mask(LOG_GUEST_ERROR,
"itc_storage_write: Bad ITC View %d\n", (int)view);
break;
}
}
static const MemoryRegionOps itc_storage_ops = {
.read = itc_storage_read,
.write = itc_storage_write,
.endianness = DEVICE_NATIVE_ENDIAN,
};
static void itc_reset_cells(MIPSITUState *s)
{
int i;
memset(s->cell, 0, get_num_cells(s) * sizeof(s->cell[0]));
for (i = 0; i < s->num_fifo; i++) {
s->cell[i].tag.E = 1;
s->cell[i].tag.FIFO = 1;
s->cell[i].tag.FIFODepth = ITC_CELL_DEPTH_SHIFT;
}
}
static void mips_itu_init(Object *obj)
{
SysBusDevice *sbd = SYS_BUS_DEVICE(obj);
MIPSITUState *s = MIPS_ITU(obj);
memory_region_init_io(&s->storage_io, OBJECT(s), &itc_storage_ops, s,
"mips-itc-storage", ITC_STORAGE_ADDRSPACE_SZ);
sysbus_init_mmio(sbd, &s->storage_io);
memory_region_init_io(&s->tag_io, OBJECT(s), &itc_tag_ops, s,
"mips-itc-tag", ITC_TAG_ADDRSPACE_SZ);
}
static void mips_itu_realize(DeviceState *dev, Error **errp)
{
MIPSITUState *s = MIPS_ITU(dev);
if (s->num_fifo > ITC_FIFO_NUM_MAX) {
error_setg(errp, "Exceed maximum number of FIFO cells: %d",
s->num_fifo);
return;
}
if (s->num_semaphores > ITC_SEMAPH_NUM_MAX) {
error_setg(errp, "Exceed maximum number of Semaphore cells: %d",
s->num_semaphores);
return;
}
s->cell = g_new(ITCStorageCell, get_num_cells(s));
}
static void mips_itu_reset(DeviceState *dev)
{
MIPSITUState *s = MIPS_ITU(dev);
s->ITCAddressMap[0] = 0;
s->ITCAddressMap[1] =
((ITC_STORAGE_ADDRSPACE_SZ - 1) & ITC_AM1_ADDR_MASK_MASK) |
(get_num_cells(s) << ITC_AM1_NUMENTRIES_OFS);
itc_reconfigure(s);
itc_reset_cells(s);
}
static Property mips_itu_properties[] = {
DEFINE_PROP_INT32("num-fifo", MIPSITUState, num_fifo,
ITC_FIFO_NUM_MAX),
DEFINE_PROP_INT32("num-semaphores", MIPSITUState, num_semaphores,
ITC_SEMAPH_NUM_MAX),
DEFINE_PROP_END_OF_LIST(),
};
static void mips_itu_class_init(ObjectClass *klass, void *data)
{
DeviceClass *dc = DEVICE_CLASS(klass);
dc->props = mips_itu_properties;
dc->realize = mips_itu_realize;
dc->reset = mips_itu_reset;
}
static const TypeInfo mips_itu_info = {
.name = TYPE_MIPS_ITU,
.parent = TYPE_SYS_BUS_DEVICE,
.instance_size = sizeof(MIPSITUState),
.instance_init = mips_itu_init,
.class_init = mips_itu_class_init,
};
static void mips_itu_register_types(void)
{
type_register_static(&mips_itu_info);
}
type_init(mips_itu_register_types)

46
include/hw/mips/cps.h Normal file
View File

@ -0,0 +1,46 @@
/*
* Coherent Processing System emulation.
*
* Copyright (c) 2016 Imagination Technologies
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, see <http://www.gnu.org/licenses/>.
*/
#ifndef MIPS_CPS_H
#define MIPS_CPS_H
#include "hw/sysbus.h"
#include "hw/misc/mips_cmgcr.h"
#include "hw/misc/mips_cpc.h"
#include "hw/misc/mips_itu.h"
#define TYPE_MIPS_CPS "mips-cps"
#define MIPS_CPS(obj) OBJECT_CHECK(MIPSCPSState, (obj), TYPE_MIPS_CPS)
typedef struct MIPSCPSState {
SysBusDevice parent_obj;
uint32_t num_vp;
uint32_t num_irq;
char *cpu_model;
MemoryRegion container;
MIPSGCRState gcr;
MIPSCPCState cpc;
MIPSITUState itu;
} MIPSCPSState;
qemu_irq get_cps_irq(MIPSCPSState *cps, int pin_number);
#endif

View File

@ -0,0 +1,59 @@
/*
* This file is subject to the terms and conditions of the GNU General Public
* License. See the file "COPYING" in the main directory of this archive
* for more details.
*
* Copyright (C) 2015 Imagination Technologies
*
*/
#ifndef _MIPS_GCR_H
#define _MIPS_GCR_H
#define TYPE_MIPS_GCR "mips-gcr"
#define MIPS_GCR(obj) OBJECT_CHECK(MIPSGCRState, (obj), TYPE_MIPS_GCR)
#define GCR_BASE_ADDR 0x1fbf8000ULL
#define GCR_ADDRSPACE_SZ 0x8000
/* Offsets to register blocks */
#define MIPS_GCB_OFS 0x0000 /* Global Control Block */
#define MIPS_CLCB_OFS 0x2000 /* Core Local Control Block */
#define MIPS_COCB_OFS 0x4000 /* Core Other Control Block */
#define MIPS_GDB_OFS 0x6000 /* Global Debug Block */
/* Global Control Block Register Map */
#define GCR_CONFIG_OFS 0x0000
#define GCR_BASE_OFS 0x0008
#define GCR_REV_OFS 0x0030
#define GCR_CPC_BASE_OFS 0x0088
#define GCR_CPC_STATUS_OFS 0x00F0
#define GCR_L2_CONFIG_OFS 0x0130
/* Core Local and Core Other Block Register Map */
#define GCR_CL_CONFIG_OFS 0x0010
#define GCR_CL_OTHER_OFS 0x0018
/* GCR_L2_CONFIG register fields */
#define GCR_L2_CONFIG_BYPASS_SHF 20
#define GCR_L2_CONFIG_BYPASS_MSK ((0x1ULL) << GCR_L2_CONFIG_BYPASS_SHF)
/* GCR_CPC_BASE register fields */
#define GCR_CPC_BASE_CPCEN_MSK 1
#define GCR_CPC_BASE_CPCBASE_MSK 0xFFFFFFFF8000ULL
#define GCR_CPC_BASE_MSK (GCR_CPC_BASE_CPCEN_MSK | GCR_CPC_BASE_CPCBASE_MSK)
typedef struct MIPSGCRState MIPSGCRState;
struct MIPSGCRState {
SysBusDevice parent_obj;
int32_t gcr_rev;
int32_t num_vps;
hwaddr gcr_base;
MemoryRegion iomem;
MemoryRegion *cpc_mr;
uint64_t cpc_base;
};
#endif /* _MIPS_GCR_H */

View File

@ -0,0 +1,47 @@
/*
* Cluster Power Controller emulation
*
* Copyright (c) 2016 Imagination Technologies
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, see <http://www.gnu.org/licenses/>.
*/
#ifndef MIPS_CPC_H
#define MIPS_CPC_H
#define CPC_ADDRSPACE_SZ 0x6000
/* CPC blocks offsets relative to base address */
#define CPC_CL_BASE_OFS 0x2000
#define CPC_CO_BASE_OFS 0x4000
/* CPC register offsets relative to block offsets */
#define CPC_VP_STOP_OFS 0x20
#define CPC_VP_RUN_OFS 0x28
#define CPC_VP_RUNNING_OFS 0x30
#define TYPE_MIPS_CPC "mips-cpc"
#define MIPS_CPC(obj) OBJECT_CHECK(MIPSCPCState, (obj), TYPE_MIPS_CPC)
typedef struct MIPSCPCState {
SysBusDevice parent_obj;
uint32_t num_vp;
uint64_t vp_start_running; /* VPs running from restart */
MemoryRegion mr;
uint64_t vp_running; /* Indicates which VPs are in the run state */
} MIPSCPCState;
#endif /* MIPS_CPC_H */

View File

@ -0,0 +1,72 @@
/*
* Inter-Thread Communication Unit emulation.
*
* Copyright (c) 2016 Imagination Technologies
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, see <http://www.gnu.org/licenses/>.
*/
#ifndef MIPS_ITU_H
#define MIPS_ITU_H
#define TYPE_MIPS_ITU "mips-itu"
#define MIPS_ITU(obj) OBJECT_CHECK(MIPSITUState, (obj), TYPE_MIPS_ITU)
#define ITC_CELL_DEPTH_SHIFT 2
#define ITC_CELL_DEPTH (1u << ITC_CELL_DEPTH_SHIFT)
typedef struct ITCStorageCell {
struct {
uint8_t FIFODepth; /* Log2 of the cell depth */
uint8_t FIFOPtr; /* Number of elements in a FIFO cell */
uint8_t FIFO; /* 1 - FIFO cell, 0 - Semaphore cell */
uint8_t T; /* Trap Bit */
uint8_t F; /* Full Bit */
uint8_t E; /* Empty Bit */
} tag;
/* Index of the oldest element in the queue */
uint8_t fifo_out;
/* Circular buffer for FIFO. Semaphore cells use index 0 only */
uint64_t data[ITC_CELL_DEPTH];
/* Bitmap tracking blocked threads on the cell.
TODO: support >64 threads ? */
uint64_t blocked_threads;
} ITCStorageCell;
#define ITC_ADDRESSMAP_NUM 2
typedef struct MIPSITUState {
/*< private >*/
SysBusDevice parent_obj;
/*< public >*/
int32_t num_fifo;
int32_t num_semaphores;
/* ITC Storage */
ITCStorageCell *cell;
MemoryRegion storage_io;
/* ITC Configuration Tags */
uint64_t ITCAddressMap[ITC_ADDRESSMAP_NUM];
MemoryRegion tag_io;
} MIPSITUState;
/* Get ITC Configuration Tag memory region. */
MemoryRegion *mips_itu_get_tag_region(MIPSITUState *itu);
#endif /* MIPS_ITU_H */

View File

@ -165,6 +165,7 @@ typedef struct mips_def_t mips_def_t;
#define MIPS_FPU_MAX 1
#define MIPS_DSP_ACC 4
#define MIPS_KSCRATCH_NUM 6
#define MIPS_MAAR_MAX 16 /* Must be an even number. */
typedef struct TCState TCState;
struct TCState {
@ -395,6 +396,7 @@ struct CPUMIPSState {
target_ulong CP0_EPC;
int32_t CP0_PRid;
int32_t CP0_EBase;
target_ulong CP0_CMGCRBase;
int32_t CP0_Config0;
#define CP0C0_M 31
#define CP0C0_K23 28
@ -437,7 +439,7 @@ struct CPUMIPSState {
int32_t CP0_Config3;
#define CP0C3_M 31
#define CP0C3_BPG 30
#define CP0C3_CMCGR 29
#define CP0C3_CMGCR 29
#define CP0C3_MSAP 28
#define CP0C3_BP 27
#define CP0C3_BI 26
@ -482,10 +484,13 @@ struct CPUMIPSState {
#define CP0C5_SBRI 6
#define CP0C5_MVH 5
#define CP0C5_LLB 4
#define CP0C5_MRP 3
#define CP0C5_UFR 2
#define CP0C5_NFExists 0
int32_t CP0_Config6;
int32_t CP0_Config7;
uint64_t CP0_MAAR[MIPS_MAAR_MAX];
int32_t CP0_MAARI;
/* XXX: Maybe make LLAddr per-TC? */
uint64_t lladdr;
target_ulong llval;
@ -518,6 +523,10 @@ struct CPUMIPSState {
#define CP0DB_DSS 0
target_ulong CP0_DEPC;
int32_t CP0_Performance0;
int32_t CP0_ErrCtl;
#define CP0EC_WST 29
#define CP0EC_SPR 28
#define CP0EC_ITC 26
uint64_t CP0_TagLo;
int32_t CP0_DataLo;
int32_t CP0_TagHi;
@ -533,7 +542,7 @@ struct CPUMIPSState {
#define EXCP_INST_NOTAVAIL 0x2 /* No valid instruction word for BadInstr */
uint32_t hflags; /* CPU State */
/* TMASK defines different execution modes */
#define MIPS_HFLAG_TMASK 0x75807FF
#define MIPS_HFLAG_TMASK 0xF5807FF
#define MIPS_HFLAG_MODE 0x00007 /* execution modes */
/* The KSU flags must be the lowest bits in hflags. The flag order
must be the same as defined for CP0 Status. This allows to use
@ -582,6 +591,7 @@ struct CPUMIPSState {
#define MIPS_HFLAG_MSA 0x1000000
#define MIPS_HFLAG_FRE 0x2000000 /* FRE enabled */
#define MIPS_HFLAG_ELPA 0x4000000
#define MIPS_HFLAG_ITC_CACHE 0x8000000 /* CACHE instr. operates on ITC tag */
target_ulong btarget; /* Jump / branch target */
target_ulong bcond; /* Branch condition (if needed) */
@ -602,6 +612,7 @@ struct CPUMIPSState {
const mips_def_t *cpu_model;
void *irq[8];
QEMUTimer *timer; /* Internal timer */
MemoryRegion *itc_tag; /* ITC Configuration Tags */
};
#include "cpu-qom.h"
@ -759,6 +770,7 @@ MIPSCPU *cpu_mips_init(const char *cpu_model);
int cpu_mips_signal_handler(int host_signum, void *pinfo, void *puc);
#define cpu_init(cpu_model) CPU(cpu_mips_init(cpu_model))
bool cpu_supports_cps_smp(const char *cpu_model);
/* TODO QOM'ify CPU reset and remove */
void cpu_state_reset(CPUMIPSState *s);

View File

@ -77,6 +77,8 @@ DEF_HELPER_1(mftc0_epc, tl, env)
DEF_HELPER_1(mftc0_ebase, tl, env)
DEF_HELPER_2(mftc0_configx, tl, env, tl)
DEF_HELPER_1(mfc0_lladdr, tl, env)
DEF_HELPER_1(mfc0_maar, tl, env)
DEF_HELPER_1(mfhc0_maar, tl, env)
DEF_HELPER_2(mfc0_watchlo, tl, env, i32)
DEF_HELPER_2(mfc0_watchhi, tl, env, i32)
DEF_HELPER_1(mfc0_debug, tl, env)
@ -88,6 +90,7 @@ DEF_HELPER_1(dmfc0_tccontext, tl, env)
DEF_HELPER_1(dmfc0_tcschedule, tl, env)
DEF_HELPER_1(dmfc0_tcschefback, tl, env)
DEF_HELPER_1(dmfc0_lladdr, tl, env)
DEF_HELPER_1(dmfc0_maar, tl, env)
DEF_HELPER_2(dmfc0_watchlo, tl, env, i32)
#endif /* TARGET_MIPS64 */
@ -144,6 +147,9 @@ DEF_HELPER_2(mtc0_config3, void, env, tl)
DEF_HELPER_2(mtc0_config4, void, env, tl)
DEF_HELPER_2(mtc0_config5, void, env, tl)
DEF_HELPER_2(mtc0_lladdr, void, env, tl)
DEF_HELPER_2(mtc0_maar, void, env, tl)
DEF_HELPER_2(mthc0_maar, void, env, tl)
DEF_HELPER_2(mtc0_maari, void, env, tl)
DEF_HELPER_3(mtc0_watchlo, void, env, tl, i32)
DEF_HELPER_3(mtc0_watchhi, void, env, tl, i32)
DEF_HELPER_2(mtc0_xcontext, void, env, tl)
@ -151,6 +157,7 @@ DEF_HELPER_2(mtc0_framemask, void, env, tl)
DEF_HELPER_2(mtc0_debug, void, env, tl)
DEF_HELPER_2(mttc0_debug, void, env, tl)
DEF_HELPER_2(mtc0_performance0, void, env, tl)
DEF_HELPER_2(mtc0_errctl, void, env, tl)
DEF_HELPER_2(mtc0_taglo, void, env, tl)
DEF_HELPER_2(mtc0_datalo, void, env, tl)
DEF_HELPER_2(mtc0_taghi, void, env, tl)
@ -949,3 +956,5 @@ MSALDST_PROTO(h)
MSALDST_PROTO(w)
MSALDST_PROTO(d)
#undef MSALDST_PROTO
DEF_HELPER_3(cache, void, env, tl, i32)

View File

@ -204,8 +204,8 @@ const VMStateDescription vmstate_tlb = {
const VMStateDescription vmstate_mips_cpu = {
.name = "cpu",
.version_id = 7,
.minimum_version_id = 7,
.version_id = 8,
.minimum_version_id = 8,
.post_load = cpu_post_load,
.fields = (VMStateField[]) {
/* Active TC */
@ -272,6 +272,8 @@ const VMStateDescription vmstate_mips_cpu = {
VMSTATE_INT32(env.CP0_Config3, MIPSCPU),
VMSTATE_INT32(env.CP0_Config6, MIPSCPU),
VMSTATE_INT32(env.CP0_Config7, MIPSCPU),
VMSTATE_UINT64_ARRAY(env.CP0_MAAR, MIPSCPU, MIPS_MAAR_MAX),
VMSTATE_INT32(env.CP0_MAARI, MIPSCPU),
VMSTATE_UINT64(env.lladdr, MIPSCPU),
VMSTATE_UINTTL_ARRAY(env.CP0_WatchLo, MIPSCPU, 8),
VMSTATE_INT32_ARRAY(env.CP0_WatchHi, MIPSCPU, 8),

View File

@ -889,6 +889,16 @@ target_ulong helper_mfc0_lladdr(CPUMIPSState *env)
return (int32_t)(env->lladdr >> env->CP0_LLAddr_shift);
}
target_ulong helper_mfc0_maar(CPUMIPSState *env)
{
return (int32_t) env->CP0_MAAR[env->CP0_MAARI];
}
target_ulong helper_mfhc0_maar(CPUMIPSState *env)
{
return env->CP0_MAAR[env->CP0_MAARI] >> 32;
}
target_ulong helper_mfc0_watchlo(CPUMIPSState *env, uint32_t sel)
{
return (int32_t)env->CP0_WatchLo[sel];
@ -955,6 +965,11 @@ target_ulong helper_dmfc0_lladdr(CPUMIPSState *env)
return env->lladdr >> env->CP0_LLAddr_shift;
}
target_ulong helper_dmfc0_maar(CPUMIPSState *env)
{
return env->CP0_MAAR[env->CP0_MAARI];
}
target_ulong helper_dmfc0_watchlo(CPUMIPSState *env, uint32_t sel)
{
return env->CP0_WatchLo[sel];
@ -1578,6 +1593,36 @@ void helper_mtc0_lladdr(CPUMIPSState *env, target_ulong arg1)
env->lladdr = (env->lladdr & ~mask) | (arg1 & mask);
}
#define MTC0_MAAR_MASK(env) \
((0x1ULL << 63) | ((env->PAMask >> 4) & ~0xFFFull) | 0x3)
void helper_mtc0_maar(CPUMIPSState *env, target_ulong arg1)
{
env->CP0_MAAR[env->CP0_MAARI] = arg1 & MTC0_MAAR_MASK(env);
}
void helper_mthc0_maar(CPUMIPSState *env, target_ulong arg1)
{
env->CP0_MAAR[env->CP0_MAARI] =
(((uint64_t) arg1 << 32) & MTC0_MAAR_MASK(env)) |
(env->CP0_MAAR[env->CP0_MAARI] & 0x00000000ffffffffULL);
}
void helper_mtc0_maari(CPUMIPSState *env, target_ulong arg1)
{
int index = arg1 & 0x3f;
if (index == 0x3f) {
/* Software may write all ones to INDEX to determine the
maximum value supported. */
env->CP0_MAARI = MIPS_MAAR_MAX - 1;
} else if (index < MIPS_MAAR_MAX) {
env->CP0_MAARI = index;
}
/* Other than the all ones, if the
value written is not supported, then INDEX is unchanged
from its previous value. */
}
void helper_mtc0_watchlo(CPUMIPSState *env, target_ulong arg1, uint32_t sel)
{
/* Watch exceptions for instructions, data loads, data stores
@ -1632,9 +1677,31 @@ void helper_mtc0_performance0(CPUMIPSState *env, target_ulong arg1)
env->CP0_Performance0 = arg1 & 0x000007ff;
}
void helper_mtc0_errctl(CPUMIPSState *env, target_ulong arg1)
{
int32_t wst = arg1 & (1 << CP0EC_WST);
int32_t spr = arg1 & (1 << CP0EC_SPR);
int32_t itc = env->itc_tag ? (arg1 & (1 << CP0EC_ITC)) : 0;
env->CP0_ErrCtl = wst | spr | itc;
if (itc && !wst && !spr) {
env->hflags |= MIPS_HFLAG_ITC_CACHE;
} else {
env->hflags &= ~MIPS_HFLAG_ITC_CACHE;
}
}
void helper_mtc0_taglo(CPUMIPSState *env, target_ulong arg1)
{
env->CP0_TagLo = arg1 & 0xFFFFFCF6;
if (env->hflags & MIPS_HFLAG_ITC_CACHE) {
/* If CACHE instruction is configured for ITC tags then make all
CP0.TagLo bits writable. The actual write to ITC Configuration
Tag will take care of the read-only bits. */
env->CP0_TagLo = arg1;
} else {
env->CP0_TagLo = arg1 & 0xFFFFFCF6;
}
}
void helper_mtc0_datalo(CPUMIPSState *env, target_ulong arg1)
@ -3781,3 +3848,19 @@ MSA_ST_DF(DF_HALF, h, cpu_stw_data)
MSA_ST_DF(DF_WORD, w, cpu_stl_data)
MSA_ST_DF(DF_DOUBLE, d, cpu_stq_data)
#endif
void helper_cache(CPUMIPSState *env, target_ulong addr, uint32_t op)
{
#ifndef CONFIG_USER_ONLY
target_ulong index = addr & 0x1fffffff;
if (op == 9) {
/* Index Store Tag */
memory_region_dispatch_write(env->itc_tag, index, env->CP0_TagLo,
8, MEMTXATTRS_UNSPECIFIED);
} else if (op == 5) {
/* Index Load Tag */
memory_region_dispatch_read(env->itc_tag, index, &env->CP0_TagLo,
8, MEMTXATTRS_UNSPECIFIED);
}
#endif
}

View File

@ -1432,6 +1432,8 @@ typedef struct DisasContext {
int CP0_LLAddr_shift;
bool ps;
bool vp;
bool cmgcr;
bool mrp;
} DisasContext;
enum {
@ -4774,13 +4776,18 @@ static inline void gen_mtc0_store32 (TCGv arg, target_ulong off)
tcg_temp_free_i32(t0);
}
#define CP0_CHECK(c) \
do { \
if (!(c)) { \
goto cp0_unimplemented; \
} \
} while (0)
static void gen_mfhc0(DisasContext *ctx, TCGv arg, int reg, int sel)
{
const char *rn = "invalid";
if (!(ctx->hflags & MIPS_HFLAG_ELPA)) {
goto mfhc0_read_zero;
}
CP0_CHECK(ctx->hflags & MIPS_HFLAG_ELPA);
switch (reg) {
case 2:
@ -4790,7 +4797,7 @@ static void gen_mfhc0(DisasContext *ctx, TCGv arg, int reg, int sel)
rn = "EntryLo0";
break;
default:
goto mfhc0_read_zero;
goto cp0_unimplemented;
}
break;
case 3:
@ -4800,7 +4807,7 @@ static void gen_mfhc0(DisasContext *ctx, TCGv arg, int reg, int sel)
rn = "EntryLo1";
break;
default:
goto mfhc0_read_zero;
goto cp0_unimplemented;
}
break;
case 17:
@ -4810,8 +4817,13 @@ static void gen_mfhc0(DisasContext *ctx, TCGv arg, int reg, int sel)
ctx->CP0_LLAddr_shift);
rn = "LLAddr";
break;
case 1:
CP0_CHECK(ctx->mrp);
gen_helper_mfhc0_maar(arg, cpu_env);
rn = "MAAR";
break;
default:
goto mfhc0_read_zero;
goto cp0_unimplemented;
}
break;
case 28:
@ -4824,18 +4836,18 @@ static void gen_mfhc0(DisasContext *ctx, TCGv arg, int reg, int sel)
rn = "TagLo";
break;
default:
goto mfhc0_read_zero;
goto cp0_unimplemented;
}
break;
default:
goto mfhc0_read_zero;
goto cp0_unimplemented;
}
(void)rn; /* avoid a compiler warning */
LOG_DISAS("mfhc0 %s (reg %d sel %d)\n", rn, reg, sel);
return;
mfhc0_read_zero:
cp0_unimplemented:
LOG_DISAS("mfhc0 %s (reg %d sel %d)\n", rn, reg, sel);
tcg_gen_movi_tl(arg, 0);
}
@ -4845,9 +4857,7 @@ static void gen_mthc0(DisasContext *ctx, TCGv arg, int reg, int sel)
const char *rn = "invalid";
uint64_t mask = ctx->PAMask >> 36;
if (!(ctx->hflags & MIPS_HFLAG_ELPA)) {
goto mthc0_nop;
}
CP0_CHECK(ctx->hflags & MIPS_HFLAG_ELPA);
switch (reg) {
case 2:
@ -4858,7 +4868,7 @@ static void gen_mthc0(DisasContext *ctx, TCGv arg, int reg, int sel)
rn = "EntryLo0";
break;
default:
goto mthc0_nop;
goto cp0_unimplemented;
}
break;
case 3:
@ -4869,7 +4879,7 @@ static void gen_mthc0(DisasContext *ctx, TCGv arg, int reg, int sel)
rn = "EntryLo1";
break;
default:
goto mthc0_nop;
goto cp0_unimplemented;
}
break;
case 17:
@ -4881,8 +4891,13 @@ static void gen_mthc0(DisasContext *ctx, TCGv arg, int reg, int sel)
treating MTHC0 to LLAddr as NOP. */
rn = "LLAddr";
break;
case 1:
CP0_CHECK(ctx->mrp);
gen_helper_mthc0_maar(cpu_env, arg);
rn = "MAAR";
break;
default:
goto mthc0_nop;
goto cp0_unimplemented;
}
break;
case 28:
@ -4896,15 +4911,15 @@ static void gen_mthc0(DisasContext *ctx, TCGv arg, int reg, int sel)
rn = "TagLo";
break;
default:
goto mthc0_nop;
goto cp0_unimplemented;
}
break;
default:
goto mthc0_nop;
goto cp0_unimplemented;
}
(void)rn; /* avoid a compiler warning */
mthc0_nop:
cp0_unimplemented:
LOG_DISAS("mthc0 %s (reg %d sel %d)\n", rn, reg, sel);
}
@ -4917,13 +4932,6 @@ static inline void gen_mfc0_unimplemented(DisasContext *ctx, TCGv arg)
}
}
#define CP0_CHECK(c) \
do { \
if (!(c)) { \
goto cp0_unimplemented; \
} \
} while (0)
static void gen_mfc0(DisasContext *ctx, TCGv arg, int reg, int sel)
{
const char *rn = "invalid";
@ -5298,6 +5306,13 @@ static void gen_mfc0(DisasContext *ctx, TCGv arg, int reg, int sel)
gen_mfc0_load32(arg, offsetof(CPUMIPSState, CP0_EBase));
rn = "EBase";
break;
case 3:
check_insn(ctx, ISA_MIPS32R2);
CP0_CHECK(ctx->cmgcr);
tcg_gen_ld_tl(arg, cpu_env, offsetof(CPUMIPSState, CP0_CMGCRBase));
tcg_gen_ext32s_tl(arg, arg);
rn = "CMGCRBase";
break;
default:
goto cp0_unimplemented;
}
@ -5347,6 +5362,16 @@ static void gen_mfc0(DisasContext *ctx, TCGv arg, int reg, int sel)
gen_helper_mfc0_lladdr(arg, cpu_env);
rn = "LLAddr";
break;
case 1:
CP0_CHECK(ctx->mrp);
gen_helper_mfc0_maar(arg, cpu_env);
rn = "MAAR";
break;
case 2:
CP0_CHECK(ctx->mrp);
gen_mfc0_load32(arg, offsetof(CPUMIPSState, CP0_MAARI));
rn = "MAARI";
break;
default:
goto cp0_unimplemented;
}
@ -5478,8 +5503,14 @@ static void gen_mfc0(DisasContext *ctx, TCGv arg, int reg, int sel)
}
break;
case 26:
tcg_gen_movi_tl(arg, 0); /* unimplemented */
rn = "ECC";
switch (sel) {
case 0:
gen_mfc0_load32(arg, offsetof(CPUMIPSState, CP0_ErrCtl));
rn = "ErrCtl";
break;
default:
goto cp0_unimplemented;
}
break;
case 27:
switch (sel) {
@ -5986,6 +6017,16 @@ static void gen_mtc0(DisasContext *ctx, TCGv arg, int reg, int sel)
gen_helper_mtc0_lladdr(cpu_env, arg);
rn = "LLAddr";
break;
case 1:
CP0_CHECK(ctx->mrp);
gen_helper_mtc0_maar(cpu_env, arg);
rn = "MAAR";
break;
case 2:
CP0_CHECK(ctx->mrp);
gen_helper_mtc0_maari(cpu_env, arg);
rn = "MAARI";
break;
default:
goto cp0_unimplemented;
}
@ -6128,8 +6169,15 @@ static void gen_mtc0(DisasContext *ctx, TCGv arg, int reg, int sel)
}
break;
case 26:
/* ignored */
rn = "ECC";
switch (sel) {
case 0:
gen_helper_mtc0_errctl(cpu_env, arg);
ctx->bstate = BS_STOP;
rn = "ErrCtl";
break;
default:
goto cp0_unimplemented;
}
break;
case 27:
switch (sel) {
@ -6572,6 +6620,12 @@ static void gen_dmfc0(DisasContext *ctx, TCGv arg, int reg, int sel)
gen_mfc0_load32(arg, offsetof(CPUMIPSState, CP0_EBase));
rn = "EBase";
break;
case 3:
check_insn(ctx, ISA_MIPS32R2);
CP0_CHECK(ctx->cmgcr);
tcg_gen_ld_tl(arg, cpu_env, offsetof(CPUMIPSState, CP0_CMGCRBase));
rn = "CMGCRBase";
break;
default:
goto cp0_unimplemented;
}
@ -6621,6 +6675,16 @@ static void gen_dmfc0(DisasContext *ctx, TCGv arg, int reg, int sel)
gen_helper_dmfc0_lladdr(arg, cpu_env);
rn = "LLAddr";
break;
case 1:
CP0_CHECK(ctx->mrp);
gen_helper_dmfc0_maar(arg, cpu_env);
rn = "MAAR";
break;
case 2:
CP0_CHECK(ctx->mrp);
gen_mfc0_load32(arg, offsetof(CPUMIPSState, CP0_MAARI));
rn = "MAARI";
break;
default:
goto cp0_unimplemented;
}
@ -6748,8 +6812,14 @@ static void gen_dmfc0(DisasContext *ctx, TCGv arg, int reg, int sel)
}
break;
case 26:
tcg_gen_movi_tl(arg, 0); /* unimplemented */
rn = "ECC";
switch (sel) {
case 0:
gen_mfc0_load32(arg, offsetof(CPUMIPSState, CP0_ErrCtl));
rn = "ErrCtl";
break;
default:
goto cp0_unimplemented;
}
break;
case 27:
switch (sel) {
@ -7252,6 +7322,16 @@ static void gen_dmtc0(DisasContext *ctx, TCGv arg, int reg, int sel)
gen_helper_mtc0_lladdr(cpu_env, arg);
rn = "LLAddr";
break;
case 1:
CP0_CHECK(ctx->mrp);
gen_helper_mtc0_maar(cpu_env, arg);
rn = "MAAR";
break;
case 2:
CP0_CHECK(ctx->mrp);
gen_helper_mtc0_maari(cpu_env, arg);
rn = "MAARI";
break;
default:
goto cp0_unimplemented;
}
@ -7390,8 +7470,15 @@ static void gen_dmtc0(DisasContext *ctx, TCGv arg, int reg, int sel)
}
break;
case 26:
/* ignored */
rn = "ECC";
switch (sel) {
case 0:
gen_helper_mtc0_errctl(cpu_env, arg);
ctx->bstate = BS_STOP;
rn = "ErrCtl";
break;
default:
goto cp0_unimplemented;
}
break;
case 27:
switch (sel) {
@ -11176,6 +11263,15 @@ static void gen_addiupc (DisasContext *ctx, int rx, int imm,
tcg_temp_free(t0);
}
static void gen_cache_operation(DisasContext *ctx, uint32_t op, int base,
int16_t offset)
{
TCGv_i32 t0 = tcg_const_i32(op);
TCGv t1 = tcg_temp_new();
gen_base_offset_addr(ctx, t1, base, offset);
gen_helper_cache(cpu_env, t1, t0);
}
#if defined(TARGET_MIPS64)
static void decode_i64_mips16 (DisasContext *ctx,
int ry, int funct, int16_t offset,
@ -13727,7 +13823,9 @@ static void decode_micromips32_opc(CPUMIPSState *env, DisasContext *ctx)
switch (minor) {
case CACHE:
check_cp0_enabled(ctx);
/* Treat as no-op. */
if (ctx->hflags & MIPS_HFLAG_ITC_CACHE) {
gen_cache_operation(ctx, rt, rs, imm);
}
break;
case LWC2:
case SWC2:
@ -17180,7 +17278,10 @@ static void decode_opc_special3_r6(CPUMIPSState *env, DisasContext *ctx)
/* Treat as NOP. */
break;
case R6_OPC_CACHE:
/* Treat as NOP. */
check_cp0_enabled(ctx);
if (ctx->hflags & MIPS_HFLAG_ITC_CACHE) {
gen_cache_operation(ctx, rt, rs, imm);
}
break;
case R6_OPC_SC:
gen_st_cond(ctx, op1, rt, rs, imm);
@ -19289,6 +19390,9 @@ static void decode_opc(CPUMIPSState *env, DisasContext *ctx)
check_insn_opc_removed(ctx, ISA_MIPS32R6);
check_cp0_enabled(ctx);
check_insn(ctx, ISA_MIPS3 | ISA_MIPS32);
if (ctx->hflags & MIPS_HFLAG_ITC_CACHE) {
gen_cache_operation(ctx, rt, rs, imm);
}
/* Treat as NOP. */
break;
case OPC_PREF:
@ -19663,12 +19767,14 @@ void gen_intermediate_code(CPUMIPSState *env, struct TranslationBlock *tb)
ctx.PAMask = env->PAMask;
ctx.mvh = (env->CP0_Config5 >> CP0C5_MVH) & 1;
ctx.CP0_LLAddr_shift = env->CP0_LLAddr_shift;
ctx.cmgcr = (env->CP0_Config3 >> CP0C3_CMGCR) & 1;
/* Restore delay slot state from the tb context. */
ctx.hflags = (uint32_t)tb->flags; /* FIXME: maybe use 64 bits here? */
ctx.ulri = (env->CP0_Config3 >> CP0C3_ULRI) & 1;
ctx.ps = ((env->active_fpu.fcr0 >> FCR0_PS) & 1) ||
(env->insn_flags & (INSN_LOONGSON2E | INSN_LOONGSON2F));
ctx.vp = (env->CP0_Config5 >> CP0C5_VP) & 1;
ctx.mrp = (env->CP0_Config5 >> CP0C5_MRP) & 1;
restore_cpu_state(env, &ctx);
#ifdef CONFIG_USER_ONLY
ctx.mem_idx = MIPS_HFLAG_UM;
@ -19962,6 +20068,16 @@ MIPSCPU *cpu_mips_init(const char *cpu_model)
return cpu;
}
bool cpu_supports_cps_smp(const char *cpu_model)
{
const mips_def_t *def = cpu_mips_find_by_name(cpu_model);
if (!def) {
return false;
}
return (def->CP0_Config3 & (1 << CP0C3_CMGCR)) != 0;
}
void cpu_state_reset(CPUMIPSState *env)
{
MIPSCPU *cpu = mips_env_get_cpu(env);
@ -20062,6 +20178,9 @@ void cpu_state_reset(CPUMIPSState *env)
} else {
env->CP0_EBase |= 0x80000000;
}
if (env->CP0_Config3 & (1 << CP0C3_CMGCR)) {
env->CP0_CMGCRBase = 0x1fbf8000 >> 4;
}
env->CP0_Status = (1 << CP0St_BEV) | (1 << CP0St_ERL);
/* vectored interrupts not implemented, timer on int 7,
no performance counters. */

View File

@ -411,7 +411,8 @@ static const mips_def_t mips_defs[] =
.CP0_Config4 = MIPS_CONFIG4 | (1U << CP0C4_M) | (2 << CP0C4_IE) |
(0x1c << CP0C4_KScrExist),
.CP0_Config4_rw_bitmask = 0,
.CP0_Config5 = MIPS_CONFIG5 | (1 << CP0C5_MVH) | (1 << CP0C5_LLB),
.CP0_Config5 = MIPS_CONFIG5 | (1 << CP0C5_MVH) | (1 << CP0C5_LLB) |
(1 << CP0C5_MRP),
.CP0_Config5_rw_bitmask = (1 << CP0C5_K) | (1 << CP0C5_CV) |
(1 << CP0C5_MSAEn) | (1 << CP0C5_UFE) |
(1 << CP0C5_FRE) | (1 << CP0C5_UFR),
@ -663,7 +664,8 @@ static const mips_def_t mips_defs[] =
(2 << CP0C1_DS) | (4 << CP0C1_DL) | (3 << CP0C1_DA) |
(0 << CP0C1_PC) | (1 << CP0C1_WR) | (1 << CP0C1_EP),
.CP0_Config2 = MIPS_CONFIG2,
.CP0_Config3 = MIPS_CONFIG3 | (1U << CP0C3_M) | (1 << CP0C3_MSAP) |
.CP0_Config3 = MIPS_CONFIG3 | (1U << CP0C3_M) |
(1 << CP0C3_CMGCR) | (1 << CP0C3_MSAP) |
(1 << CP0C3_BP) | (1 << CP0C3_BI) | (1 << CP0C3_ULRI) |
(1 << CP0C3_RXI) | (1 << CP0C3_LPA),
.CP0_Config4 = MIPS_CONFIG4 | (1U << CP0C4_M) | (3 << CP0C4_IE) |