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_MC146818RTC=y
CONFIG_ISA_TESTDEV=y CONFIG_ISA_TESTDEV=y
CONFIG_EMPTY_SLOT=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_JAZZ) += mips_jazz.o
obj-$(CONFIG_FULONG) += mips_fulong2e.o obj-$(CONFIG_FULONG) += mips_fulong2e.o
obj-y += gt64xxx_pci.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 "hw/empty_slot.h"
#include "sysemu/kvm.h" #include "sysemu/kvm.h"
#include "exec/semihost.h" #include "exec/semihost.h"
#include "hw/mips/cps.h"
//#define DEBUG_BOARD_INIT //#define DEBUG_BOARD_INIT
@ -95,6 +96,7 @@ typedef struct {
typedef struct { typedef struct {
SysBusDevice parent_obj; SysBusDevice parent_obj;
MIPSCPSState *cps;
qemu_irq *i8259; qemu_irq *i8259;
} MaltaState; } MaltaState;
@ -608,8 +610,8 @@ static void network_init(PCIBus *pci_bus)
a3 - RAM size in bytes a3 - RAM size in bytes
*/ */
static void write_bootloader (CPUMIPSState *env, uint8_t *base, static void write_bootloader(uint8_t *base, int64_t run_addr,
int64_t run_addr, int64_t kernel_entry) int64_t kernel_entry)
{ {
uint32_t *p; 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 static
void mips_malta_init(MachineState *machine) void mips_malta_init(MachineState *machine)
{ {
ram_addr_t ram_size = machine->ram_size; ram_addr_t ram_size = machine->ram_size;
ram_addr_t ram_low_size; ram_addr_t ram_low_size;
const char *cpu_model = machine->cpu_model;
const char *kernel_filename = machine->kernel_filename; const char *kernel_filename = machine->kernel_filename;
const char *kernel_cmdline = machine->kernel_cmdline; const char *kernel_cmdline = machine->kernel_cmdline;
const char *initrd_filename = machine->initrd_filename; const char *initrd_filename = machine->initrd_filename;
@ -930,9 +1001,8 @@ void mips_malta_init(MachineState *machine)
int64_t kernel_entry, bootloader_run_addr; int64_t kernel_entry, bootloader_run_addr;
PCIBus *pci_bus; PCIBus *pci_bus;
ISABus *isa_bus; ISABus *isa_bus;
MIPSCPU *cpu;
CPUMIPSState *env;
qemu_irq *isa_irq; qemu_irq *isa_irq;
qemu_irq cbus_irq, i8259_irq;
int piix4_devfn; int piix4_devfn;
I2CBus *smbus; I2CBus *smbus;
int i; int i;
@ -962,30 +1032,8 @@ void mips_malta_init(MachineState *machine)
} }
} }
/* init CPUs */ /* create CPU */
if (cpu_model == NULL) { create_cpu(s, machine->cpu_model, &cbus_irq, &i8259_irq);
#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;
/* allocate RAM */ /* allocate RAM */
if (ram_size > (2048u << 20)) { if (ram_size > (2048u << 20)) {
@ -1026,7 +1074,7 @@ void mips_malta_init(MachineState *machine)
#endif #endif
/* FPGA */ /* FPGA */
/* The CBUS UART is attached to the MIPS CPU INT2 pin, ie interrupt 4 */ /* 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. */ /* Load firmware in flash / BIOS. */
dinfo = drive_get(IF_PFLASH, 0, fl_idx); dinfo = drive_get(IF_PFLASH, 0, fl_idx);
@ -1063,11 +1111,11 @@ void mips_malta_init(MachineState *machine)
loaderparams.initrd_filename = initrd_filename; loaderparams.initrd_filename = initrd_filename;
kernel_entry = load_kernel(); 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); bootloader_run_addr, kernel_entry);
if (kvm_enabled()) { if (kvm_enabled()) {
/* Write the bootloader code @ the end of RAM, 1MB reserved */ /* 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, ram_low_size,
bootloader_run_addr, kernel_entry); bootloader_run_addr, kernel_entry);
} }
@ -1135,10 +1183,6 @@ void mips_malta_init(MachineState *machine)
/* Board ID = 0x420 (Malta Board with CoreLV) */ /* Board ID = 0x420 (Malta Board with CoreLV) */
stl_p(memory_region_get_ram_ptr(bios_copy) + 0x10, 0x00000420); 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, * We have a circular dependency problem: pci_bus depends on isa_irq,
* isa_irq is provided by i8259, i8259 depends on ISA, ISA depends * isa_irq is provided by i8259, i8259 depends on ISA, ISA depends
@ -1158,7 +1202,7 @@ void mips_malta_init(MachineState *machine)
/* Interrupt controller */ /* Interrupt controller */
/* The 8259 is attached to the MIPS CPU INT0 pin, ie interrupt 2 */ /* 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); isa_bus_irqs(isa_bus, s->i8259);
pci_piix4_ide_init(pci_bus, hd, piix4_devfn + 1); 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_slcr.o
obj-$(CONFIG_ZYNQ) += zynq-xadc.o obj-$(CONFIG_ZYNQ) += zynq-xadc.o
obj-$(CONFIG_STM32F2XX_SYSCFG) += stm32f2xx_syscfg.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_PVPANIC) += pvpanic.o
obj-$(CONFIG_EDU) += edu.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_FPU_MAX 1
#define MIPS_DSP_ACC 4 #define MIPS_DSP_ACC 4
#define MIPS_KSCRATCH_NUM 6 #define MIPS_KSCRATCH_NUM 6
#define MIPS_MAAR_MAX 16 /* Must be an even number. */
typedef struct TCState TCState; typedef struct TCState TCState;
struct TCState { struct TCState {
@ -395,6 +396,7 @@ struct CPUMIPSState {
target_ulong CP0_EPC; target_ulong CP0_EPC;
int32_t CP0_PRid; int32_t CP0_PRid;
int32_t CP0_EBase; int32_t CP0_EBase;
target_ulong CP0_CMGCRBase;
int32_t CP0_Config0; int32_t CP0_Config0;
#define CP0C0_M 31 #define CP0C0_M 31
#define CP0C0_K23 28 #define CP0C0_K23 28
@ -437,7 +439,7 @@ struct CPUMIPSState {
int32_t CP0_Config3; int32_t CP0_Config3;
#define CP0C3_M 31 #define CP0C3_M 31
#define CP0C3_BPG 30 #define CP0C3_BPG 30
#define CP0C3_CMCGR 29 #define CP0C3_CMGCR 29
#define CP0C3_MSAP 28 #define CP0C3_MSAP 28
#define CP0C3_BP 27 #define CP0C3_BP 27
#define CP0C3_BI 26 #define CP0C3_BI 26
@ -482,10 +484,13 @@ struct CPUMIPSState {
#define CP0C5_SBRI 6 #define CP0C5_SBRI 6
#define CP0C5_MVH 5 #define CP0C5_MVH 5
#define CP0C5_LLB 4 #define CP0C5_LLB 4
#define CP0C5_MRP 3
#define CP0C5_UFR 2 #define CP0C5_UFR 2
#define CP0C5_NFExists 0 #define CP0C5_NFExists 0
int32_t CP0_Config6; int32_t CP0_Config6;
int32_t CP0_Config7; int32_t CP0_Config7;
uint64_t CP0_MAAR[MIPS_MAAR_MAX];
int32_t CP0_MAARI;
/* XXX: Maybe make LLAddr per-TC? */ /* XXX: Maybe make LLAddr per-TC? */
uint64_t lladdr; uint64_t lladdr;
target_ulong llval; target_ulong llval;
@ -518,6 +523,10 @@ struct CPUMIPSState {
#define CP0DB_DSS 0 #define CP0DB_DSS 0
target_ulong CP0_DEPC; target_ulong CP0_DEPC;
int32_t CP0_Performance0; int32_t CP0_Performance0;
int32_t CP0_ErrCtl;
#define CP0EC_WST 29
#define CP0EC_SPR 28
#define CP0EC_ITC 26
uint64_t CP0_TagLo; uint64_t CP0_TagLo;
int32_t CP0_DataLo; int32_t CP0_DataLo;
int32_t CP0_TagHi; int32_t CP0_TagHi;
@ -533,7 +542,7 @@ struct CPUMIPSState {
#define EXCP_INST_NOTAVAIL 0x2 /* No valid instruction word for BadInstr */ #define EXCP_INST_NOTAVAIL 0x2 /* No valid instruction word for BadInstr */
uint32_t hflags; /* CPU State */ uint32_t hflags; /* CPU State */
/* TMASK defines different execution modes */ /* TMASK defines different execution modes */
#define MIPS_HFLAG_TMASK 0x75807FF #define MIPS_HFLAG_TMASK 0xF5807FF
#define MIPS_HFLAG_MODE 0x00007 /* execution modes */ #define MIPS_HFLAG_MODE 0x00007 /* execution modes */
/* The KSU flags must be the lowest bits in hflags. The flag order /* 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 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_MSA 0x1000000
#define MIPS_HFLAG_FRE 0x2000000 /* FRE enabled */ #define MIPS_HFLAG_FRE 0x2000000 /* FRE enabled */
#define MIPS_HFLAG_ELPA 0x4000000 #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 btarget; /* Jump / branch target */
target_ulong bcond; /* Branch condition (if needed) */ target_ulong bcond; /* Branch condition (if needed) */
@ -602,6 +612,7 @@ struct CPUMIPSState {
const mips_def_t *cpu_model; const mips_def_t *cpu_model;
void *irq[8]; void *irq[8];
QEMUTimer *timer; /* Internal timer */ QEMUTimer *timer; /* Internal timer */
MemoryRegion *itc_tag; /* ITC Configuration Tags */
}; };
#include "cpu-qom.h" #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); int cpu_mips_signal_handler(int host_signum, void *pinfo, void *puc);
#define cpu_init(cpu_model) CPU(cpu_mips_init(cpu_model)) #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 */ /* TODO QOM'ify CPU reset and remove */
void cpu_state_reset(CPUMIPSState *s); 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_1(mftc0_ebase, tl, env)
DEF_HELPER_2(mftc0_configx, tl, env, tl) DEF_HELPER_2(mftc0_configx, tl, env, tl)
DEF_HELPER_1(mfc0_lladdr, tl, env) 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_watchlo, tl, env, i32)
DEF_HELPER_2(mfc0_watchhi, tl, env, i32) DEF_HELPER_2(mfc0_watchhi, tl, env, i32)
DEF_HELPER_1(mfc0_debug, tl, env) 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_tcschedule, tl, env)
DEF_HELPER_1(dmfc0_tcschefback, tl, env) DEF_HELPER_1(dmfc0_tcschefback, tl, env)
DEF_HELPER_1(dmfc0_lladdr, tl, env) DEF_HELPER_1(dmfc0_lladdr, tl, env)
DEF_HELPER_1(dmfc0_maar, tl, env)
DEF_HELPER_2(dmfc0_watchlo, tl, env, i32) DEF_HELPER_2(dmfc0_watchlo, tl, env, i32)
#endif /* TARGET_MIPS64 */ #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_config4, void, env, tl)
DEF_HELPER_2(mtc0_config5, void, env, tl) DEF_HELPER_2(mtc0_config5, void, env, tl)
DEF_HELPER_2(mtc0_lladdr, 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_watchlo, void, env, tl, i32)
DEF_HELPER_3(mtc0_watchhi, void, env, tl, i32) DEF_HELPER_3(mtc0_watchhi, void, env, tl, i32)
DEF_HELPER_2(mtc0_xcontext, void, env, tl) 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(mtc0_debug, void, env, tl)
DEF_HELPER_2(mttc0_debug, void, env, tl) DEF_HELPER_2(mttc0_debug, void, env, tl)
DEF_HELPER_2(mtc0_performance0, 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_taglo, void, env, tl)
DEF_HELPER_2(mtc0_datalo, void, env, tl) DEF_HELPER_2(mtc0_datalo, void, env, tl)
DEF_HELPER_2(mtc0_taghi, void, env, tl) DEF_HELPER_2(mtc0_taghi, void, env, tl)
@ -949,3 +956,5 @@ MSALDST_PROTO(h)
MSALDST_PROTO(w) MSALDST_PROTO(w)
MSALDST_PROTO(d) MSALDST_PROTO(d)
#undef MSALDST_PROTO #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 = { const VMStateDescription vmstate_mips_cpu = {
.name = "cpu", .name = "cpu",
.version_id = 7, .version_id = 8,
.minimum_version_id = 7, .minimum_version_id = 8,
.post_load = cpu_post_load, .post_load = cpu_post_load,
.fields = (VMStateField[]) { .fields = (VMStateField[]) {
/* Active TC */ /* Active TC */
@ -272,6 +272,8 @@ const VMStateDescription vmstate_mips_cpu = {
VMSTATE_INT32(env.CP0_Config3, MIPSCPU), VMSTATE_INT32(env.CP0_Config3, MIPSCPU),
VMSTATE_INT32(env.CP0_Config6, MIPSCPU), VMSTATE_INT32(env.CP0_Config6, MIPSCPU),
VMSTATE_INT32(env.CP0_Config7, 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_UINT64(env.lladdr, MIPSCPU),
VMSTATE_UINTTL_ARRAY(env.CP0_WatchLo, MIPSCPU, 8), VMSTATE_UINTTL_ARRAY(env.CP0_WatchLo, MIPSCPU, 8),
VMSTATE_INT32_ARRAY(env.CP0_WatchHi, 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); 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) target_ulong helper_mfc0_watchlo(CPUMIPSState *env, uint32_t sel)
{ {
return (int32_t)env->CP0_WatchLo[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; 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) target_ulong helper_dmfc0_watchlo(CPUMIPSState *env, uint32_t sel)
{ {
return env->CP0_WatchLo[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); 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) void helper_mtc0_watchlo(CPUMIPSState *env, target_ulong arg1, uint32_t sel)
{ {
/* Watch exceptions for instructions, data loads, data stores /* 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; 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) 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) 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_WORD, w, cpu_stl_data)
MSA_ST_DF(DF_DOUBLE, d, cpu_stq_data) MSA_ST_DF(DF_DOUBLE, d, cpu_stq_data)
#endif #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; int CP0_LLAddr_shift;
bool ps; bool ps;
bool vp; bool vp;
bool cmgcr;
bool mrp;
} DisasContext; } DisasContext;
enum { enum {
@ -4774,13 +4776,18 @@ static inline void gen_mtc0_store32 (TCGv arg, target_ulong off)
tcg_temp_free_i32(t0); 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) static void gen_mfhc0(DisasContext *ctx, TCGv arg, int reg, int sel)
{ {
const char *rn = "invalid"; const char *rn = "invalid";
if (!(ctx->hflags & MIPS_HFLAG_ELPA)) { CP0_CHECK(ctx->hflags & MIPS_HFLAG_ELPA);
goto mfhc0_read_zero;
}
switch (reg) { switch (reg) {
case 2: case 2:
@ -4790,7 +4797,7 @@ static void gen_mfhc0(DisasContext *ctx, TCGv arg, int reg, int sel)
rn = "EntryLo0"; rn = "EntryLo0";
break; break;
default: default:
goto mfhc0_read_zero; goto cp0_unimplemented;
} }
break; break;
case 3: case 3:
@ -4800,7 +4807,7 @@ static void gen_mfhc0(DisasContext *ctx, TCGv arg, int reg, int sel)
rn = "EntryLo1"; rn = "EntryLo1";
break; break;
default: default:
goto mfhc0_read_zero; goto cp0_unimplemented;
} }
break; break;
case 17: case 17:
@ -4810,8 +4817,13 @@ static void gen_mfhc0(DisasContext *ctx, TCGv arg, int reg, int sel)
ctx->CP0_LLAddr_shift); ctx->CP0_LLAddr_shift);
rn = "LLAddr"; rn = "LLAddr";
break; break;
case 1:
CP0_CHECK(ctx->mrp);
gen_helper_mfhc0_maar(arg, cpu_env);
rn = "MAAR";
break;
default: default:
goto mfhc0_read_zero; goto cp0_unimplemented;
} }
break; break;
case 28: case 28:
@ -4824,18 +4836,18 @@ static void gen_mfhc0(DisasContext *ctx, TCGv arg, int reg, int sel)
rn = "TagLo"; rn = "TagLo";
break; break;
default: default:
goto mfhc0_read_zero; goto cp0_unimplemented;
} }
break; break;
default: default:
goto mfhc0_read_zero; goto cp0_unimplemented;
} }
(void)rn; /* avoid a compiler warning */ (void)rn; /* avoid a compiler warning */
LOG_DISAS("mfhc0 %s (reg %d sel %d)\n", rn, reg, sel); LOG_DISAS("mfhc0 %s (reg %d sel %d)\n", rn, reg, sel);
return; return;
mfhc0_read_zero: cp0_unimplemented:
LOG_DISAS("mfhc0 %s (reg %d sel %d)\n", rn, reg, sel); LOG_DISAS("mfhc0 %s (reg %d sel %d)\n", rn, reg, sel);
tcg_gen_movi_tl(arg, 0); 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"; const char *rn = "invalid";
uint64_t mask = ctx->PAMask >> 36; uint64_t mask = ctx->PAMask >> 36;
if (!(ctx->hflags & MIPS_HFLAG_ELPA)) { CP0_CHECK(ctx->hflags & MIPS_HFLAG_ELPA);
goto mthc0_nop;
}
switch (reg) { switch (reg) {
case 2: case 2:
@ -4858,7 +4868,7 @@ static void gen_mthc0(DisasContext *ctx, TCGv arg, int reg, int sel)
rn = "EntryLo0"; rn = "EntryLo0";
break; break;
default: default:
goto mthc0_nop; goto cp0_unimplemented;
} }
break; break;
case 3: case 3:
@ -4869,7 +4879,7 @@ static void gen_mthc0(DisasContext *ctx, TCGv arg, int reg, int sel)
rn = "EntryLo1"; rn = "EntryLo1";
break; break;
default: default:
goto mthc0_nop; goto cp0_unimplemented;
} }
break; break;
case 17: case 17:
@ -4881,8 +4891,13 @@ static void gen_mthc0(DisasContext *ctx, TCGv arg, int reg, int sel)
treating MTHC0 to LLAddr as NOP. */ treating MTHC0 to LLAddr as NOP. */
rn = "LLAddr"; rn = "LLAddr";
break; break;
case 1:
CP0_CHECK(ctx->mrp);
gen_helper_mthc0_maar(cpu_env, arg);
rn = "MAAR";
break;
default: default:
goto mthc0_nop; goto cp0_unimplemented;
} }
break; break;
case 28: case 28:
@ -4896,15 +4911,15 @@ static void gen_mthc0(DisasContext *ctx, TCGv arg, int reg, int sel)
rn = "TagLo"; rn = "TagLo";
break; break;
default: default:
goto mthc0_nop; goto cp0_unimplemented;
} }
break; break;
default: default:
goto mthc0_nop; goto cp0_unimplemented;
} }
(void)rn; /* avoid a compiler warning */ (void)rn; /* avoid a compiler warning */
mthc0_nop: cp0_unimplemented:
LOG_DISAS("mthc0 %s (reg %d sel %d)\n", rn, reg, sel); 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) static void gen_mfc0(DisasContext *ctx, TCGv arg, int reg, int sel)
{ {
const char *rn = "invalid"; 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)); gen_mfc0_load32(arg, offsetof(CPUMIPSState, CP0_EBase));
rn = "EBase"; rn = "EBase";
break; 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: default:
goto cp0_unimplemented; 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); gen_helper_mfc0_lladdr(arg, cpu_env);
rn = "LLAddr"; rn = "LLAddr";
break; 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: default:
goto cp0_unimplemented; goto cp0_unimplemented;
} }
@ -5478,8 +5503,14 @@ static void gen_mfc0(DisasContext *ctx, TCGv arg, int reg, int sel)
} }
break; break;
case 26: case 26:
tcg_gen_movi_tl(arg, 0); /* unimplemented */ switch (sel) {
rn = "ECC"; case 0:
gen_mfc0_load32(arg, offsetof(CPUMIPSState, CP0_ErrCtl));
rn = "ErrCtl";
break;
default:
goto cp0_unimplemented;
}
break; break;
case 27: case 27:
switch (sel) { 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); gen_helper_mtc0_lladdr(cpu_env, arg);
rn = "LLAddr"; rn = "LLAddr";
break; 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: default:
goto cp0_unimplemented; goto cp0_unimplemented;
} }
@ -6128,8 +6169,15 @@ static void gen_mtc0(DisasContext *ctx, TCGv arg, int reg, int sel)
} }
break; break;
case 26: case 26:
/* ignored */ switch (sel) {
rn = "ECC"; case 0:
gen_helper_mtc0_errctl(cpu_env, arg);
ctx->bstate = BS_STOP;
rn = "ErrCtl";
break;
default:
goto cp0_unimplemented;
}
break; break;
case 27: case 27:
switch (sel) { 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)); gen_mfc0_load32(arg, offsetof(CPUMIPSState, CP0_EBase));
rn = "EBase"; rn = "EBase";
break; 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: default:
goto cp0_unimplemented; 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); gen_helper_dmfc0_lladdr(arg, cpu_env);
rn = "LLAddr"; rn = "LLAddr";
break; 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: default:
goto cp0_unimplemented; goto cp0_unimplemented;
} }
@ -6748,8 +6812,14 @@ static void gen_dmfc0(DisasContext *ctx, TCGv arg, int reg, int sel)
} }
break; break;
case 26: case 26:
tcg_gen_movi_tl(arg, 0); /* unimplemented */ switch (sel) {
rn = "ECC"; case 0:
gen_mfc0_load32(arg, offsetof(CPUMIPSState, CP0_ErrCtl));
rn = "ErrCtl";
break;
default:
goto cp0_unimplemented;
}
break; break;
case 27: case 27:
switch (sel) { 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); gen_helper_mtc0_lladdr(cpu_env, arg);
rn = "LLAddr"; rn = "LLAddr";
break; 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: default:
goto cp0_unimplemented; goto cp0_unimplemented;
} }
@ -7390,8 +7470,15 @@ static void gen_dmtc0(DisasContext *ctx, TCGv arg, int reg, int sel)
} }
break; break;
case 26: case 26:
/* ignored */ switch (sel) {
rn = "ECC"; case 0:
gen_helper_mtc0_errctl(cpu_env, arg);
ctx->bstate = BS_STOP;
rn = "ErrCtl";
break;
default:
goto cp0_unimplemented;
}
break; break;
case 27: case 27:
switch (sel) { switch (sel) {
@ -11176,6 +11263,15 @@ static void gen_addiupc (DisasContext *ctx, int rx, int imm,
tcg_temp_free(t0); 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) #if defined(TARGET_MIPS64)
static void decode_i64_mips16 (DisasContext *ctx, static void decode_i64_mips16 (DisasContext *ctx,
int ry, int funct, int16_t offset, int ry, int funct, int16_t offset,
@ -13727,7 +13823,9 @@ static void decode_micromips32_opc(CPUMIPSState *env, DisasContext *ctx)
switch (minor) { switch (minor) {
case CACHE: case CACHE:
check_cp0_enabled(ctx); check_cp0_enabled(ctx);
/* Treat as no-op. */ if (ctx->hflags & MIPS_HFLAG_ITC_CACHE) {
gen_cache_operation(ctx, rt, rs, imm);
}
break; break;
case LWC2: case LWC2:
case SWC2: case SWC2:
@ -17180,7 +17278,10 @@ static void decode_opc_special3_r6(CPUMIPSState *env, DisasContext *ctx)
/* Treat as NOP. */ /* Treat as NOP. */
break; break;
case R6_OPC_CACHE: 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; break;
case R6_OPC_SC: case R6_OPC_SC:
gen_st_cond(ctx, op1, rt, rs, imm); 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_insn_opc_removed(ctx, ISA_MIPS32R6);
check_cp0_enabled(ctx); check_cp0_enabled(ctx);
check_insn(ctx, ISA_MIPS3 | ISA_MIPS32); check_insn(ctx, ISA_MIPS3 | ISA_MIPS32);
if (ctx->hflags & MIPS_HFLAG_ITC_CACHE) {
gen_cache_operation(ctx, rt, rs, imm);
}
/* Treat as NOP. */ /* Treat as NOP. */
break; break;
case OPC_PREF: case OPC_PREF:
@ -19663,12 +19767,14 @@ void gen_intermediate_code(CPUMIPSState *env, struct TranslationBlock *tb)
ctx.PAMask = env->PAMask; ctx.PAMask = env->PAMask;
ctx.mvh = (env->CP0_Config5 >> CP0C5_MVH) & 1; ctx.mvh = (env->CP0_Config5 >> CP0C5_MVH) & 1;
ctx.CP0_LLAddr_shift = env->CP0_LLAddr_shift; ctx.CP0_LLAddr_shift = env->CP0_LLAddr_shift;
ctx.cmgcr = (env->CP0_Config3 >> CP0C3_CMGCR) & 1;
/* Restore delay slot state from the tb context. */ /* Restore delay slot state from the tb context. */
ctx.hflags = (uint32_t)tb->flags; /* FIXME: maybe use 64 bits here? */ ctx.hflags = (uint32_t)tb->flags; /* FIXME: maybe use 64 bits here? */
ctx.ulri = (env->CP0_Config3 >> CP0C3_ULRI) & 1; ctx.ulri = (env->CP0_Config3 >> CP0C3_ULRI) & 1;
ctx.ps = ((env->active_fpu.fcr0 >> FCR0_PS) & 1) || ctx.ps = ((env->active_fpu.fcr0 >> FCR0_PS) & 1) ||
(env->insn_flags & (INSN_LOONGSON2E | INSN_LOONGSON2F)); (env->insn_flags & (INSN_LOONGSON2E | INSN_LOONGSON2F));
ctx.vp = (env->CP0_Config5 >> CP0C5_VP) & 1; ctx.vp = (env->CP0_Config5 >> CP0C5_VP) & 1;
ctx.mrp = (env->CP0_Config5 >> CP0C5_MRP) & 1;
restore_cpu_state(env, &ctx); restore_cpu_state(env, &ctx);
#ifdef CONFIG_USER_ONLY #ifdef CONFIG_USER_ONLY
ctx.mem_idx = MIPS_HFLAG_UM; ctx.mem_idx = MIPS_HFLAG_UM;
@ -19962,6 +20068,16 @@ MIPSCPU *cpu_mips_init(const char *cpu_model)
return cpu; 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) void cpu_state_reset(CPUMIPSState *env)
{ {
MIPSCPU *cpu = mips_env_get_cpu(env); MIPSCPU *cpu = mips_env_get_cpu(env);
@ -20062,6 +20178,9 @@ void cpu_state_reset(CPUMIPSState *env)
} else { } else {
env->CP0_EBase |= 0x80000000; 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); env->CP0_Status = (1 << CP0St_BEV) | (1 << CP0St_ERL);
/* vectored interrupts not implemented, timer on int 7, /* vectored interrupts not implemented, timer on int 7,
no performance counters. */ 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) | .CP0_Config4 = MIPS_CONFIG4 | (1U << CP0C4_M) | (2 << CP0C4_IE) |
(0x1c << CP0C4_KScrExist), (0x1c << CP0C4_KScrExist),
.CP0_Config4_rw_bitmask = 0, .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) | .CP0_Config5_rw_bitmask = (1 << CP0C5_K) | (1 << CP0C5_CV) |
(1 << CP0C5_MSAEn) | (1 << CP0C5_UFE) | (1 << CP0C5_MSAEn) | (1 << CP0C5_UFE) |
(1 << CP0C5_FRE) | (1 << CP0C5_UFR), (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) | (2 << CP0C1_DS) | (4 << CP0C1_DL) | (3 << CP0C1_DA) |
(0 << CP0C1_PC) | (1 << CP0C1_WR) | (1 << CP0C1_EP), (0 << CP0C1_PC) | (1 << CP0C1_WR) | (1 << CP0C1_EP),
.CP0_Config2 = MIPS_CONFIG2, .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_BP) | (1 << CP0C3_BI) | (1 << CP0C3_ULRI) |
(1 << CP0C3_RXI) | (1 << CP0C3_LPA), (1 << CP0C3_RXI) | (1 << CP0C3_LPA),
.CP0_Config4 = MIPS_CONFIG4 | (1U << CP0C4_M) | (3 << CP0C4_IE) | .CP0_Config4 = MIPS_CONFIG4 | (1U << CP0C4_M) | (3 << CP0C4_IE) |