aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorPeter Maydell <peter.maydell@linaro.org>2020-10-19 10:52:56 +0100
committerPeter Maydell <peter.maydell@linaro.org>2020-10-19 10:52:57 +0100
commit22d30b340aa5d8a2b1fbc90d5263f801f1584d01 (patch)
treede271aafb22a72bd5ccecf151e463c29b4b00524
parent782d7b30dd8e27ba24346e7c411b476db88b59e7 (diff)
parent68fa519a6cb455005317bd61f95214b58b2f1e69 (diff)
Merge remote-tracking branch 'remotes/philmd-gitlab/tags/mips-next-20201017' into staging
MIPS patches queue . Fix some comment spelling errors . Demacro some TCG helpers . Add loongson-ext lswc2/lsdc2 group of instructions . Log unimplemented cache opcode . Increase number of TLB entries on the 34Kf core . Allow the CPU to use dynamic frequencies . Calculate the CP0 timer period using the CPU frequency . Set CPU frequency for each machine . Fix Malta FPGA I/O region size . Allow running qtests when ROM is missing . Add record/replay acceptance tests . Update MIPS CPU documentation . MAINTAINERS updates CI jobs results: https://gitlab.com/philmd/qemu/-/pipelines/203931842 https://travis-ci.org/github/philmd/qemu/builds/736491461 https://cirrus-ci.com/build/6272264062631936 https://app.shippable.com/github/philmd/qemu/runs/886/summary/console # gpg: Signature made Sat 17 Oct 2020 14:59:53 BST # gpg: using RSA key FAABE75E12917221DCFD6BB2E3E32C2CDEADC0DE # gpg: Good signature from "Philippe Mathieu-Daudé (F4BUG) <f4bug@amsat.org>" [full] # Primary key fingerprint: FAAB E75E 1291 7221 DCFD 6BB2 E3E3 2C2C DEAD C0DE * remotes/philmd-gitlab/tags/mips-next-20201017: (44 commits) target/mips: Increase number of TLB entries on the 34Kf core (16 -> 64) MAINTAINERS: Remove duplicated Malta test entries MAINTAINERS: Downgrade MIPS Boston to 'Odd Fixes', fix Paul Burton mail MAINTAINERS: Put myself forward for MIPS target MAINTAINERS: Remove myself docs/system: Update MIPS CPU documentation tests/acceptance: Add MIPS record/replay tests hw/mips: Remove exit(1) in case of missing ROM hw/mips: Rename TYPE_MIPS_BOSTON to TYPE_BOSTON hw/mips: Simplify code using ROUND_UP(INITRD_PAGE_SIZE) hw/mips: Simplify loading 64-bit ELF kernels hw/mips/malta: Use clearer qdev style hw/mips/malta: Move gt64120 related code together hw/mips/malta: Fix FPGA I/O region size target/mips/cpu: Display warning when CPU is used without input clock hw/mips/cps: Do not allow use without input clock hw/mips/malta: Set CPU frequency to 320 MHz hw/mips/boston: Set CPU frequency to 1 GHz hw/mips/cps: Expose input clock and connect it to CPU cores hw/mips/jazz: Correct CPU frequencies ... Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
-rw-r--r--MAINTAINERS25
-rw-r--r--docs/system/cpu-models-mips.rst.inc10
-rw-r--r--hw/core/clock.c15
-rw-r--r--hw/core/qdev-clock.c11
-rw-r--r--hw/mips/boston.c21
-rw-r--r--hw/mips/cps.c9
-rw-r--r--hw/mips/fuloong2e.c18
-rw-r--r--hw/mips/jazz.c23
-rw-r--r--hw/mips/malta.c59
-rw-r--r--hw/mips/mipssim.c30
-rw-r--r--hw/mips/r4k.c16
-rw-r--r--include/hw/clock.h13
-rw-r--r--include/hw/mips/cps.h2
-rw-r--r--include/hw/mips/mips.h4
-rw-r--r--include/qemu/cutils.h12
-rw-r--r--target/mips/cp0_helper.c25
-rw-r--r--target/mips/cp0_timer.c51
-rw-r--r--target/mips/cpu.c55
-rw-r--r--target/mips/cpu.h26
-rw-r--r--target/mips/fpu_helper.c220
-rw-r--r--target/mips/internal.h4
-rw-r--r--target/mips/op_helper.c27
-rw-r--r--target/mips/translate.c453
-rw-r--r--target/mips/translate_init.c.inc4
-rw-r--r--tests/acceptance/replay_kernel.py167
-rw-r--r--util/cutils.c14
26 files changed, 1119 insertions, 195 deletions
diff --git a/MAINTAINERS b/MAINTAINERS
index 99ab02bbab..a7f0acf866 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -220,11 +220,11 @@ F: hw/microblaze/
F: disas/microblaze.c
MIPS TCG CPUs
-M: Aleksandar Markovic <aleksandar.qemu.devel@gmail.com>
+M: Philippe Mathieu-Daudé <f4bug@amsat.org>
R: Aurelien Jarno <aurelien@aurel32.net>
R: Jiaxun Yang <jiaxun.yang@flygoat.com>
R: Aleksandar Rikalo <aleksandar.rikalo@syrmia.com>
-S: Maintained
+S: Odd Fixes
F: target/mips/
F: default-configs/*mips*
F: disas/*mips*
@@ -237,8 +237,6 @@ F: include/hw/intc/mips_gic.h
F: include/hw/mips/
F: include/hw/misc/mips_*
F: include/hw/timer/mips_gictimer.h
-F: tests/acceptance/linux_ssh_mips_malta.py
-F: tests/acceptance/machine_mips_malta.py
F: tests/tcg/mips/
K: ^Subject:.*(?i)mips
@@ -386,7 +384,6 @@ F: target/arm/kvm.c
MIPS KVM CPUs
M: Huacai Chen <chenhc@lemote.com>
-M: Aleksandar Markovic <aleksandar.qemu.devel@gmail.com>
S: Odd Fixes
F: target/mips/kvm.c
@@ -1123,10 +1120,9 @@ F: hw/display/jazz_led.c
F: hw/dma/rc4030.c
Malta
-M: Aleksandar Markovic <aleksandar.qemu.devel@gmail.com>
M: Philippe Mathieu-Daudé <f4bug@amsat.org>
R: Aurelien Jarno <aurelien@aurel32.net>
-S: Maintained
+S: Odd Fixes
F: hw/isa/piix4.c
F: hw/acpi/piix4.c
F: hw/mips/malta.c
@@ -1136,14 +1132,12 @@ F: tests/acceptance/linux_ssh_mips_malta.py
F: tests/acceptance/machine_mips_malta.py
Mipssim
-M: Aleksandar Markovic <aleksandar.qemu.devel@gmail.com>
R: Aleksandar Rikalo <aleksandar.rikalo@syrmia.com>
-S: Odd Fixes
+S: Orphaned
F: hw/mips/mipssim.c
F: hw/net/mipsnet.c
R4000
-M: Aleksandar Markovic <aleksandar.qemu.devel@gmail.com>
R: Aurelien Jarno <aurelien@aurel32.net>
R: Aleksandar Rikalo <aleksandar.rikalo@syrmia.com>
S: Obsolete
@@ -1152,7 +1146,6 @@ F: hw/mips/r4k.c
Fuloong 2E
M: Huacai Chen <chenhc@lemote.com>
M: Philippe Mathieu-Daudé <f4bug@amsat.org>
-M: Aleksandar Markovic <aleksandar.qemu.devel@gmail.com>
R: Jiaxun Yang <jiaxun.yang@flygoat.com>
S: Odd Fixes
F: hw/mips/fuloong2e.c
@@ -1167,9 +1160,9 @@ S: Maintained
F: hw/intc/loongson_liointc.c
Boston
-M: Paul Burton <pburton@wavecomp.com>
+M: Paul Burton <paulburton@kernel.org>
R: Aleksandar Rikalo <aleksandar.rikalo@syrmia.com>
-S: Maintained
+S: Odd Fixes
F: hw/core/loader-fit.c
F: hw/mips/boston.c
F: hw/pci-host/xilinx-pcie.c
@@ -2823,12 +2816,12 @@ F: tcg/i386/
F: disas/i386.c
MIPS TCG target
-M: Aleksandar Markovic <aleksandar.qemu.devel@gmail.com>
+M: Philippe Mathieu-Daudé <f4bug@amsat.org>
R: Aurelien Jarno <aurelien@aurel32.net>
R: Huacai Chen <chenhc@lemote.com>
R: Jiaxun Yang <jiaxun.yang@flygoat.com>
R: Aleksandar Rikalo <aleksandar.rikalo@syrmia.com>
-S: Maintained
+S: Odd Fixes
F: tcg/mips/
PPC TCG target
@@ -3169,7 +3162,7 @@ S: Odd Fixes
F: scripts/git-submodule.sh
UI translations
-M: Aleksandar Markovic <aleksandar.qemu.devel@gmail.com>
+S: Orphaned
F: po/*.po
Sphinx documentation configuration and build machinery
diff --git a/docs/system/cpu-models-mips.rst.inc b/docs/system/cpu-models-mips.rst.inc
index 499b5b6fed..02cc4bb884 100644
--- a/docs/system/cpu-models-mips.rst.inc
+++ b/docs/system/cpu-models-mips.rst.inc
@@ -48,11 +48,17 @@ across all desired hosts.
``I6400``
MIPS64 Processor (Release 6, 2014)
+``Loongson-2E``
+ MIPS64 Processor (Loongson 2, 2006)
+
``Loongson-2F``
MIPS64 Processor (Loongson 2, 2008)
-``Loongson-2E``
- MIPS64 Processor (Loongson 2, 2006)
+``Loongson-3A1000``
+ MIPS64 Processor (Loongson 3, 2010)
+
+``Loongson-3A4000``
+ MIPS64 Processor (Loongson 3, 2018)
``mips64dspr2``
MIPS64 Processor (Release 2, 2006)
diff --git a/hw/core/clock.c b/hw/core/clock.c
index 7066282f7b..f866717a83 100644
--- a/hw/core/clock.c
+++ b/hw/core/clock.c
@@ -23,6 +23,21 @@ void clock_setup_canonical_path(Clock *clk)
clk->canonical_path = object_get_canonical_path(OBJECT(clk));
}
+Clock *clock_new(Object *parent, const char *name)
+{
+ Object *obj;
+ Clock *clk;
+
+ obj = object_new(TYPE_CLOCK);
+ object_property_add_child(parent, name, obj);
+ object_unref(obj);
+
+ clk = CLOCK(obj);
+ clock_setup_canonical_path(clk);
+
+ return clk;
+}
+
void clock_set_callback(Clock *clk, ClockCallback *cb, void *opaque)
{
clk->callback = cb;
diff --git a/hw/core/qdev-clock.c b/hw/core/qdev-clock.c
index 47ecb5b4fa..6a9a340d0f 100644
--- a/hw/core/qdev-clock.c
+++ b/hw/core/qdev-clock.c
@@ -12,6 +12,7 @@
*/
#include "qemu/osdep.h"
+#include "qemu/error-report.h"
#include "hw/qdev-clock.h"
#include "hw/qdev-core.h"
#include "qapi/error.h"
@@ -153,6 +154,11 @@ Clock *qdev_get_clock_in(DeviceState *dev, const char *name)
assert(name);
ncl = qdev_get_clocklist(dev, name);
+ if (!ncl) {
+ error_report("Can not find clock-in '%s' for device type '%s'",
+ name, object_get_typename(OBJECT(dev)));
+ abort();
+ }
assert(!ncl->output);
return ncl->clock;
@@ -165,6 +171,11 @@ Clock *qdev_get_clock_out(DeviceState *dev, const char *name)
assert(name);
ncl = qdev_get_clocklist(dev, name);
+ if (!ncl) {
+ error_report("Can not find clock-out '%s' for device type '%s'",
+ name, object_get_typename(OBJECT(dev)));
+ abort();
+ }
assert(ncl->output);
return ncl->clock;
diff --git a/hw/mips/boston.c b/hw/mips/boston.c
index 1b3f69e949..74c18edbb3 100644
--- a/hw/mips/boston.c
+++ b/hw/mips/boston.c
@@ -30,6 +30,7 @@
#include "hw/mips/cps.h"
#include "hw/mips/cpudevs.h"
#include "hw/pci-host/xilinx-pcie.h"
+#include "hw/qdev-clock.h"
#include "hw/qdev-properties.h"
#include "qapi/error.h"
#include "qemu/error-report.h"
@@ -43,10 +44,10 @@
#include <libfdt.h>
#include "qom/object.h"
-#define TYPE_MIPS_BOSTON "mips-boston"
+#define TYPE_BOSTON "mips-boston"
typedef struct BostonState BostonState;
DECLARE_INSTANCE_CHECKER(BostonState, BOSTON,
- TYPE_MIPS_BOSTON)
+ TYPE_BOSTON)
struct BostonState {
SysBusDevice parent_obj;
@@ -54,6 +55,7 @@ struct BostonState {
MachineState *mach;
MIPSCPSState cps;
SerialMM *uart;
+ Clock *cpuclk;
CharBackend lcd_display;
char lcd_content[8];
@@ -251,10 +253,19 @@ static const MemoryRegionOps boston_platreg_ops = {
.endianness = DEVICE_NATIVE_ENDIAN,
};
+static void mips_boston_instance_init(Object *obj)
+{
+ BostonState *s = BOSTON(obj);
+
+ s->cpuclk = qdev_init_clock_out(DEVICE(obj), "cpu-refclk");
+ clock_set_hz(s->cpuclk, 1000000000); /* 1 GHz */
+}
+
static const TypeInfo boston_device = {
- .name = TYPE_MIPS_BOSTON,
+ .name = TYPE_BOSTON,
.parent = TYPE_SYS_BUS_DEVICE,
.instance_size = sizeof(BostonState),
+ .instance_init = mips_boston_instance_init,
};
static void boston_register_types(void)
@@ -444,7 +455,7 @@ static void boston_mach_init(MachineState *machine)
exit(1);
}
- dev = qdev_new(TYPE_MIPS_BOSTON);
+ dev = qdev_new(TYPE_BOSTON);
sysbus_realize_and_unref(SYS_BUS_DEVICE(dev), &error_fatal);
s = BOSTON(dev);
@@ -462,6 +473,8 @@ static void boston_mach_init(MachineState *machine)
&error_fatal);
object_property_set_int(OBJECT(&s->cps), "num-vp", machine->smp.cpus,
&error_fatal);
+ qdev_connect_clock_in(DEVICE(&s->cps), "clk-in",
+ qdev_get_clock_out(dev, "cpu-refclk"));
sysbus_realize(SYS_BUS_DEVICE(&s->cps), &error_fatal);
sysbus_mmio_map_overlap(SYS_BUS_DEVICE(&s->cps), 0, 0, 1);
diff --git a/hw/mips/cps.c b/hw/mips/cps.c
index 23c0f87e41..c624821315 100644
--- a/hw/mips/cps.c
+++ b/hw/mips/cps.c
@@ -22,6 +22,7 @@
#include "qemu/module.h"
#include "hw/mips/cps.h"
#include "hw/mips/mips.h"
+#include "hw/qdev-clock.h"
#include "hw/qdev-properties.h"
#include "hw/mips/cpudevs.h"
#include "sysemu/kvm.h"
@@ -38,6 +39,7 @@ static void mips_cps_init(Object *obj)
SysBusDevice *sbd = SYS_BUS_DEVICE(obj);
MIPSCPSState *s = MIPS_CPS(obj);
+ s->clock = qdev_init_clock_in(DEVICE(obj), "clk-in", NULL, NULL);
/*
* Cover entire address space as there do not seem to be any
* constraints for the base address of CPC and GIC.
@@ -72,6 +74,11 @@ static void mips_cps_realize(DeviceState *dev, Error **errp)
bool itu_present = false;
bool saar_present = false;
+ if (!clock_get(s->clock)) {
+ error_setg(errp, "CPS input clock is not connected to an output clock");
+ return;
+ }
+
for (i = 0; i < s->num_vp; i++) {
cpu = MIPS_CPU(object_new(s->cpu_type));
@@ -80,6 +87,8 @@ static void mips_cps_realize(DeviceState *dev, Error **errp)
errp)) {
return;
}
+ /* All cores use the same clock tree */
+ qdev_connect_clock_in(DEVICE(cpu), "clk-in", s->clock);
if (!qdev_realize_and_unref(DEVICE(cpu), NULL, errp)) {
return;
diff --git a/hw/mips/fuloong2e.c b/hw/mips/fuloong2e.c
index b000ed1d7f..a9e0c2f8d3 100644
--- a/hw/mips/fuloong2e.c
+++ b/hw/mips/fuloong2e.c
@@ -23,6 +23,7 @@
#include "qemu/units.h"
#include "qapi/error.h"
#include "cpu.h"
+#include "hw/clock.h"
#include "hw/intc/i8259.h"
#include "hw/dma/i8257.h"
#include "hw/isa/superio.h"
@@ -132,8 +133,7 @@ static int64_t load_kernel(CPUMIPSState *env)
if (loaderparams.initrd_filename) {
initrd_size = get_image_size(loaderparams.initrd_filename);
if (initrd_size > 0) {
- initrd_offset = (kernel_high + ~INITRD_PAGE_MASK) &
- INITRD_PAGE_MASK;
+ initrd_offset = ROUND_UP(kernel_high, INITRD_PAGE_SIZE);
if (initrd_offset + initrd_size > ram_size) {
error_report("memory too small for initial ram disk '%s'",
loaderparams.initrd_filename);
@@ -298,12 +298,16 @@ static void mips_fuloong2e_init(MachineState *machine)
PCIBus *pci_bus;
ISABus *isa_bus;
I2CBus *smbus;
+ Clock *cpuclk;
MIPSCPU *cpu;
CPUMIPSState *env;
DeviceState *dev;
+ cpuclk = clock_new(OBJECT(machine), "cpu-refclk");
+ clock_set_hz(cpuclk, 533080000); /* ~533 MHz */
+
/* init CPUs */
- cpu = MIPS_CPU(cpu_create(machine->cpu_type));
+ cpu = mips_cpu_create_with_clock(machine->cpu_type, cpuclk);
env = &cpu->env;
qemu_register_reset(main_cpu_reset, cpu);
@@ -333,10 +337,8 @@ static void mips_fuloong2e_init(MachineState *machine)
kernel_entry = load_kernel(env);
write_bootloader(env, memory_region_get_ram_ptr(bios), kernel_entry);
} else {
- if (bios_name == NULL) {
- bios_name = FULOONG_BIOSNAME;
- }
- filename = qemu_find_file(QEMU_FILE_TYPE_BIOS, bios_name);
+ filename = qemu_find_file(QEMU_FILE_TYPE_BIOS,
+ bios_name ?: FULOONG_BIOSNAME);
if (filename) {
bios_size = load_image_targphys(filename, 0x1fc00000LL,
BIOS_SIZE);
@@ -346,7 +348,7 @@ static void mips_fuloong2e_init(MachineState *machine)
}
if ((bios_size < 0 || bios_size > BIOS_SIZE) &&
- !kernel_filename && !qtest_enabled()) {
+ bios_name && !qtest_enabled()) {
error_report("Could not load MIPS bios '%s'", bios_name);
exit(1);
}
diff --git a/hw/mips/jazz.c b/hw/mips/jazz.c
index 47723093b6..71448f72ac 100644
--- a/hw/mips/jazz.c
+++ b/hw/mips/jazz.c
@@ -24,6 +24,7 @@
#include "qemu/osdep.h"
#include "qemu-common.h"
+#include "hw/clock.h"
#include "hw/mips/mips.h"
#include "hw/mips/cpudevs.h"
#include "hw/intc/i8259.h"
@@ -142,6 +143,7 @@ static void mips_jazz_init(MachineState *machine,
MemoryRegion *address_space = get_system_memory();
char *filename;
int bios_size, n;
+ Clock *cpuclk;
MIPSCPU *cpu;
CPUClass *cc;
CPUMIPSState *env;
@@ -163,14 +165,25 @@ static void mips_jazz_init(MachineState *machine,
MemoryRegion *bios2 = g_new(MemoryRegion, 1);
SysBusESPState *sysbus_esp;
ESPState *esp;
+ static const struct {
+ unsigned freq_hz;
+ unsigned pll_mult;
+ } ext_clk[] = {
+ [JAZZ_MAGNUM] = {50000000, 2},
+ [JAZZ_PICA61] = {33333333, 4},
+ };
if (machine->ram_size > 256 * MiB) {
error_report("RAM size more than 256Mb is not supported");
exit(EXIT_FAILURE);
}
+ cpuclk = clock_new(OBJECT(machine), "cpu-refclk");
+ clock_set_hz(cpuclk, ext_clk[jazz_model].freq_hz
+ * ext_clk[jazz_model].pll_mult);
+
/* init CPUs */
- cpu = MIPS_CPU(cpu_create(machine->cpu_type));
+ cpu = mips_cpu_create_with_clock(machine->cpu_type, cpuclk);
env = &cpu->env;
qemu_register_reset(main_cpu_reset, cpu);
@@ -205,10 +218,7 @@ static void mips_jazz_init(MachineState *machine,
memory_region_add_subregion(address_space, 0xfff00000LL, bios2);
/* load the BIOS image. */
- if (bios_name == NULL) {
- bios_name = BIOS_FILENAME;
- }
- filename = qemu_find_file(QEMU_FILE_TYPE_BIOS, bios_name);
+ filename = qemu_find_file(QEMU_FILE_TYPE_BIOS, bios_name ?: BIOS_FILENAME);
if (filename) {
bios_size = load_image_targphys(filename, 0xfff00000LL,
MAGNUM_BIOS_SIZE);
@@ -216,7 +226,8 @@ static void mips_jazz_init(MachineState *machine,
} else {
bios_size = -1;
}
- if ((bios_size < 0 || bios_size > MAGNUM_BIOS_SIZE) && !qtest_enabled()) {
+ if ((bios_size < 0 || bios_size > MAGNUM_BIOS_SIZE)
+ && bios_name && !qtest_enabled()) {
error_report("Could not load MIPS bios '%s'", bios_name);
exit(1);
}
diff --git a/hw/mips/malta.c b/hw/mips/malta.c
index 4019c9dc1a..9d1a3b50b7 100644
--- a/hw/mips/malta.c
+++ b/hw/mips/malta.c
@@ -26,6 +26,7 @@
#include "qemu/units.h"
#include "qemu-common.h"
#include "cpu.h"
+#include "hw/clock.h"
#include "hw/southbridge/piix.h"
#include "hw/isa/superio.h"
#include "hw/char/serial.h"
@@ -57,6 +58,7 @@
#include "sysemu/kvm.h"
#include "hw/semihosting/semihost.h"
#include "hw/mips/cps.h"
+#include "hw/qdev-clock.h"
#define ENVP_ADDR 0x80002000l
#define ENVP_NB_ENTRIES 16
@@ -94,6 +96,7 @@ OBJECT_DECLARE_SIMPLE_TYPE(MaltaState, MIPS_MALTA)
struct MaltaState {
SysBusDevice parent_obj;
+ Clock *cpuclk;
MIPSCPSState cps;
qemu_irq i8259[ISA_NUM_IRQS];
};
@@ -575,7 +578,7 @@ static MaltaFPGAState *malta_fpga_init(MemoryRegion *address_space,
memory_region_init_alias(&s->iomem_lo, NULL, "malta-fpga",
&s->iomem, 0, 0x900);
memory_region_init_alias(&s->iomem_hi, NULL, "malta-fpga",
- &s->iomem, 0xa00, 0x10000 - 0xa00);
+ &s->iomem, 0xa00, 0x100000 - 0xa00);
memory_region_add_subregion(address_space, base, &s->iomem_lo);
memory_region_add_subregion(address_space, base + 0xa00, &s->iomem_hi);
@@ -1074,9 +1077,9 @@ static int64_t load_kernel(void)
* the initrd. It takes at most 128kiB for 2GB RAM and 4kiB
* pages.
*/
- initrd_offset = (loaderparams.ram_low_size - initrd_size
- - (128 * KiB)
- - ~INITRD_PAGE_MASK) & INITRD_PAGE_MASK;
+ initrd_offset = ROUND_UP(loaderparams.ram_low_size
+ - (initrd_size + 128 * KiB),
+ INITRD_PAGE_SIZE);
if (kernel_high >= initrd_offset) {
error_report("memory too small for initial ram disk '%s'",
loaderparams.initrd_filename);
@@ -1159,7 +1162,7 @@ static void main_cpu_reset(void *opaque)
}
}
-static void create_cpu_without_cps(MachineState *ms,
+static void create_cpu_without_cps(MachineState *ms, MaltaState *s,
qemu_irq *cbus_irq, qemu_irq *i8259_irq)
{
CPUMIPSState *env;
@@ -1167,7 +1170,7 @@ static void create_cpu_without_cps(MachineState *ms,
int i;
for (i = 0; i < ms->smp.cpus; i++) {
- cpu = MIPS_CPU(cpu_create(ms->cpu_type));
+ cpu = mips_cpu_create_with_clock(ms->cpu_type, s->cpuclk);
/* Init internal devices */
cpu_mips_irq_init_cpu(cpu);
@@ -1189,6 +1192,7 @@ static void create_cps(MachineState *ms, MaltaState *s,
&error_fatal);
object_property_set_int(OBJECT(&s->cps), "num-vp", ms->smp.cpus,
&error_fatal);
+ qdev_connect_clock_in(DEVICE(&s->cps), "clk-in", s->cpuclk);
sysbus_realize(SYS_BUS_DEVICE(&s->cps), &error_fatal);
sysbus_mmio_map_overlap(SYS_BUS_DEVICE(&s->cps), 0, 0, 1);
@@ -1203,7 +1207,7 @@ static void mips_create_cpu(MachineState *ms, MaltaState *s,
if ((ms->smp.cpus > 1) && cpu_supports_cps_smp(ms->cpu_type)) {
create_cps(ms, s, cbus_irq, i8259_irq);
} else {
- create_cpu_without_cps(ms, cbus_irq, i8259_irq);
+ create_cpu_without_cps(ms, s, cbus_irq, i8259_irq);
}
}
@@ -1231,18 +1235,11 @@ void mips_malta_init(MachineState *machine)
DriveInfo *dinfo;
int fl_idx = 0;
int be;
+ MaltaState *s;
+ DeviceState *dev;
- DeviceState *dev = qdev_new(TYPE_MIPS_MALTA);
- MaltaState *s = MIPS_MALTA(dev);
-
- /*
- * The whole address space decoded by the GT-64120A doesn't generate
- * exception when accessing invalid memory. Create an empty slot to
- * emulate this feature.
- */
- empty_slot_init("GT64120", 0, 0x20000000);
-
- sysbus_realize_and_unref(SYS_BUS_DEVICE(dev), &error_fatal);
+ s = MIPS_MALTA(qdev_new(TYPE_MIPS_MALTA));
+ sysbus_realize_and_unref(SYS_BUS_DEVICE(s), &error_fatal);
/* create CPU */
mips_create_cpu(machine, s, &cbus_irq, &i8259_irq);
@@ -1335,10 +1332,8 @@ void mips_malta_init(MachineState *machine)
/* Load firmware from flash. */
if (!dinfo) {
/* Load a BIOS image. */
- if (bios_name == NULL) {
- bios_name = BIOS_FILENAME;
- }
- filename = qemu_find_file(QEMU_FILE_TYPE_BIOS, bios_name);
+ filename = qemu_find_file(QEMU_FILE_TYPE_BIOS,
+ bios_name ?: BIOS_FILENAME);
if (filename) {
bios_size = load_image_targphys(filename, FLASH_ADDRESS,
BIOS_SIZE);
@@ -1347,9 +1342,8 @@ void mips_malta_init(MachineState *machine)
bios_size = -1;
}
if ((bios_size < 0 || bios_size > BIOS_SIZE) &&
- !kernel_filename && !qtest_enabled()) {
- error_report("Could not load MIPS bios '%s', and no "
- "-kernel argument was specified", bios_name);
+ bios_name && !qtest_enabled()) {
+ error_report("Could not load MIPS bios '%s'", bios_name);
exit(1);
}
}
@@ -1395,6 +1389,12 @@ void mips_malta_init(MachineState *machine)
/* Northbridge */
pci_bus = gt64120_register(s->i8259);
+ /*
+ * The whole address space decoded by the GT-64120A doesn't generate
+ * exception when accessing invalid memory. Create an empty slot to
+ * emulate this feature.
+ */
+ empty_slot_init("GT64120", 0, 0x20000000);
/* Southbridge */
dev = piix4_create(pci_bus, &isa_bus, &smbus);
@@ -1421,10 +1421,19 @@ void mips_malta_init(MachineState *machine)
pci_vga_init(pci_bus);
}
+static void mips_malta_instance_init(Object *obj)
+{
+ MaltaState *s = MIPS_MALTA(obj);
+
+ s->cpuclk = qdev_init_clock_out(DEVICE(obj), "cpu-refclk");
+ clock_set_hz(s->cpuclk, 320000000); /* 320 MHz */
+}
+
static const TypeInfo mips_malta_device = {
.name = TYPE_MIPS_MALTA,
.parent = TYPE_SYS_BUS_DEVICE,
.instance_size = sizeof(MaltaState),
+ .instance_init = mips_malta_instance_init,
};
static void mips_malta_machine_init(MachineClass *mc)
diff --git a/hw/mips/mipssim.c b/hw/mips/mipssim.c
index 5d4ad74828..aaa62a0f4b 100644
--- a/hw/mips/mipssim.c
+++ b/hw/mips/mipssim.c
@@ -29,6 +29,7 @@
#include "qapi/error.h"
#include "qemu-common.h"
#include "cpu.h"
+#include "hw/clock.h"
#include "hw/mips/mips.h"
#include "hw/mips/cpudevs.h"
#include "hw/char/serial.h"
@@ -76,11 +77,7 @@ static int64_t load_kernel(void)
(uint64_t *)&entry, NULL,
(uint64_t *)&kernel_high, NULL, big_endian,
EM_MIPS, 1, 0);
- if (kernel_size >= 0) {
- if ((entry & ~0x7fffffffULL) == 0x80000000) {
- entry = (int32_t)entry;
- }
- } else {
+ if (kernel_size < 0) {
error_report("could not load kernel '%s': %s",
loaderparams.kernel_filename,
load_elf_strerror(kernel_size));
@@ -93,8 +90,7 @@ static int64_t load_kernel(void)
if (loaderparams.initrd_filename) {
initrd_size = get_image_size(loaderparams.initrd_filename);
if (initrd_size > 0) {
- initrd_offset = (kernel_high + ~INITRD_PAGE_MASK) &
- INITRD_PAGE_MASK;
+ initrd_offset = ROUND_UP(kernel_high, INITRD_PAGE_SIZE);
if (initrd_offset + initrd_size > loaderparams.ram_size) {
error_report("memory too small for initial ram disk '%s'",
loaderparams.initrd_filename);
@@ -150,13 +146,21 @@ mips_mipssim_init(MachineState *machine)
MemoryRegion *address_space_mem = get_system_memory();
MemoryRegion *isa = g_new(MemoryRegion, 1);
MemoryRegion *bios = g_new(MemoryRegion, 1);
+ Clock *cpuclk;
MIPSCPU *cpu;
CPUMIPSState *env;
ResetData *reset_info;
int bios_size;
+ cpuclk = clock_new(OBJECT(machine), "cpu-refclk");
+#ifdef TARGET_MIPS64
+ clock_set_hz(cpuclk, 6000000); /* 6 MHz */
+#else
+ clock_set_hz(cpuclk, 12000000); /* 12 MHz */
+#endif
+
/* Init CPUs. */
- cpu = MIPS_CPU(cpu_create(machine->cpu_type));
+ cpu = mips_cpu_create_with_clock(machine->cpu_type, cpuclk);
env = &cpu->env;
reset_info = g_malloc0(sizeof(ResetData));
@@ -173,10 +177,7 @@ mips_mipssim_init(MachineState *machine)
/* Map the BIOS / boot exception handler. */
memory_region_add_subregion(address_space_mem, 0x1fc00000LL, bios);
/* Load a BIOS / boot exception handler image. */
- if (bios_name == NULL) {
- bios_name = BIOS_FILENAME;
- }
- filename = qemu_find_file(QEMU_FILE_TYPE_BIOS, bios_name);
+ filename = qemu_find_file(QEMU_FILE_TYPE_BIOS, bios_name ?: BIOS_FILENAME);
if (filename) {
bios_size = load_image_targphys(filename, 0x1fc00000LL, BIOS_SIZE);
g_free(filename);
@@ -184,10 +185,9 @@ mips_mipssim_init(MachineState *machine)
bios_size = -1;
}
if ((bios_size < 0 || bios_size > BIOS_SIZE) &&
- !kernel_filename && !qtest_enabled()) {
+ bios_name && !qtest_enabled()) {
/* Bail out if we have neither a kernel image nor boot vector code. */
- error_report("Could not load MIPS bios '%s', and no "
- "-kernel argument was specified", bios_name);
+ error_report("Could not load MIPS bios '%s'", bios_name);
exit(1);
} else {
/* We have a boot vector start address. */
diff --git a/hw/mips/r4k.c b/hw/mips/r4k.c
index 3487013a4a..3830854342 100644
--- a/hw/mips/r4k.c
+++ b/hw/mips/r4k.c
@@ -13,6 +13,7 @@
#include "qapi/error.h"
#include "qemu-common.h"
#include "cpu.h"
+#include "hw/clock.h"
#include "hw/mips/mips.h"
#include "hw/mips/cpudevs.h"
#include "hw/intc/i8259.h"
@@ -101,11 +102,7 @@ static int64_t load_kernel(void)
(uint64_t *)&entry, NULL,
(uint64_t *)&kernel_high, NULL, big_endian,
EM_MIPS, 1, 0);
- if (kernel_size >= 0) {
- if ((entry & ~0x7fffffffULL) == 0x80000000) {
- entry = (int32_t)entry;
- }
- } else {
+ if (kernel_size < 0) {
error_report("could not load kernel '%s': %s",
loaderparams.kernel_filename,
load_elf_strerror(kernel_size));
@@ -118,8 +115,7 @@ static int64_t load_kernel(void)
if (loaderparams.initrd_filename) {
initrd_size = get_image_size(loaderparams.initrd_filename);
if (initrd_size > 0) {
- initrd_offset = (kernel_high + ~INITRD_PAGE_MASK) &
- INITRD_PAGE_MASK;
+ initrd_offset = ROUND_UP(kernel_high, INITRD_PAGE_SIZE);
if (initrd_offset + initrd_size > ram_size) {
error_report("memory too small for initial ram disk '%s'",
loaderparams.initrd_filename);
@@ -182,6 +178,7 @@ void mips_r4k_init(MachineState *machine)
MemoryRegion *isa_io = g_new(MemoryRegion, 1);
MemoryRegion *isa_mem = g_new(MemoryRegion, 1);
int bios_size;
+ Clock *cpuclk;
MIPSCPU *cpu;
CPUMIPSState *env;
ResetData *reset_info;
@@ -192,8 +189,11 @@ void mips_r4k_init(MachineState *machine)
DriveInfo *dinfo;
int be;
+ cpuclk = clock_new(OBJECT(machine), "cpu-refclk");
+ clock_set_hz(cpuclk, 200000000); /* 200 MHz */
+
/* init CPUs */
- cpu = MIPS_CPU(cpu_create(machine->cpu_type));
+ cpu = mips_cpu_create_with_clock(machine->cpu_type, cpuclk);
env = &cpu->env;
reset_info = g_malloc0(sizeof(ResetData));
diff --git a/include/hw/clock.h b/include/hw/clock.h
index d357594df9..cbc5e6ced1 100644
--- a/include/hw/clock.h
+++ b/include/hw/clock.h
@@ -91,6 +91,19 @@ extern const VMStateDescription vmstate_clock;
void clock_setup_canonical_path(Clock *clk);
/**
+ * clock_new:
+ * @parent: the clock parent
+ * @name: the clock object name
+ *
+ * Helper function to create a new clock and parent it to @parent. There is no
+ * need to call clock_setup_canonical_path on the returned clock as it is done
+ * by this function.
+ *
+ * @return the newly created clock
+ */
+Clock *clock_new(Object *parent, const char *name);
+
+/**
* clock_set_callback:
* @clk: the clock to register the callback into
* @cb: the callback function
diff --git a/include/hw/mips/cps.h b/include/hw/mips/cps.h
index 9e35a88136..859a8d4a67 100644
--- a/include/hw/mips/cps.h
+++ b/include/hw/mips/cps.h
@@ -21,6 +21,7 @@
#define MIPS_CPS_H
#include "hw/sysbus.h"
+#include "hw/clock.h"
#include "hw/misc/mips_cmgcr.h"
#include "hw/intc/mips_gic.h"
#include "hw/misc/mips_cpc.h"
@@ -43,6 +44,7 @@ struct MIPSCPSState {
MIPSGICState gic;
MIPSCPCState cpc;
MIPSITUState itu;
+ Clock *clock;
};
qemu_irq get_cps_irq(MIPSCPSState *cps, int pin_number);
diff --git a/include/hw/mips/mips.h b/include/hw/mips/mips.h
index 0af4c3d5d7..6c9c8805f3 100644
--- a/include/hw/mips/mips.h
+++ b/include/hw/mips/mips.h
@@ -2,8 +2,10 @@
#define HW_MIPS_H
/* Definitions for mips board emulation. */
+#include "qemu/units.h"
+
/* Kernels can be configured with 64KB pages */
-#define INITRD_PAGE_MASK (~((1 << 16) - 1))
+#define INITRD_PAGE_SIZE (64 * KiB)
#include "exec/memory.h"
diff --git a/include/qemu/cutils.h b/include/qemu/cutils.h
index 3a86ec0321..4bbf4834ea 100644
--- a/include/qemu/cutils.h
+++ b/include/qemu/cutils.h
@@ -158,6 +158,18 @@ int qemu_strtosz_metric(const char *nptr, const char **end, uint64_t *result);
char *size_to_str(uint64_t val);
+/**
+ * freq_to_str:
+ * @freq_hz: frequency to stringify
+ *
+ * Return human readable string for frequency @freq_hz.
+ * Use SI units like KHz, MHz, and so forth.
+ *
+ * The caller is responsible for releasing the value returned
+ * with g_free() after use.
+ */
+char *freq_to_str(uint64_t freq_hz);
+
/* used to print char* safely */
#define STR_OR_NULL(str) ((str) ? (str) : "null")
diff --git a/target/mips/cp0_helper.c b/target/mips/cp0_helper.c
index de64add038..12143ac55b 100644
--- a/target/mips/cp0_helper.c
+++ b/target/mips/cp0_helper.c
@@ -203,6 +203,31 @@ static void sync_c0_entryhi(CPUMIPSState *cpu, int tc)
*tcst |= asid;
}
+/* XXX: do not use a global */
+uint32_t cpu_mips_get_random(CPUMIPSState *env)
+{
+ static uint32_t seed = 1;
+ static uint32_t prev_idx;
+ uint32_t idx;
+ uint32_t nb_rand_tlb = env->tlb->nb_tlb - env->CP0_Wired;
+
+ if (nb_rand_tlb == 1) {
+ return env->tlb->nb_tlb - 1;
+ }
+
+ /* Don't return same value twice, so get another value */
+ do {
+ /*
+ * Use a simple algorithm of Linear Congruential Generator
+ * from ISO/IEC 9899 standard.
+ */
+ seed = 1103515245 * seed + 12345;
+ idx = (seed >> 16) % nb_rand_tlb + env->CP0_Wired;
+ } while (idx == prev_idx);
+ prev_idx = idx;
+ return idx;
+}
+
/* CP0 helpers */
target_ulong helper_mfc0_mvpcontrol(CPUMIPSState *env)
{
diff --git a/target/mips/cp0_timer.c b/target/mips/cp0_timer.c
index bd7efb152d..5ec0d6249e 100644
--- a/target/mips/cp0_timer.c
+++ b/target/mips/cp0_timer.c
@@ -27,43 +27,17 @@
#include "sysemu/kvm.h"
#include "internal.h"
-#define TIMER_PERIOD 10 /* 10 ns period for 100 Mhz frequency */
-
-/* XXX: do not use a global */
-uint32_t cpu_mips_get_random(CPUMIPSState *env)
-{
- static uint32_t seed = 1;
- static uint32_t prev_idx = 0;
- uint32_t idx;
- uint32_t nb_rand_tlb = env->tlb->nb_tlb - env->CP0_Wired;
-
- if (nb_rand_tlb == 1) {
- return env->tlb->nb_tlb - 1;
- }
-
- /* Don't return same value twice, so get another value */
- do {
- /*
- * Use a simple algorithm of Linear Congruential Generator
- * from ISO/IEC 9899 standard.
- */
- seed = 1103515245 * seed + 12345;
- idx = (seed >> 16) % nb_rand_tlb + env->CP0_Wired;
- } while (idx == prev_idx);
- prev_idx = idx;
- return idx;
-}
-
/* MIPS R4K timer */
static void cpu_mips_timer_update(CPUMIPSState *env)
{
- uint64_t now, next;
+ uint64_t now_ns, next_ns;
uint32_t wait;
- now = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL);
- wait = env->CP0_Compare - env->CP0_Count - (uint32_t)(now / TIMER_PERIOD);
- next = now + (uint64_t)wait * TIMER_PERIOD;
- timer_mod(env->timer, next);
+ now_ns = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL);
+ wait = env->CP0_Compare - env->CP0_Count -
+ (uint32_t)(now_ns / env->cp0_count_ns);
+ next_ns = now_ns + (uint64_t)wait * env->cp0_count_ns;
+ timer_mod(env->timer, next_ns);
}
/* Expire the timer. */
@@ -81,16 +55,16 @@ uint32_t cpu_mips_get_count(CPUMIPSState *env)
if (env->CP0_Cause & (1 << CP0Ca_DC)) {
return env->CP0_Count;
} else {
- uint64_t now;
+ uint64_t now_ns;
- now = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL);
+ now_ns = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL);
if (timer_pending(env->timer)
- && timer_expired(env->timer, now)) {
+ && timer_expired(env->timer, now_ns)) {
/* The timer has already expired. */
cpu_mips_timer_expire(env);
}
- return env->CP0_Count + (uint32_t)(now / TIMER_PERIOD);
+ return env->CP0_Count + (uint32_t)(now_ns / env->cp0_count_ns);
}
}
@@ -106,7 +80,8 @@ void cpu_mips_store_count(CPUMIPSState *env, uint32_t count)
} else {
/* Store new count register */
env->CP0_Count = count -
- (uint32_t)(qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) / TIMER_PERIOD);
+ (uint32_t)(qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) /
+ env->cp0_count_ns);
/* Update timer timer */
cpu_mips_timer_update(env);
}
@@ -133,7 +108,7 @@ void cpu_mips_stop_count(CPUMIPSState *env)
{
/* Store the current value */
env->CP0_Count += (uint32_t)(qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) /
- TIMER_PERIOD);
+ env->cp0_count_ns);
}
static void mips_timer_cb(void *opaque)
diff --git a/target/mips/cpu.c b/target/mips/cpu.c
index e86cd06548..76d50b00b4 100644
--- a/target/mips/cpu.c
+++ b/target/mips/cpu.c
@@ -19,14 +19,17 @@
*/
#include "qemu/osdep.h"
+#include "qemu/cutils.h"
#include "qapi/error.h"
#include "cpu.h"
#include "internal.h"
#include "kvm_mips.h"
#include "qemu/module.h"
#include "sysemu/kvm.h"
+#include "sysemu/qtest.h"
#include "exec/exec-all.h"
-
+#include "hw/qdev-properties.h"
+#include "hw/qdev-clock.h"
static void mips_cpu_set_pc(CPUState *cs, vaddr value)
{
@@ -134,6 +137,21 @@ static void mips_cpu_disas_set_info(CPUState *s, disassemble_info *info)
}
}
+/*
+ * Since commit 6af0bf9c7c3 this model assumes a CPU clocked at 200MHz.
+ */
+#define CPU_FREQ_HZ_DEFAULT 200000000
+#define CP0_COUNT_RATE_DEFAULT 2
+
+static void mips_cp0_period_set(MIPSCPU *cpu)
+{
+ CPUMIPSState *env = &cpu->env;
+
+ env->cp0_count_ns = cpu->cp0_count_rate
+ * clock_get_ns(MIPS_CPU(cpu)->clock);
+ assert(env->cp0_count_ns);
+}
+
static void mips_cpu_realizefn(DeviceState *dev, Error **errp)
{
CPUState *cs = CPU(dev);
@@ -141,6 +159,20 @@ static void mips_cpu_realizefn(DeviceState *dev, Error **errp)
MIPSCPUClass *mcc = MIPS_CPU_GET_CLASS(dev);
Error *local_err = NULL;
+ if (!clock_get(cpu->clock)) {
+#ifndef CONFIG_USER_ONLY
+ if (!qtest_enabled()) {
+ g_autofree char *cpu_freq_str = freq_to_str(CPU_FREQ_HZ_DEFAULT);
+
+ warn_report("CPU input clock is not connected to any output clock, "
+ "using default frequency of %s.", cpu_freq_str);
+ }
+#endif
+ /* Initialize the frequency in case the clock remains unconnected. */
+ clock_set_hz(cpu->clock, CPU_FREQ_HZ_DEFAULT);
+ }
+ mips_cp0_period_set(cpu);
+
cpu_exec_realizefn(cs, &local_err);
if (local_err != NULL) {
error_propagate(errp, local_err);
@@ -162,6 +194,7 @@ static void mips_cpu_initfn(Object *obj)
MIPSCPUClass *mcc = MIPS_CPU_GET_CLASS(obj);
cpu_set_cpustate_pointers(cpu);
+ cpu->clock = qdev_init_clock_in(DEVICE(obj), "clk-in", NULL, cpu);
env->cpu_model = mcc->cpu_def;
}
@@ -181,6 +214,13 @@ static ObjectClass *mips_cpu_class_by_name(const char *cpu_model)
return oc;
}
+static Property mips_cpu_properties[] = {
+ /* CP0 timer running at half the clock of the CPU */
+ DEFINE_PROP_UINT32("cp0-count-rate", MIPSCPU, cp0_count_rate,
+ CP0_COUNT_RATE_DEFAULT),
+ DEFINE_PROP_END_OF_LIST()
+};
+
static void mips_cpu_class_init(ObjectClass *c, void *data)
{
MIPSCPUClass *mcc = MIPS_CPU_CLASS(c);
@@ -190,6 +230,7 @@ static void mips_cpu_class_init(ObjectClass *c, void *data)
device_class_set_parent_realize(dc, mips_cpu_realizefn,
&mcc->parent_realize);
device_class_set_parent_reset(dc, mips_cpu_reset, &mcc->parent_reset);
+ device_class_set_props(dc, mips_cpu_properties);
cc->class_by_name = mips_cpu_class_by_name;
cc->has_work = mips_cpu_has_work;
@@ -257,3 +298,15 @@ static void mips_cpu_register_types(void)
}
type_init(mips_cpu_register_types)
+
+/* Could be used by generic CPU object */
+MIPSCPU *mips_cpu_create_with_clock(const char *cpu_type, Clock *cpu_refclk)
+{
+ DeviceState *cpu;
+
+ cpu = DEVICE(object_new(cpu_type));
+ qdev_connect_clock_in(cpu, "clk-in", cpu_refclk);
+ qdev_realize(cpu, NULL, &error_abort);
+
+ return MIPS_CPU(cpu);
+}
diff --git a/target/mips/cpu.h b/target/mips/cpu.h
index 7cf7f5239f..d41579d44a 100644
--- a/target/mips/cpu.h
+++ b/target/mips/cpu.h
@@ -4,6 +4,7 @@
#include "cpu-qom.h"
#include "exec/cpu-defs.h"
#include "fpu/softfloat-types.h"
+#include "hw/clock.h"
#include "mips-defs.h"
#define TCG_GUEST_DEFAULT_MO (0)
@@ -1145,11 +1146,15 @@ struct CPUMIPSState {
struct MIPSITUState *itu;
MemoryRegion *itc_tag; /* ITC Configuration Tags */
target_ulong exception_base; /* ExceptionBase input to the core */
+ uint64_t cp0_count_ns; /* CP0_Count clock period (in nanoseconds) */
};
/**
* MIPSCPU:
* @env: #CPUMIPSState
+ * @clock: this CPU input clock (may be connected
+ * to an output clock from another device).
+ * @cp0_count_rate: rate at which the coprocessor 0 counter increments
*
* A MIPS CPU.
*/
@@ -1158,8 +1163,17 @@ struct MIPSCPU {
CPUState parent_obj;
/*< public >*/
+ Clock *clock;
CPUNegativeOffsetState neg;
CPUMIPSState env;
+ /*
+ * The Count register acts as a timer, incrementing at a constant rate,
+ * whether or not an instruction is executed, retired, or any forward
+ * progress is made through the pipeline. The rate at which the counter
+ * increments is implementation dependent, and is a function of the
+ * pipeline clock of the processor, not the issue width of the processor.
+ */
+ unsigned cp0_count_rate;
};
@@ -1293,4 +1307,16 @@ static inline void cpu_get_tb_cpu_state(CPUMIPSState *env, target_ulong *pc,
MIPS_HFLAG_HWRENA_ULR);
}
+/**
+ * mips_cpu_create_with_clock:
+ * @typename: a MIPS CPU type.
+ * @cpu_refclk: this cpu input clock (an output clock of another device)
+ *
+ * Instantiates a MIPS CPU, set the input clock of the CPU to @cpu_refclk,
+ * then realizes the CPU.
+ *
+ * Returns: A #CPUState or %NULL if an error occurred.
+ */
+MIPSCPU *mips_cpu_create_with_clock(const char *cpu_type, Clock *cpu_refclk);
+
#endif /* MIPS_CPU_H */
diff --git a/target/mips/fpu_helper.c b/target/mips/fpu_helper.c
index 56beda49d8..6cc956c023 100644
--- a/target/mips/fpu_helper.c
+++ b/target/mips/fpu_helper.c
@@ -983,27 +983,46 @@ uint32_t helper_float_floor_2008_w_s(CPUMIPSState *env, uint32_t fst0)
}
/* unary operations, not modifying fp status */
-#define FLOAT_UNOP(name) \
-uint64_t helper_float_ ## name ## _d(uint64_t fdt0) \
-{ \
- return float64_ ## name(fdt0); \
-} \
-uint32_t helper_float_ ## name ## _s(uint32_t fst0) \
-{ \
- return float32_ ## name(fst0); \
-} \
-uint64_t helper_float_ ## name ## _ps(uint64_t fdt0) \
-{ \
- uint32_t wt0; \
- uint32_t wth0; \
- \
- wt0 = float32_ ## name(fdt0 & 0XFFFFFFFF); \
- wth0 = float32_ ## name(fdt0 >> 32); \
- return ((uint64_t)wth0 << 32) | wt0; \
+
+uint64_t helper_float_abs_d(uint64_t fdt0)
+{
+ return float64_abs(fdt0);
+}
+
+uint32_t helper_float_abs_s(uint32_t fst0)
+{
+ return float32_abs(fst0);
+}
+
+uint64_t helper_float_abs_ps(uint64_t fdt0)
+{
+ uint32_t wt0;
+ uint32_t wth0;
+
+ wt0 = float32_abs(fdt0 & 0XFFFFFFFF);
+ wth0 = float32_abs(fdt0 >> 32);
+ return ((uint64_t)wth0 << 32) | wt0;
+}
+
+uint64_t helper_float_chs_d(uint64_t fdt0)
+{
+ return float64_chs(fdt0);
+}
+
+uint32_t helper_float_chs_s(uint32_t fst0)
+{
+ return float32_chs(fst0);
+}
+
+uint64_t helper_float_chs_ps(uint64_t fdt0)
+{
+ uint32_t wt0;
+ uint32_t wth0;
+
+ wt0 = float32_chs(fdt0 & 0XFFFFFFFF);
+ wth0 = float32_chs(fdt0 >> 32);
+ return ((uint64_t)wth0 << 32) | wt0;
}
-FLOAT_UNOP(abs)
-FLOAT_UNOP(chs)
-#undef FLOAT_UNOP
/* MIPS specific unary operations */
uint64_t helper_float_recip_d(CPUMIPSState *env, uint64_t fdt0)
@@ -1456,29 +1475,87 @@ uint64_t helper_float_mulr_ps(CPUMIPSState *env, uint64_t fdt0, uint64_t fdt1)
return ((uint64_t)fsth2 << 32) | fstl2;
}
-#define FLOAT_MINMAX(name, bits, minmaxfunc) \
-uint ## bits ## _t helper_float_ ## name(CPUMIPSState *env, \
- uint ## bits ## _t fs, \
- uint ## bits ## _t ft) \
-{ \
- uint ## bits ## _t fdret; \
- \
- fdret = float ## bits ## _ ## minmaxfunc(fs, ft, \
- &env->active_fpu.fp_status); \
- update_fcr31(env, GETPC()); \
- return fdret; \
+
+uint32_t helper_float_max_s(CPUMIPSState *env, uint32_t fs, uint32_t ft)
+{
+ uint32_t fdret;
+
+ fdret = float32_maxnum(fs, ft, &env->active_fpu.fp_status);
+
+ update_fcr31(env, GETPC());
+ return fdret;
+}
+
+uint64_t helper_float_max_d(CPUMIPSState *env, uint64_t fs, uint64_t ft)
+{
+ uint64_t fdret;
+
+ fdret = float64_maxnum(fs, ft, &env->active_fpu.fp_status);
+
+ update_fcr31(env, GETPC());
+ return fdret;
+}
+
+uint32_t helper_float_maxa_s(CPUMIPSState *env, uint32_t fs, uint32_t ft)
+{
+ uint32_t fdret;
+
+ fdret = float32_maxnummag(fs, ft, &env->active_fpu.fp_status);
+
+ update_fcr31(env, GETPC());
+ return fdret;
+}
+
+uint64_t helper_float_maxa_d(CPUMIPSState *env, uint64_t fs, uint64_t ft)
+{
+ uint64_t fdret;
+
+ fdret = float64_maxnummag(fs, ft, &env->active_fpu.fp_status);
+
+ update_fcr31(env, GETPC());
+ return fdret;
+}
+
+uint32_t helper_float_min_s(CPUMIPSState *env, uint32_t fs, uint32_t ft)
+{
+ uint32_t fdret;
+
+ fdret = float32_minnum(fs, ft, &env->active_fpu.fp_status);
+
+ update_fcr31(env, GETPC());
+ return fdret;
+}
+
+uint64_t helper_float_min_d(CPUMIPSState *env, uint64_t fs, uint64_t ft)
+{
+ uint64_t fdret;
+
+ fdret = float64_minnum(fs, ft, &env->active_fpu.fp_status);
+
+ update_fcr31(env, GETPC());
+ return fdret;
}
-FLOAT_MINMAX(max_s, 32, maxnum)
-FLOAT_MINMAX(max_d, 64, maxnum)
-FLOAT_MINMAX(maxa_s, 32, maxnummag)
-FLOAT_MINMAX(maxa_d, 64, maxnummag)
+uint32_t helper_float_mina_s(CPUMIPSState *env, uint32_t fs, uint32_t ft)
+{
+ uint32_t fdret;
+
+ fdret = float32_minnummag(fs, ft, &env->active_fpu.fp_status);
+
+ update_fcr31(env, GETPC());
+ return fdret;
+}
+
+uint64_t helper_float_mina_d(CPUMIPSState *env, uint64_t fs, uint64_t ft)
+{
+ uint64_t fdret;
+
+ fdret = float64_minnummag(fs, ft, &env->active_fpu.fp_status);
+
+ update_fcr31(env, GETPC());
+ return fdret;
+}
-FLOAT_MINMAX(min_s, 32, minnum)
-FLOAT_MINMAX(min_d, 64, minnum)
-FLOAT_MINMAX(mina_s, 32, minnummag)
-FLOAT_MINMAX(mina_d, 64, minnummag)
-#undef FLOAT_MINMAX
/* ternary operations */
@@ -1647,25 +1724,54 @@ uint64_t helper_float_nmsub_ps(CPUMIPSState *env, uint64_t fdt0,
}
-#define FLOAT_FMADDSUB(name, bits, muladd_arg) \
-uint ## bits ## _t helper_float_ ## name(CPUMIPSState *env, \
- uint ## bits ## _t fs, \
- uint ## bits ## _t ft, \
- uint ## bits ## _t fd) \
-{ \
- uint ## bits ## _t fdret; \
- \
- fdret = float ## bits ## _muladd(fs, ft, fd, muladd_arg, \
- &env->active_fpu.fp_status); \
- update_fcr31(env, GETPC()); \
- return fdret; \
+uint32_t helper_float_maddf_s(CPUMIPSState *env, uint32_t fs,
+ uint32_t ft, uint32_t fd)
+{
+ uint32_t fdret;
+
+ fdret = float32_muladd(fs, ft, fd, 0,
+ &env->active_fpu.fp_status);
+
+ update_fcr31(env, GETPC());
+ return fdret;
+}
+
+uint64_t helper_float_maddf_d(CPUMIPSState *env, uint64_t fs,
+ uint64_t ft, uint64_t fd)
+{
+ uint64_t fdret;
+
+ fdret = float64_muladd(fs, ft, fd, 0,
+ &env->active_fpu.fp_status);
+
+ update_fcr31(env, GETPC());
+ return fdret;
+}
+
+uint32_t helper_float_msubf_s(CPUMIPSState *env, uint32_t fs,
+ uint32_t ft, uint32_t fd)
+{
+ uint32_t fdret;
+
+ fdret = float32_muladd(fs, ft, fd, float_muladd_negate_product,
+ &env->active_fpu.fp_status);
+
+ update_fcr31(env, GETPC());
+ return fdret;
+}
+
+uint64_t helper_float_msubf_d(CPUMIPSState *env, uint64_t fs,
+ uint64_t ft, uint64_t fd)
+{
+ uint64_t fdret;
+
+ fdret = float64_muladd(fs, ft, fd, float_muladd_negate_product,
+ &env->active_fpu.fp_status);
+
+ update_fcr31(env, GETPC());
+ return fdret;
}
-FLOAT_FMADDSUB(maddf_s, 32, 0)
-FLOAT_FMADDSUB(maddf_d, 64, 0)
-FLOAT_FMADDSUB(msubf_s, 32, float_muladd_negate_product)
-FLOAT_FMADDSUB(msubf_d, 64, float_muladd_negate_product)
-#undef FLOAT_FMADDSUB
/* compare operations */
#define FOP_COND_D(op, cond) \
diff --git a/target/mips/internal.h b/target/mips/internal.h
index 7f159a9230..dd8a7809b6 100644
--- a/target/mips/internal.h
+++ b/target/mips/internal.h
@@ -144,6 +144,7 @@ void r4k_helper_tlbr(CPUMIPSState *env);
void r4k_helper_tlbinv(CPUMIPSState *env);
void r4k_helper_tlbinvf(CPUMIPSState *env);
void r4k_invalidate_tlb(CPUMIPSState *env, int idx, int use_extra);
+uint32_t cpu_mips_get_random(CPUMIPSState *env);
void mips_cpu_do_transaction_failed(CPUState *cs, hwaddr physaddr,
vaddr addr, unsigned size,
@@ -188,7 +189,7 @@ static inline bool cpu_mips_hw_interrupts_pending(CPUMIPSState *env)
/*
* A MIPS configured with a vectorizing external interrupt controller
* will feed a vector into the Cause pending lines. The core treats
- * the status lines as a vector level, not as indiviual masks.
+ * the status lines as a vector level, not as individual masks.
*/
r = pending > status;
} else {
@@ -209,7 +210,6 @@ void cpu_state_reset(CPUMIPSState *s);
void cpu_mips_realize_env(CPUMIPSState *env);
/* cp0_timer.c */
-uint32_t cpu_mips_get_random(CPUMIPSState *env);
uint32_t cpu_mips_get_count(CPUMIPSState *env);
void cpu_mips_store_count(CPUMIPSState *env, uint32_t value);
void cpu_mips_store_compare(CPUMIPSState *env, uint32_t value);
diff --git a/target/mips/op_helper.c b/target/mips/op_helper.c
index 9552b280e0..0050d0616b 100644
--- a/target/mips/op_helper.c
+++ b/target/mips/op_helper.c
@@ -1574,15 +1574,34 @@ void helper_msa_st_d(CPUMIPSState *env, uint32_t wd,
void helper_cache(CPUMIPSState *env, target_ulong addr, uint32_t op)
{
#ifndef CONFIG_USER_ONLY
+ static const char *const type_name[] = {
+ "Primary Instruction",
+ "Primary Data or Unified Primary",
+ "Tertiary",
+ "Secondary"
+ };
+ uint32_t cache_type = extract32(op, 0, 2);
+ uint32_t cache_operation = extract32(op, 2, 3);
target_ulong index = addr & 0x1fffffff;
- if (op == 9) {
- /* Index Store Tag */
+
+ switch (cache_operation) {
+ case 0b010: /* Index Store Tag */
memory_region_dispatch_write(env->itc_tag, index, env->CP0_TagLo,
MO_64, MEMTXATTRS_UNSPECIFIED);
- } else if (op == 5) {
- /* Index Load Tag */
+ break;
+ case 0b001: /* Index Load Tag */
memory_region_dispatch_read(env->itc_tag, index, &env->CP0_TagLo,
MO_64, MEMTXATTRS_UNSPECIFIED);
+ break;
+ case 0b000: /* Index Invalidate */
+ case 0b100: /* Hit Invalidate */
+ case 0b110: /* Hit Writeback */
+ /* no-op */
+ break;
+ default:
+ qemu_log_mask(LOG_UNIMP, "cache operation:%u (type: %s cache)\n",
+ cache_operation, type_name[cache_type]);
+ break;
}
#endif
}
diff --git a/target/mips/translate.c b/target/mips/translate.c
index 398edf7289..f449758606 100644
--- a/target/mips/translate.c
+++ b/target/mips/translate.c
@@ -460,6 +460,48 @@ enum {
R6_OPC_SCD = 0x27 | OPC_SPECIAL3,
};
+/* Loongson EXT load/store quad word opcodes */
+#define MASK_LOONGSON_GSLSQ(op) (MASK_OP_MAJOR(op) | (op & 0x8020))
+enum {
+ OPC_GSLQ = 0x0020 | OPC_LWC2,
+ OPC_GSLQC1 = 0x8020 | OPC_LWC2,
+ OPC_GSSHFL = OPC_LWC2,
+ OPC_GSSQ = 0x0020 | OPC_SWC2,
+ OPC_GSSQC1 = 0x8020 | OPC_SWC2,
+ OPC_GSSHFS = OPC_SWC2,
+};
+
+/* Loongson EXT shifted load/store opcodes */
+#define MASK_LOONGSON_GSSHFLS(op) (MASK_OP_MAJOR(op) | (op & 0xc03f))
+enum {
+ OPC_GSLWLC1 = 0x4 | OPC_GSSHFL,
+ OPC_GSLWRC1 = 0x5 | OPC_GSSHFL,
+ OPC_GSLDLC1 = 0x6 | OPC_GSSHFL,
+ OPC_GSLDRC1 = 0x7 | OPC_GSSHFL,
+ OPC_GSSWLC1 = 0x4 | OPC_GSSHFS,
+ OPC_GSSWRC1 = 0x5 | OPC_GSSHFS,
+ OPC_GSSDLC1 = 0x6 | OPC_GSSHFS,
+ OPC_GSSDRC1 = 0x7 | OPC_GSSHFS,
+};
+
+/* Loongson EXT LDC2/SDC2 opcodes */
+#define MASK_LOONGSON_LSDC2(op) (MASK_OP_MAJOR(op) | (op & 0x7))
+
+enum {
+ OPC_GSLBX = 0x0 | OPC_LDC2,
+ OPC_GSLHX = 0x1 | OPC_LDC2,
+ OPC_GSLWX = 0x2 | OPC_LDC2,
+ OPC_GSLDX = 0x3 | OPC_LDC2,
+ OPC_GSLWXC1 = 0x6 | OPC_LDC2,
+ OPC_GSLDXC1 = 0x7 | OPC_LDC2,
+ OPC_GSSBX = 0x0 | OPC_SDC2,
+ OPC_GSSHX = 0x1 | OPC_SDC2,
+ OPC_GSSWX = 0x2 | OPC_SDC2,
+ OPC_GSSDX = 0x3 | OPC_SDC2,
+ OPC_GSSWXC1 = 0x6 | OPC_SDC2,
+ OPC_GSSDXC1 = 0x7 | OPC_SDC2,
+};
+
/* BSHFL opcodes */
#define MASK_BSHFL(op) (MASK_SPECIAL3(op) | (op & (0x1F << 6)))
@@ -3718,7 +3760,7 @@ static void gen_st_cond(DisasContext *ctx, int rt, int base, int offset,
t0 = tcg_temp_new();
addr = tcg_temp_new();
- /* compare the address against that of the preceeding LL */
+ /* compare the address against that of the preceding LL */
gen_base_offset_addr(ctx, addr, base, offset);
tcg_gen_brcond_tl(TCG_COND_EQ, addr, cpu_lladdr, l1);
tcg_temp_free(addr);
@@ -5910,6 +5952,403 @@ no_rd:
tcg_temp_free_i64(t1);
}
+static void gen_loongson_lswc2(DisasContext *ctx, int rt,
+ int rs, int rd)
+{
+ TCGv t0, t1, t2;
+ TCGv_i32 fp0;
+#if defined(TARGET_MIPS64)
+ int lsq_rt1 = ctx->opcode & 0x1f;
+ int lsq_offset = sextract32(ctx->opcode, 6, 9) << 4;
+#endif
+ int shf_offset = sextract32(ctx->opcode, 6, 8);
+
+ t0 = tcg_temp_new();
+
+ switch (MASK_LOONGSON_GSLSQ(ctx->opcode)) {
+#if defined(TARGET_MIPS64)
+ case OPC_GSLQ:
+ t1 = tcg_temp_new();
+ gen_base_offset_addr(ctx, t0, rs, lsq_offset);
+ tcg_gen_qemu_ld_tl(t1, t0, ctx->mem_idx, MO_TEQ |
+ ctx->default_tcg_memop_mask);
+ gen_base_offset_addr(ctx, t0, rs, lsq_offset + 8);
+ tcg_gen_qemu_ld_tl(t0, t0, ctx->mem_idx, MO_TEQ |
+ ctx->default_tcg_memop_mask);
+ gen_store_gpr(t1, rt);
+ gen_store_gpr(t0, lsq_rt1);
+ tcg_temp_free(t1);
+ break;
+ case OPC_GSLQC1:
+ check_cp1_enabled(ctx);
+ t1 = tcg_temp_new();
+ gen_base_offset_addr(ctx, t0, rs, lsq_offset);
+ tcg_gen_qemu_ld_tl(t1, t0, ctx->mem_idx, MO_TEQ |
+ ctx->default_tcg_memop_mask);
+ gen_base_offset_addr(ctx, t0, rs, lsq_offset + 8);
+ tcg_gen_qemu_ld_tl(t0, t0, ctx->mem_idx, MO_TEQ |
+ ctx->default_tcg_memop_mask);
+ gen_store_fpr64(ctx, t1, rt);
+ gen_store_fpr64(ctx, t0, lsq_rt1);
+ tcg_temp_free(t1);
+ break;
+ case OPC_GSSQ:
+ t1 = tcg_temp_new();
+ gen_base_offset_addr(ctx, t0, rs, lsq_offset);
+ gen_load_gpr(t1, rt);
+ tcg_gen_qemu_st_tl(t1, t0, ctx->mem_idx, MO_TEQ |
+ ctx->default_tcg_memop_mask);
+ gen_base_offset_addr(ctx, t0, rs, lsq_offset + 8);
+ gen_load_gpr(t1, lsq_rt1);
+ tcg_gen_qemu_st_tl(t1, t0, ctx->mem_idx, MO_TEQ |
+ ctx->default_tcg_memop_mask);
+ tcg_temp_free(t1);
+ break;
+ case OPC_GSSQC1:
+ check_cp1_enabled(ctx);
+ t1 = tcg_temp_new();
+ gen_base_offset_addr(ctx, t0, rs, lsq_offset);
+ gen_load_fpr64(ctx, t1, rt);
+ tcg_gen_qemu_st_tl(t1, t0, ctx->mem_idx, MO_TEQ |
+ ctx->default_tcg_memop_mask);
+ gen_base_offset_addr(ctx, t0, rs, lsq_offset + 8);
+ gen_load_fpr64(ctx, t1, lsq_rt1);
+ tcg_gen_qemu_st_tl(t1, t0, ctx->mem_idx, MO_TEQ |
+ ctx->default_tcg_memop_mask);
+ tcg_temp_free(t1);
+ break;
+#endif
+ case OPC_GSSHFL:
+ switch (MASK_LOONGSON_GSSHFLS(ctx->opcode)) {
+ case OPC_GSLWLC1:
+ check_cp1_enabled(ctx);
+ gen_base_offset_addr(ctx, t0, rs, shf_offset);
+ t1 = tcg_temp_new();
+ tcg_gen_qemu_ld_tl(t1, t0, ctx->mem_idx, MO_UB);
+ tcg_gen_andi_tl(t1, t0, 3);
+#ifndef TARGET_WORDS_BIGENDIAN
+ tcg_gen_xori_tl(t1, t1, 3);
+#endif
+ tcg_gen_shli_tl(t1, t1, 3);
+ tcg_gen_andi_tl(t0, t0, ~3);
+ tcg_gen_qemu_ld_tl(t0, t0, ctx->mem_idx, MO_TEUL);
+ tcg_gen_shl_tl(t0, t0, t1);
+ t2 = tcg_const_tl(-1);
+ tcg_gen_shl_tl(t2, t2, t1);
+ fp0 = tcg_temp_new_i32();
+ gen_load_fpr32(ctx, fp0, rt);
+ tcg_gen_ext_i32_tl(t1, fp0);
+ tcg_gen_andc_tl(t1, t1, t2);
+ tcg_temp_free(t2);
+ tcg_gen_or_tl(t0, t0, t1);
+ tcg_temp_free(t1);
+#if defined(TARGET_MIPS64)
+ tcg_gen_extrl_i64_i32(fp0, t0);
+#else
+ tcg_gen_ext32s_tl(fp0, t0);
+#endif
+ gen_store_fpr32(ctx, fp0, rt);
+ tcg_temp_free_i32(fp0);
+ break;
+ case OPC_GSLWRC1:
+ check_cp1_enabled(ctx);
+ gen_base_offset_addr(ctx, t0, rs, shf_offset);
+ t1 = tcg_temp_new();
+ tcg_gen_qemu_ld_tl(t1, t0, ctx->mem_idx, MO_UB);
+ tcg_gen_andi_tl(t1, t0, 3);
+#ifdef TARGET_WORDS_BIGENDIAN
+ tcg_gen_xori_tl(t1, t1, 3);
+#endif
+ tcg_gen_shli_tl(t1, t1, 3);
+ tcg_gen_andi_tl(t0, t0, ~3);
+ tcg_gen_qemu_ld_tl(t0, t0, ctx->mem_idx, MO_TEUL);
+ tcg_gen_shr_tl(t0, t0, t1);
+ tcg_gen_xori_tl(t1, t1, 31);
+ t2 = tcg_const_tl(0xfffffffeull);
+ tcg_gen_shl_tl(t2, t2, t1);
+ fp0 = tcg_temp_new_i32();
+ gen_load_fpr32(ctx, fp0, rt);
+ tcg_gen_ext_i32_tl(t1, fp0);
+ tcg_gen_and_tl(t1, t1, t2);
+ tcg_temp_free(t2);
+ tcg_gen_or_tl(t0, t0, t1);
+ tcg_temp_free(t1);
+#if defined(TARGET_MIPS64)
+ tcg_gen_extrl_i64_i32(fp0, t0);
+#else
+ tcg_gen_ext32s_tl(fp0, t0);
+#endif
+ gen_store_fpr32(ctx, fp0, rt);
+ tcg_temp_free_i32(fp0);
+ break;
+#if defined(TARGET_MIPS64)
+ case OPC_GSLDLC1:
+ check_cp1_enabled(ctx);
+ gen_base_offset_addr(ctx, t0, rs, shf_offset);
+ t1 = tcg_temp_new();
+ tcg_gen_qemu_ld_tl(t1, t0, ctx->mem_idx, MO_UB);
+ tcg_gen_andi_tl(t1, t0, 7);
+#ifndef TARGET_WORDS_BIGENDIAN
+ tcg_gen_xori_tl(t1, t1, 7);
+#endif
+ tcg_gen_shli_tl(t1, t1, 3);
+ tcg_gen_andi_tl(t0, t0, ~7);
+ tcg_gen_qemu_ld_tl(t0, t0, ctx->mem_idx, MO_TEQ);
+ tcg_gen_shl_tl(t0, t0, t1);
+ t2 = tcg_const_tl(-1);
+ tcg_gen_shl_tl(t2, t2, t1);
+ gen_load_fpr64(ctx, t1, rt);
+ tcg_gen_andc_tl(t1, t1, t2);
+ tcg_temp_free(t2);
+ tcg_gen_or_tl(t0, t0, t1);
+ tcg_temp_free(t1);
+ gen_store_fpr64(ctx, t0, rt);
+ break;
+ case OPC_GSLDRC1:
+ check_cp1_enabled(ctx);
+ gen_base_offset_addr(ctx, t0, rs, shf_offset);
+ t1 = tcg_temp_new();
+ tcg_gen_qemu_ld_tl(t1, t0, ctx->mem_idx, MO_UB);
+ tcg_gen_andi_tl(t1, t0, 7);
+#ifdef TARGET_WORDS_BIGENDIAN
+ tcg_gen_xori_tl(t1, t1, 7);
+#endif
+ tcg_gen_shli_tl(t1, t1, 3);
+ tcg_gen_andi_tl(t0, t0, ~7);
+ tcg_gen_qemu_ld_tl(t0, t0, ctx->mem_idx, MO_TEQ);
+ tcg_gen_shr_tl(t0, t0, t1);
+ tcg_gen_xori_tl(t1, t1, 63);
+ t2 = tcg_const_tl(0xfffffffffffffffeull);
+ tcg_gen_shl_tl(t2, t2, t1);
+ gen_load_fpr64(ctx, t1, rt);
+ tcg_gen_and_tl(t1, t1, t2);
+ tcg_temp_free(t2);
+ tcg_gen_or_tl(t0, t0, t1);
+ tcg_temp_free(t1);
+ gen_store_fpr64(ctx, t0, rt);
+ break;
+#endif
+ default:
+ MIPS_INVAL("loongson_gsshfl");
+ generate_exception_end(ctx, EXCP_RI);
+ break;
+ }
+ break;
+ case OPC_GSSHFS:
+ switch (MASK_LOONGSON_GSSHFLS(ctx->opcode)) {
+ case OPC_GSSWLC1:
+ check_cp1_enabled(ctx);
+ t1 = tcg_temp_new();
+ gen_base_offset_addr(ctx, t0, rs, shf_offset);
+ fp0 = tcg_temp_new_i32();
+ gen_load_fpr32(ctx, fp0, rt);
+ tcg_gen_ext_i32_tl(t1, fp0);
+ gen_helper_0e2i(swl, t1, t0, ctx->mem_idx);
+ tcg_temp_free_i32(fp0);
+ tcg_temp_free(t1);
+ break;
+ case OPC_GSSWRC1:
+ check_cp1_enabled(ctx);
+ t1 = tcg_temp_new();
+ gen_base_offset_addr(ctx, t0, rs, shf_offset);
+ fp0 = tcg_temp_new_i32();
+ gen_load_fpr32(ctx, fp0, rt);
+ tcg_gen_ext_i32_tl(t1, fp0);
+ gen_helper_0e2i(swr, t1, t0, ctx->mem_idx);
+ tcg_temp_free_i32(fp0);
+ tcg_temp_free(t1);
+ break;
+#if defined(TARGET_MIPS64)
+ case OPC_GSSDLC1:
+ check_cp1_enabled(ctx);
+ t1 = tcg_temp_new();
+ gen_base_offset_addr(ctx, t0, rs, shf_offset);
+ gen_load_fpr64(ctx, t1, rt);
+ gen_helper_0e2i(sdl, t1, t0, ctx->mem_idx);
+ tcg_temp_free(t1);
+ break;
+ case OPC_GSSDRC1:
+ check_cp1_enabled(ctx);
+ t1 = tcg_temp_new();
+ gen_base_offset_addr(ctx, t0, rs, shf_offset);
+ gen_load_fpr64(ctx, t1, rt);
+ gen_helper_0e2i(sdr, t1, t0, ctx->mem_idx);
+ tcg_temp_free(t1);
+ break;
+#endif
+ default:
+ MIPS_INVAL("loongson_gsshfs");
+ generate_exception_end(ctx, EXCP_RI);
+ break;
+ }
+ break;
+ default:
+ MIPS_INVAL("loongson_gslsq");
+ generate_exception_end(ctx, EXCP_RI);
+ break;
+ }
+ tcg_temp_free(t0);
+}
+
+/* Loongson EXT LDC2/SDC2 */
+static void gen_loongson_lsdc2(DisasContext *ctx, int rt,
+ int rs, int rd)
+{
+ int offset = sextract32(ctx->opcode, 3, 8);
+ uint32_t opc = MASK_LOONGSON_LSDC2(ctx->opcode);
+ TCGv t0, t1;
+ TCGv_i32 fp0;
+
+ /* Pre-conditions */
+ switch (opc) {
+ case OPC_GSLBX:
+ case OPC_GSLHX:
+ case OPC_GSLWX:
+ case OPC_GSLDX:
+ /* prefetch, implement as NOP */
+ if (rt == 0) {
+ return;
+ }
+ break;
+ case OPC_GSSBX:
+ case OPC_GSSHX:
+ case OPC_GSSWX:
+ case OPC_GSSDX:
+ break;
+ case OPC_GSLWXC1:
+#if defined(TARGET_MIPS64)
+ case OPC_GSLDXC1:
+#endif
+ check_cp1_enabled(ctx);
+ /* prefetch, implement as NOP */
+ if (rt == 0) {
+ return;
+ }
+ break;
+ case OPC_GSSWXC1:
+#if defined(TARGET_MIPS64)
+ case OPC_GSSDXC1:
+#endif
+ check_cp1_enabled(ctx);
+ break;
+ default:
+ MIPS_INVAL("loongson_lsdc2");
+ generate_exception_end(ctx, EXCP_RI);
+ return;
+ break;
+ }
+
+ t0 = tcg_temp_new();
+
+ gen_base_offset_addr(ctx, t0, rs, offset);
+ gen_op_addr_add(ctx, t0, cpu_gpr[rd], t0);
+
+ switch (opc) {
+ case OPC_GSLBX:
+ tcg_gen_qemu_ld_tl(t0, t0, ctx->mem_idx, MO_SB);
+ gen_store_gpr(t0, rt);
+ break;
+ case OPC_GSLHX:
+ tcg_gen_qemu_ld_tl(t0, t0, ctx->mem_idx, MO_TESW |
+ ctx->default_tcg_memop_mask);
+ gen_store_gpr(t0, rt);
+ break;
+ case OPC_GSLWX:
+ gen_base_offset_addr(ctx, t0, rs, offset);
+ if (rd) {
+ gen_op_addr_add(ctx, t0, cpu_gpr[rd], t0);
+ }
+ tcg_gen_qemu_ld_tl(t0, t0, ctx->mem_idx, MO_TESL |
+ ctx->default_tcg_memop_mask);
+ gen_store_gpr(t0, rt);
+ break;
+#if defined(TARGET_MIPS64)
+ case OPC_GSLDX:
+ gen_base_offset_addr(ctx, t0, rs, offset);
+ if (rd) {
+ gen_op_addr_add(ctx, t0, cpu_gpr[rd], t0);
+ }
+ tcg_gen_qemu_ld_tl(t0, t0, ctx->mem_idx, MO_TEQ |
+ ctx->default_tcg_memop_mask);
+ gen_store_gpr(t0, rt);
+ break;
+#endif
+ case OPC_GSLWXC1:
+ check_cp1_enabled(ctx);
+ gen_base_offset_addr(ctx, t0, rs, offset);
+ if (rd) {
+ gen_op_addr_add(ctx, t0, cpu_gpr[rd], t0);
+ }
+ fp0 = tcg_temp_new_i32();
+ tcg_gen_qemu_ld_i32(fp0, t0, ctx->mem_idx, MO_TESL |
+ ctx->default_tcg_memop_mask);
+ gen_store_fpr32(ctx, fp0, rt);
+ tcg_temp_free_i32(fp0);
+ break;
+#if defined(TARGET_MIPS64)
+ case OPC_GSLDXC1:
+ check_cp1_enabled(ctx);
+ gen_base_offset_addr(ctx, t0, rs, offset);
+ if (rd) {
+ gen_op_addr_add(ctx, t0, cpu_gpr[rd], t0);
+ }
+ tcg_gen_qemu_ld_tl(t0, t0, ctx->mem_idx, MO_TEQ |
+ ctx->default_tcg_memop_mask);
+ gen_store_fpr64(ctx, t0, rt);
+ break;
+#endif
+ case OPC_GSSBX:
+ t1 = tcg_temp_new();
+ gen_load_gpr(t1, rt);
+ tcg_gen_qemu_st_tl(t1, t0, ctx->mem_idx, MO_SB);
+ tcg_temp_free(t1);
+ break;
+ case OPC_GSSHX:
+ t1 = tcg_temp_new();
+ gen_load_gpr(t1, rt);
+ tcg_gen_qemu_st_tl(t1, t0, ctx->mem_idx, MO_TEUW |
+ ctx->default_tcg_memop_mask);
+ tcg_temp_free(t1);
+ break;
+ case OPC_GSSWX:
+ t1 = tcg_temp_new();
+ gen_load_gpr(t1, rt);
+ tcg_gen_qemu_st_tl(t1, t0, ctx->mem_idx, MO_TEUL |
+ ctx->default_tcg_memop_mask);
+ tcg_temp_free(t1);
+ break;
+#if defined(TARGET_MIPS64)
+ case OPC_GSSDX:
+ t1 = tcg_temp_new();
+ gen_load_gpr(t1, rt);
+ tcg_gen_qemu_st_tl(t1, t0, ctx->mem_idx, MO_TEQ |
+ ctx->default_tcg_memop_mask);
+ tcg_temp_free(t1);
+ break;
+#endif
+ case OPC_GSSWXC1:
+ fp0 = tcg_temp_new_i32();
+ gen_load_fpr32(ctx, fp0, rt);
+ tcg_gen_qemu_st_i32(fp0, t0, ctx->mem_idx, MO_TEUL |
+ ctx->default_tcg_memop_mask);
+ tcg_temp_free_i32(fp0);
+ break;
+#if defined(TARGET_MIPS64)
+ case OPC_GSSDXC1:
+ t1 = tcg_temp_new();
+ gen_load_fpr64(ctx, t1, rt);
+ tcg_gen_qemu_st_i64(t1, t0, ctx->mem_idx, MO_TEQ |
+ ctx->default_tcg_memop_mask);
+ tcg_temp_free(t1);
+ break;
+#endif
+ default:
+ break;
+ }
+
+ tcg_temp_free(t0);
+}
+
/* Traps */
static void gen_trap(DisasContext *ctx, uint32_t opc,
int rs, int rt, int16_t imm)
@@ -25597,7 +26036,7 @@ static void gen_mxu_D16MAX_D16MIN(DisasContext *ctx)
}
/* return resulting half-words to its original position */
tcg_gen_shri_i32(t0, t0, 16);
- /* finaly update the destination */
+ /* finally update the destination */
tcg_gen_or_i32(mxu_gpr[XRa - 1], mxu_gpr[XRa - 1], t0);
tcg_temp_free(t1);
@@ -25633,7 +26072,7 @@ static void gen_mxu_D16MAX_D16MIN(DisasContext *ctx)
}
/* return resulting half-words to its original position */
tcg_gen_shri_i32(t0, t0, 16);
- /* finaly update the destination */
+ /* finally update the destination */
tcg_gen_or_i32(mxu_gpr[XRa - 1], mxu_gpr[XRa - 1], t0);
tcg_temp_free(t1);
@@ -25702,7 +26141,7 @@ static void gen_mxu_Q8MAX_Q8MIN(DisasContext *ctx)
}
/* return resulting byte to its original position */
tcg_gen_shri_i32(t0, t0, 8 * (3 - i));
- /* finaly update the destination */
+ /* finally update the destination */
tcg_gen_or_i32(mxu_gpr[XRa - 1], mxu_gpr[XRa - 1], t0);
}
@@ -25742,7 +26181,7 @@ static void gen_mxu_Q8MAX_Q8MIN(DisasContext *ctx)
}
/* return resulting byte to its original position */
tcg_gen_shri_i32(t0, t0, 8 * (3 - i));
- /* finaly update the destination */
+ /* finally update the destination */
tcg_gen_or_i32(mxu_gpr[XRa - 1], mxu_gpr[XRa - 1], t0);
}
@@ -30774,6 +31213,8 @@ static void decode_opc(CPUMIPSState *env, DisasContext *ctx)
/* OPC_BC, OPC_BALC */
gen_compute_compact_branch(ctx, op, 0, 0,
sextract32(ctx->opcode << 2, 0, 28));
+ } else if (ctx->insn_flags & ASE_LEXT) {
+ gen_loongson_lswc2(ctx, rt, rs, rd);
} else {
/* OPC_LWC2, OPC_SWC2 */
/* COP2: Not implemented. */
@@ -30791,6 +31232,8 @@ static void decode_opc(CPUMIPSState *env, DisasContext *ctx)
/* OPC_JIC, OPC_JIALC */
gen_compute_compact_branch(ctx, op, 0, rt, imm);
}
+ } else if (ctx->insn_flags & ASE_LEXT) {
+ gen_loongson_lsdc2(ctx, rt, rs, rd);
} else {
/* OPC_LWC2, OPC_SWC2 */
/* COP2: Not implemented. */
diff --git a/target/mips/translate_init.c.inc b/target/mips/translate_init.c.inc
index 637caccd89..fb5a9b38e5 100644
--- a/target/mips/translate_init.c.inc
+++ b/target/mips/translate_init.c.inc
@@ -254,7 +254,7 @@ const mips_def_t mips_defs[] =
.CP0_PRid = 0x00019500,
.CP0_Config0 = MIPS_CONFIG0 | (0x1 << CP0C0_AR) |
(MMU_TYPE_R4000 << CP0C0_MT),
- .CP0_Config1 = MIPS_CONFIG1 | (1 << CP0C1_FP) | (15 << CP0C1_MMU) |
+ .CP0_Config1 = MIPS_CONFIG1 | (1 << CP0C1_FP) | (63 << CP0C1_MMU) |
(0 << CP0C1_IS) | (3 << CP0C1_IL) | (1 << CP0C1_IA) |
(0 << CP0C1_DS) | (3 << CP0C1_DL) | (1 << CP0C1_DA) |
(1 << CP0C1_CA),
@@ -995,7 +995,7 @@ static void mvp_init (CPUMIPSState *env, const mips_def_t *def)
/* MVPConf1 implemented, TLB sharable, no gating storage support,
programmable cache partitioning implemented, number of allocatable
- and sharable TLB entries, MVP has allocatable TCs, 2 VPEs
+ and shareable TLB entries, MVP has allocatable TCs, 2 VPEs
implemented, 5 TCs implemented. */
env->mvp->CP0_MVPConf0 = (1U << CP0MVPC0_M) | (1 << CP0MVPC0_TLBS) |
(0 << CP0MVPC0_GS) | (1 << CP0MVPC0_PCP) |
diff --git a/tests/acceptance/replay_kernel.py b/tests/acceptance/replay_kernel.py
index 952f429cac..00c228382b 100644
--- a/tests/acceptance/replay_kernel.py
+++ b/tests/acceptance/replay_kernel.py
@@ -9,6 +9,8 @@
# later. See the COPYING file in the top-level directory.
import os
+import lzma
+import shutil
import logging
import time
@@ -19,7 +21,7 @@ from avocado.utils import archive
from avocado.utils import process
from boot_linux_console import LinuxKernelTest
-class ReplayKernel(LinuxKernelTest):
+class ReplayKernelBase(LinuxKernelTest):
"""
Boots a Linux kernel in record mode and checks that the console
is operational and the kernel command line is properly passed
@@ -74,6 +76,7 @@ class ReplayKernel(LinuxKernelTest):
logger = logging.getLogger('replay')
logger.info('replay overhead {:.2%}'.format(t2 / t1 - 1))
+class ReplayKernelNormal(ReplayKernelBase):
@skipIf(os.getenv('GITLAB_CI'), 'Running on GitLab')
def test_x86_64_pc(self):
"""
@@ -91,6 +94,51 @@ class ReplayKernel(LinuxKernelTest):
self.run_rr(kernel_path, kernel_command_line, console_pattern, shift=5)
+ def test_mips_malta(self):
+ """
+ :avocado: tags=arch:mips
+ :avocado: tags=machine:malta
+ :avocado: tags=endian:big
+ """
+ deb_url = ('http://snapshot.debian.org/archive/debian/'
+ '20130217T032700Z/pool/main/l/linux-2.6/'
+ 'linux-image-2.6.32-5-4kc-malta_2.6.32-48_mips.deb')
+ deb_hash = 'a8cfc28ad8f45f54811fc6cf74fc43ffcfe0ba04'
+ deb_path = self.fetch_asset(deb_url, asset_hash=deb_hash)
+ kernel_path = self.extract_from_deb(deb_path,
+ '/boot/vmlinux-2.6.32-5-4kc-malta')
+ kernel_command_line = self.KERNEL_COMMON_COMMAND_LINE + 'console=ttyS0'
+ console_pattern = 'Kernel command line: %s' % kernel_command_line
+
+ self.run_rr(kernel_path, kernel_command_line, console_pattern, shift=5)
+
+ def test_mips64el_malta(self):
+ """
+ This test requires the ar tool to extract "data.tar.gz" from
+ the Debian package.
+
+ The kernel can be rebuilt using this Debian kernel source [1] and
+ following the instructions on [2].
+
+ [1] http://snapshot.debian.org/package/linux-2.6/2.6.32-48/
+ #linux-source-2.6.32_2.6.32-48
+ [2] https://kernel-team.pages.debian.net/kernel-handbook/
+ ch-common-tasks.html#s-common-official
+
+ :avocado: tags=arch:mips64el
+ :avocado: tags=machine:malta
+ """
+ deb_url = ('http://snapshot.debian.org/archive/debian/'
+ '20130217T032700Z/pool/main/l/linux-2.6/'
+ 'linux-image-2.6.32-5-5kc-malta_2.6.32-48_mipsel.deb')
+ deb_hash = '1aaec92083bf22fda31e0d27fa8d9a388e5fc3d5'
+ deb_path = self.fetch_asset(deb_url, asset_hash=deb_hash)
+ kernel_path = self.extract_from_deb(deb_path,
+ '/boot/vmlinux-2.6.32-5-5kc-malta')
+ kernel_command_line = self.KERNEL_COMMON_COMMAND_LINE + 'console=ttyS0'
+ console_pattern = 'Kernel command line: %s' % kernel_command_line
+ self.run_rr(kernel_path, kernel_command_line, console_pattern, shift=5)
+
def test_aarch64_virt(self):
"""
:avocado: tags=arch:aarch64
@@ -302,3 +350,120 @@ class ReplayKernel(LinuxKernelTest):
file_path = self.fetch_asset(tar_url, asset_hash=tar_hash)
self.do_test_advcal_2018(file_path, 'santas-sleigh-ride.elf',
args=('-cpu', 'dc233c'))
+
+@skipUnless(os.getenv('AVOCADO_TIMEOUT_EXPECTED'), 'Test might timeout')
+class ReplayKernelSlow(ReplayKernelBase):
+ # Override the timeout, because this kernel includes an inner
+ # loop which is executed with TB recompilings during replay,
+ # making it very slow.
+ timeout = 180
+
+ def test_mips_malta_cpio(self):
+ """
+ :avocado: tags=arch:mips
+ :avocado: tags=machine:malta
+ :avocado: tags=endian:big
+ :avocado: tags=slowness:high
+ """
+ deb_url = ('http://snapshot.debian.org/archive/debian/'
+ '20160601T041800Z/pool/main/l/linux/'
+ 'linux-image-4.5.0-2-4kc-malta_4.5.5-1_mips.deb')
+ deb_hash = 'a3c84f3e88b54e06107d65a410d1d1e8e0f340f8'
+ deb_path = self.fetch_asset(deb_url, asset_hash=deb_hash)
+ kernel_path = self.extract_from_deb(deb_path,
+ '/boot/vmlinux-4.5.0-2-4kc-malta')
+ initrd_url = ('https://github.com/groeck/linux-build-test/raw/'
+ '8584a59ed9e5eb5ee7ca91f6d74bbb06619205b8/rootfs/'
+ 'mips/rootfs.cpio.gz')
+ initrd_hash = 'bf806e17009360a866bf537f6de66590de349a99'
+ initrd_path_gz = self.fetch_asset(initrd_url, asset_hash=initrd_hash)
+ initrd_path = self.workdir + "rootfs.cpio"
+ archive.gzip_uncompress(initrd_path_gz, initrd_path)
+
+ kernel_command_line = (self.KERNEL_COMMON_COMMAND_LINE +
+ 'console=ttyS0 console=tty '
+ 'rdinit=/sbin/init noreboot')
+ console_pattern = 'Boot successful.'
+ self.run_rr(kernel_path, kernel_command_line, console_pattern, shift=5,
+ args=('-initrd', initrd_path))
+
+ @skipUnless(os.getenv('AVOCADO_ALLOW_UNTRUSTED_CODE'), 'untrusted code')
+ def test_mips64el_malta_5KEc_cpio(self):
+ """
+ :avocado: tags=arch:mips64el
+ :avocado: tags=machine:malta
+ :avocado: tags=endian:little
+ :avocado: tags=slowness:high
+ """
+ kernel_url = ('https://github.com/philmd/qemu-testing-blob/'
+ 'raw/9ad2df38/mips/malta/mips64el/'
+ 'vmlinux-3.19.3.mtoman.20150408')
+ kernel_hash = '00d1d268fb9f7d8beda1de6bebcc46e884d71754'
+ kernel_path = self.fetch_asset(kernel_url, asset_hash=kernel_hash)
+ initrd_url = ('https://github.com/groeck/linux-build-test/'
+ 'raw/8584a59e/rootfs/'
+ 'mipsel64/rootfs.mipsel64r1.cpio.gz')
+ initrd_hash = '1dbb8a396e916847325284dbe2151167'
+ initrd_path_gz = self.fetch_asset(initrd_url, algorithm='md5',
+ asset_hash=initrd_hash)
+ initrd_path = self.workdir + "rootfs.cpio"
+ archive.gzip_uncompress(initrd_path_gz, initrd_path)
+
+ kernel_command_line = (self.KERNEL_COMMON_COMMAND_LINE +
+ 'console=ttyS0 console=tty '
+ 'rdinit=/sbin/init noreboot')
+ console_pattern = 'Boot successful.'
+ self.run_rr(kernel_path, kernel_command_line, console_pattern, shift=5,
+ args=('-initrd', initrd_path, '-cpu', '5KEc'))
+
+ def do_test_mips_malta32el_nanomips(self, kernel_path_xz):
+ kernel_path = self.workdir + "kernel"
+ with lzma.open(kernel_path_xz, 'rb') as f_in:
+ with open(kernel_path, 'wb') as f_out:
+ shutil.copyfileobj(f_in, f_out)
+
+ kernel_command_line = (self.KERNEL_COMMON_COMMAND_LINE +
+ 'mem=256m@@0x0 '
+ 'console=ttyS0')
+ console_pattern = 'Kernel command line: %s' % kernel_command_line
+ self.run_rr(kernel_path, kernel_command_line, console_pattern, shift=5,
+ args=('-cpu', 'I7200'))
+
+ def test_mips_malta32el_nanomips_4k(self):
+ """
+ :avocado: tags=arch:mipsel
+ :avocado: tags=machine:malta
+ :avocado: tags=endian:little
+ """
+ kernel_url = ('https://mipsdistros.mips.com/LinuxDistro/nanomips/'
+ 'kernels/v4.15.18-432-gb2eb9a8b07a1-20180627102142/'
+ 'generic_nano32r6el_page4k.xz')
+ kernel_hash = '477456aafd2a0f1ddc9482727f20fe9575565dd6'
+ kernel_path_xz = self.fetch_asset(kernel_url, asset_hash=kernel_hash)
+ self.do_test_mips_malta32el_nanomips(kernel_path_xz)
+
+ def test_mips_malta32el_nanomips_16k_up(self):
+ """
+ :avocado: tags=arch:mipsel
+ :avocado: tags=machine:malta
+ :avocado: tags=endian:little
+ """
+ kernel_url = ('https://mipsdistros.mips.com/LinuxDistro/nanomips/'
+ 'kernels/v4.15.18-432-gb2eb9a8b07a1-20180627102142/'
+ 'generic_nano32r6el_page16k_up.xz')
+ kernel_hash = 'e882868f944c71c816e832e2303b7874d044a7bc'
+ kernel_path_xz = self.fetch_asset(kernel_url, asset_hash=kernel_hash)
+ self.do_test_mips_malta32el_nanomips(kernel_path_xz)
+
+ def test_mips_malta32el_nanomips_64k_dbg(self):
+ """
+ :avocado: tags=arch:mipsel
+ :avocado: tags=machine:malta
+ :avocado: tags=endian:little
+ """
+ kernel_url = ('https://mipsdistros.mips.com/LinuxDistro/nanomips/'
+ 'kernels/v4.15.18-432-gb2eb9a8b07a1-20180627102142/'
+ 'generic_nano32r6el_page64k_dbg.xz')
+ kernel_hash = '18d1c68f2e23429e266ca39ba5349ccd0aeb7180'
+ kernel_path_xz = self.fetch_asset(kernel_url, asset_hash=kernel_hash)
+ self.do_test_mips_malta32el_nanomips(kernel_path_xz)
diff --git a/util/cutils.c b/util/cutils.c
index 8da34e04b0..be4e43a9ef 100644
--- a/util/cutils.c
+++ b/util/cutils.c
@@ -885,6 +885,20 @@ char *size_to_str(uint64_t val)
return g_strdup_printf("%0.3g %sB", (double)val / div, suffixes[i]);
}
+char *freq_to_str(uint64_t freq_hz)
+{
+ static const char *const suffixes[] = { "", "K", "M", "G", "T", "P", "E" };
+ double freq = freq_hz;
+ size_t idx = 0;
+
+ while (freq >= 1000.0 && idx < ARRAY_SIZE(suffixes)) {
+ freq /= 1000.0;
+ idx++;
+ }
+
+ return g_strdup_printf("%0.3g %sHz", freq, suffixes[idx]);
+}
+
int qemu_pstrcmp0(const char **str1, const char **str2)
{
return g_strcmp0(*str1, *str2);