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:
commit
489ef4c810
@ -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
|
||||
|
@ -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
180
hw/mips/cps.c
Normal 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)
|
@ -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);
|
||||
|
@ -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
160
hw/misc/mips_cmgcr.c
Normal 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
177
hw/misc/mips_cpc.c
Normal 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
526
hw/misc/mips_itu.c
Normal 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
46
include/hw/mips/cps.h
Normal 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
|
59
include/hw/misc/mips_cmgcr.h
Normal file
59
include/hw/misc/mips_cmgcr.h
Normal 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 */
|
47
include/hw/misc/mips_cpc.h
Normal file
47
include/hw/misc/mips_cpc.h
Normal 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 */
|
72
include/hw/misc/mips_itu.h
Normal file
72
include/hw/misc/mips_itu.h
Normal 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 */
|
@ -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);
|
||||
|
@ -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)
|
||||
|
@ -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),
|
||||
|
@ -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,10 +1677,32 @@ 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)
|
||||
{
|
||||
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
|
||||
}
|
||||
|
@ -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. */
|
||||
|
@ -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) |
|
||||
|
Loading…
Reference in New Issue
Block a user