aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--MAINTAINERS7
-rw-r--r--default-configs/sparc64-softmmu.mak2
-rw-r--r--hw/sparc64/Makefile.objs2
-rw-r--r--hw/sparc64/niagara.c177
-rw-r--r--hw/sparc64/sparc64.c378
-rw-r--r--hw/sparc64/sun4u.c379
-rw-r--r--hw/timer/Makefile.objs2
-rw-r--r--hw/timer/sun4v-rtc.c102
-rw-r--r--include/hw/sparc/sparc64.h5
-rw-r--r--include/hw/timer/sun4v-rtc.h1
-rw-r--r--linux-user/main.c2
-rw-r--r--qemu-doc.texi14
-rw-r--r--target/sparc/asi.h1
-rw-r--r--target/sparc/cpu.c13
-rw-r--r--target/sparc/cpu.h105
-rw-r--r--target/sparc/helper.h1
-rw-r--r--target/sparc/int64_helper.c43
-rw-r--r--target/sparc/ldst_helper.c387
-rw-r--r--target/sparc/machine.c4
-rw-r--r--target/sparc/mmu_helper.c20
-rw-r--r--target/sparc/translate.c64
-rw-r--r--target/sparc/win_helper.c46
22 files changed, 1227 insertions, 528 deletions
diff --git a/MAINTAINERS b/MAINTAINERS
index 1444b26..b5ebfab 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -725,6 +725,13 @@ S: Maintained
F: hw/sparc64/sun4u.c
F: pc-bios/openbios-sparc64
+Sun4v
+M: Artyom Tarasenko <atar4qemu@gmail.com>
+S: Maintained
+F: hw/sparc64/sun4v.c
+F: hw/timer/sun4v-rtc.c
+F: include/hw/timer/sun4v-rtc.h
+
Leon3
M: Fabien Chouteau <chouteau@adacore.com>
S: Maintained
diff --git a/default-configs/sparc64-softmmu.mak b/default-configs/sparc64-softmmu.mak
index c0cdd64..c581e61 100644
--- a/default-configs/sparc64-softmmu.mak
+++ b/default-configs/sparc64-softmmu.mak
@@ -13,3 +13,5 @@ CONFIG_IDE_CMD646=y
CONFIG_PCI_APB=y
CONFIG_MC146818RTC=y
CONFIG_ISA_TESTDEV=y
+CONFIG_EMPTY_SLOT=y
+CONFIG_SUN4V_RTC=y
diff --git a/hw/sparc64/Makefile.objs b/hw/sparc64/Makefile.objs
index a84cfe3..cf9de21 100644
--- a/hw/sparc64/Makefile.objs
+++ b/hw/sparc64/Makefile.objs
@@ -1 +1,3 @@
+obj-y += sparc64.o
obj-y += sun4u.o
+obj-y += niagara.o \ No newline at end of file
diff --git a/hw/sparc64/niagara.c b/hw/sparc64/niagara.c
new file mode 100644
index 0000000..b55d4bb
--- /dev/null
+++ b/hw/sparc64/niagara.c
@@ -0,0 +1,177 @@
+/*
+ * QEMU Sun4v/Niagara System Emulator
+ *
+ * Copyright (c) 2016 Artyom Tarasenko
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+#include "qemu/osdep.h"
+#include "qapi/error.h"
+#include "qemu-common.h"
+#include "cpu.h"
+#include "hw/hw.h"
+#include "hw/boards.h"
+#include "hw/char/serial.h"
+#include "hw/empty_slot.h"
+#include "hw/loader.h"
+#include "hw/sparc/sparc64.h"
+#include "hw/timer/sun4v-rtc.h"
+#include "exec/address-spaces.h"
+#include "sysemu/block-backend.h"
+
+
+typedef struct NiagaraBoardState {
+ MemoryRegion hv_ram;
+ MemoryRegion partition_ram;
+ MemoryRegion nvram;
+ MemoryRegion md_rom;
+ MemoryRegion hv_rom;
+ MemoryRegion vdisk_ram;
+ MemoryRegion prom;
+} NiagaraBoardState;
+
+#define NIAGARA_HV_RAM_BASE 0x100000ULL
+#define NIAGARA_HV_RAM_SIZE 0x3f00000ULL /* 63 MiB */
+
+#define NIAGARA_PARTITION_RAM_BASE 0x80000000ULL
+
+#define NIAGARA_UART_BASE 0x1f10000000ULL
+
+#define NIAGARA_NVRAM_BASE 0x1f11000000ULL
+#define NIAGARA_NVRAM_SIZE 0x2000
+
+#define NIAGARA_MD_ROM_BASE 0x1f12000000ULL
+#define NIAGARA_MD_ROM_SIZE 0x2000
+
+#define NIAGARA_HV_ROM_BASE 0x1f12080000ULL
+#define NIAGARA_HV_ROM_SIZE 0x2000
+
+#define NIAGARA_IOBBASE 0x9800000000ULL
+#define NIAGARA_IOBSIZE 0x0100000000ULL
+
+#define NIAGARA_VDISK_BASE 0x1f40000000ULL
+#define NIAGARA_RTC_BASE 0xfff0c1fff8ULL
+#define NIAGARA_UART_BASE 0x1f10000000ULL
+
+/* Firmware layout
+ *
+ * |------------------|
+ * | openboot.bin |
+ * |------------------| PROM_ADDR + OBP_OFFSET
+ * | q.bin |
+ * |------------------| PROM_ADDR + Q_OFFSET
+ * | reset.bin |
+ * |------------------| PROM_ADDR
+ */
+#define NIAGARA_PROM_BASE 0xfff0000000ULL
+#define NIAGARA_Q_OFFSET 0x10000ULL
+#define NIAGARA_OBP_OFFSET 0x80000ULL
+#define PROM_SIZE_MAX (4 * 1024 * 1024)
+
+/* Niagara hardware initialisation */
+static void niagara_init(MachineState *machine)
+{
+ NiagaraBoardState *s = g_new(NiagaraBoardState, 1);
+ DriveInfo *dinfo = drive_get_next(IF_PFLASH);
+ MemoryRegion *sysmem = get_system_memory();
+
+ /* init CPUs */
+ sparc64_cpu_devinit(machine->cpu_model, "Sun UltraSparc T1",
+ NIAGARA_PROM_BASE);
+ /* set up devices */
+ memory_region_allocate_system_memory(&s->hv_ram, NULL, "sun4v-hv.ram",
+ NIAGARA_HV_RAM_SIZE);
+ memory_region_add_subregion(sysmem, NIAGARA_HV_RAM_BASE, &s->hv_ram);
+
+ memory_region_allocate_system_memory(&s->partition_ram, NULL,
+ "sun4v-partition.ram",
+ machine->ram_size);
+ memory_region_add_subregion(sysmem, NIAGARA_PARTITION_RAM_BASE,
+ &s->partition_ram);
+
+ memory_region_allocate_system_memory(&s->nvram, NULL,
+ "sun4v.nvram", NIAGARA_NVRAM_SIZE);
+ memory_region_add_subregion(sysmem, NIAGARA_NVRAM_BASE, &s->nvram);
+ memory_region_allocate_system_memory(&s->md_rom, NULL,
+ "sun4v-md.rom", NIAGARA_MD_ROM_SIZE);
+ memory_region_add_subregion(sysmem, NIAGARA_MD_ROM_BASE, &s->md_rom);
+ memory_region_allocate_system_memory(&s->hv_rom, NULL,
+ "sun4v-hv.rom", NIAGARA_HV_ROM_SIZE);
+ memory_region_add_subregion(sysmem, NIAGARA_HV_ROM_BASE, &s->hv_rom);
+ memory_region_allocate_system_memory(&s->prom, NULL,
+ "sun4v.prom", PROM_SIZE_MAX);
+ memory_region_add_subregion(sysmem, NIAGARA_PROM_BASE, &s->prom);
+
+ rom_add_file_fixed("nvram1", NIAGARA_NVRAM_BASE, -1);
+ rom_add_file_fixed("1up-md.bin", NIAGARA_MD_ROM_BASE, -1);
+ rom_add_file_fixed("1up-hv.bin", NIAGARA_HV_ROM_BASE, -1);
+
+ rom_add_file_fixed("reset.bin", NIAGARA_PROM_BASE, -1);
+ rom_add_file_fixed("q.bin", NIAGARA_PROM_BASE + NIAGARA_Q_OFFSET, -1);
+ rom_add_file_fixed("openboot.bin", NIAGARA_PROM_BASE + NIAGARA_OBP_OFFSET,
+ -1);
+
+ /* the virtual ramdisk is kind of initrd, but it resides
+ outside of the partition RAM */
+ if (dinfo) {
+ BlockBackend *blk = blk_by_legacy_dinfo(dinfo);
+ int size = blk_getlength(blk);
+ if (size > 0) {
+ memory_region_allocate_system_memory(&s->vdisk_ram, NULL,
+ "sun4v_vdisk.ram", size);
+ memory_region_add_subregion(get_system_memory(),
+ NIAGARA_VDISK_BASE, &s->vdisk_ram);
+ dinfo->is_default = 1;
+ rom_add_file_fixed(blk_bs(blk)->filename, NIAGARA_VDISK_BASE, -1);
+ } else {
+ fprintf(stderr, "qemu: could not load ram disk '%s'\n",
+ blk_bs(blk)->filename);
+ exit(1);
+ }
+ }
+ serial_mm_init(sysmem, NIAGARA_UART_BASE, 0, NULL, 115200,
+ serial_hds[0], DEVICE_BIG_ENDIAN);
+
+ empty_slot_init(NIAGARA_IOBBASE, NIAGARA_IOBSIZE);
+ sun4v_rtc_init(NIAGARA_RTC_BASE);
+}
+
+static void niagara_class_init(ObjectClass *oc, void *data)
+{
+ MachineClass *mc = MACHINE_CLASS(oc);
+
+ mc->desc = "Sun4v platform, Niagara";
+ mc->init = niagara_init;
+ mc->max_cpus = 1; /* XXX for now */
+ mc->default_boot_order = "c";
+}
+
+static const TypeInfo niagara_type = {
+ .name = MACHINE_TYPE_NAME("niagara"),
+ .parent = TYPE_MACHINE,
+ .class_init = niagara_class_init,
+};
+
+static void niagara_register_types(void)
+{
+ type_register_static(&niagara_type);
+}
+
+type_init(niagara_register_types)
diff --git a/hw/sparc64/sparc64.c b/hw/sparc64/sparc64.c
new file mode 100644
index 0000000..b3d219c
--- /dev/null
+++ b/hw/sparc64/sparc64.c
@@ -0,0 +1,378 @@
+/*
+ * QEMU Sun4u/Sun4v System Emulator common routines
+ *
+ * Copyright (c) 2005 Fabrice Bellard
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+
+#include "qemu/osdep.h"
+#include "cpu.h"
+#include "hw/char/serial.h"
+#include "hw/sparc/sparc64.h"
+#include "qemu/timer.h"
+
+
+//#define DEBUG_IRQ
+//#define DEBUG_TIMER
+
+#ifdef DEBUG_IRQ
+#define CPUIRQ_DPRINTF(fmt, ...) \
+ do { printf("CPUIRQ: " fmt , ## __VA_ARGS__); } while (0)
+#else
+#define CPUIRQ_DPRINTF(fmt, ...)
+#endif
+
+#ifdef DEBUG_TIMER
+#define TIMER_DPRINTF(fmt, ...) \
+ do { printf("TIMER: " fmt , ## __VA_ARGS__); } while (0)
+#else
+#define TIMER_DPRINTF(fmt, ...)
+#endif
+
+#define TICK_MAX 0x7fffffffffffffffULL
+
+void cpu_check_irqs(CPUSPARCState *env)
+{
+ CPUState *cs;
+ uint32_t pil = env->pil_in |
+ (env->softint & ~(SOFTINT_TIMER | SOFTINT_STIMER));
+
+ /* TT_IVEC has a higher priority (16) than TT_EXTINT (31..17) */
+ if (env->ivec_status & 0x20) {
+ return;
+ }
+ cs = CPU(sparc_env_get_cpu(env));
+ /* check if TM or SM in SOFTINT are set
+ setting these also causes interrupt 14 */
+ if (env->softint & (SOFTINT_TIMER | SOFTINT_STIMER)) {
+ pil |= 1 << 14;
+ }
+
+ /* The bit corresponding to psrpil is (1<< psrpil), the next bit
+ is (2 << psrpil). */
+ if (pil < (2 << env->psrpil)) {
+ if (cs->interrupt_request & CPU_INTERRUPT_HARD) {
+ CPUIRQ_DPRINTF("Reset CPU IRQ (current interrupt %x)\n",
+ env->interrupt_index);
+ env->interrupt_index = 0;
+ cpu_reset_interrupt(cs, CPU_INTERRUPT_HARD);
+ }
+ return;
+ }
+
+ if (cpu_interrupts_enabled(env)) {
+
+ unsigned int i;
+
+ for (i = 15; i > env->psrpil; i--) {
+ if (pil & (1 << i)) {
+ int old_interrupt = env->interrupt_index;
+ int new_interrupt = TT_EXTINT | i;
+
+ if (unlikely(env->tl > 0 && cpu_tsptr(env)->tt > new_interrupt
+ && ((cpu_tsptr(env)->tt & 0x1f0) == TT_EXTINT))) {
+ CPUIRQ_DPRINTF("Not setting CPU IRQ: TL=%d "
+ "current %x >= pending %x\n",
+ env->tl, cpu_tsptr(env)->tt, new_interrupt);
+ } else if (old_interrupt != new_interrupt) {
+ env->interrupt_index = new_interrupt;
+ CPUIRQ_DPRINTF("Set CPU IRQ %d old=%x new=%x\n", i,
+ old_interrupt, new_interrupt);
+ cpu_interrupt(cs, CPU_INTERRUPT_HARD);
+ }
+ break;
+ }
+ }
+ } else if (cs->interrupt_request & CPU_INTERRUPT_HARD) {
+ CPUIRQ_DPRINTF("Interrupts disabled, pil=%08x pil_in=%08x softint=%08x "
+ "current interrupt %x\n",
+ pil, env->pil_in, env->softint, env->interrupt_index);
+ env->interrupt_index = 0;
+ cpu_reset_interrupt(cs, CPU_INTERRUPT_HARD);
+ }
+}
+
+static void cpu_kick_irq(SPARCCPU *cpu)
+{
+ CPUState *cs = CPU(cpu);
+ CPUSPARCState *env = &cpu->env;
+
+ cs->halted = 0;
+ cpu_check_irqs(env);
+ qemu_cpu_kick(cs);
+}
+
+void sparc64_cpu_set_ivec_irq(void *opaque, int irq, int level)
+{
+ SPARCCPU *cpu = opaque;
+ CPUSPARCState *env = &cpu->env;
+ CPUState *cs;
+
+ if (level) {
+ if (!(env->ivec_status & 0x20)) {
+ CPUIRQ_DPRINTF("Raise IVEC IRQ %d\n", irq);
+ cs = CPU(cpu);
+ cs->halted = 0;
+ env->interrupt_index = TT_IVEC;
+ env->ivec_status |= 0x20;
+ env->ivec_data[0] = (0x1f << 6) | irq;
+ env->ivec_data[1] = 0;
+ env->ivec_data[2] = 0;
+ cpu_interrupt(cs, CPU_INTERRUPT_HARD);
+ }
+ } else {
+ if (env->ivec_status & 0x20) {
+ CPUIRQ_DPRINTF("Lower IVEC IRQ %d\n", irq);
+ cs = CPU(cpu);
+ env->ivec_status &= ~0x20;
+ cpu_reset_interrupt(cs, CPU_INTERRUPT_HARD);
+ }
+ }
+}
+
+typedef struct ResetData {
+ SPARCCPU *cpu;
+ uint64_t prom_addr;
+} ResetData;
+
+static CPUTimer *cpu_timer_create(const char *name, SPARCCPU *cpu,
+ QEMUBHFunc *cb, uint32_t frequency,
+ uint64_t disabled_mask, uint64_t npt_mask)
+{
+ CPUTimer *timer = g_malloc0(sizeof(CPUTimer));
+
+ timer->name = name;
+ timer->frequency = frequency;
+ timer->disabled_mask = disabled_mask;
+ timer->npt_mask = npt_mask;
+
+ timer->disabled = 1;
+ timer->npt = 1;
+ timer->clock_offset = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL);
+
+ timer->qtimer = timer_new_ns(QEMU_CLOCK_VIRTUAL, cb, cpu);
+
+ return timer;
+}
+
+static void cpu_timer_reset(CPUTimer *timer)
+{
+ timer->disabled = 1;
+ timer->clock_offset = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL);
+
+ timer_del(timer->qtimer);
+}
+
+static void main_cpu_reset(void *opaque)
+{
+ ResetData *s = (ResetData *)opaque;
+ CPUSPARCState *env = &s->cpu->env;
+ static unsigned int nr_resets;
+
+ cpu_reset(CPU(s->cpu));
+
+ cpu_timer_reset(env->tick);
+ cpu_timer_reset(env->stick);
+ cpu_timer_reset(env->hstick);
+
+ env->gregs[1] = 0; /* Memory start */
+ env->gregs[2] = ram_size; /* Memory size */
+ env->gregs[3] = 0; /* Machine description XXX */
+ if (nr_resets++ == 0) {
+ /* Power on reset */
+ env->pc = s->prom_addr + 0x20ULL;
+ } else {
+ env->pc = s->prom_addr + 0x40ULL;
+ }
+ env->npc = env->pc + 4;
+}
+
+static void tick_irq(void *opaque)
+{
+ SPARCCPU *cpu = opaque;
+ CPUSPARCState *env = &cpu->env;
+
+ CPUTimer *timer = env->tick;
+
+ if (timer->disabled) {
+ CPUIRQ_DPRINTF("tick_irq: softint disabled\n");
+ return;
+ } else {
+ CPUIRQ_DPRINTF("tick: fire\n");
+ }
+
+ env->softint |= SOFTINT_TIMER;
+ cpu_kick_irq(cpu);
+}
+
+static void stick_irq(void *opaque)
+{
+ SPARCCPU *cpu = opaque;
+ CPUSPARCState *env = &cpu->env;
+
+ CPUTimer *timer = env->stick;
+
+ if (timer->disabled) {
+ CPUIRQ_DPRINTF("stick_irq: softint disabled\n");
+ return;
+ } else {
+ CPUIRQ_DPRINTF("stick: fire\n");
+ }
+
+ env->softint |= SOFTINT_STIMER;
+ cpu_kick_irq(cpu);
+}
+
+static void hstick_irq(void *opaque)
+{
+ SPARCCPU *cpu = opaque;
+ CPUSPARCState *env = &cpu->env;
+
+ CPUTimer *timer = env->hstick;
+
+ if (timer->disabled) {
+ CPUIRQ_DPRINTF("hstick_irq: softint disabled\n");
+ return;
+ } else {
+ CPUIRQ_DPRINTF("hstick: fire\n");
+ }
+
+ env->softint |= SOFTINT_STIMER;
+ cpu_kick_irq(cpu);
+}
+
+static int64_t cpu_to_timer_ticks(int64_t cpu_ticks, uint32_t frequency)
+{
+ return muldiv64(cpu_ticks, NANOSECONDS_PER_SECOND, frequency);
+}
+
+static uint64_t timer_to_cpu_ticks(int64_t timer_ticks, uint32_t frequency)
+{
+ return muldiv64(timer_ticks, frequency, NANOSECONDS_PER_SECOND);
+}
+
+void cpu_tick_set_count(CPUTimer *timer, uint64_t count)
+{
+ uint64_t real_count = count & ~timer->npt_mask;
+ uint64_t npt_bit = count & timer->npt_mask;
+
+ int64_t vm_clock_offset = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) -
+ cpu_to_timer_ticks(real_count, timer->frequency);
+
+ TIMER_DPRINTF("%s set_count count=0x%016lx (npt %s) p=%p\n",
+ timer->name, real_count,
+ timer->npt ? "disabled" : "enabled", timer);
+
+ timer->npt = npt_bit ? 1 : 0;
+ timer->clock_offset = vm_clock_offset;
+}
+
+uint64_t cpu_tick_get_count(CPUTimer *timer)
+{
+ uint64_t real_count = timer_to_cpu_ticks(
+ qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) - timer->clock_offset,
+ timer->frequency);
+
+ TIMER_DPRINTF("%s get_count count=0x%016lx (npt %s) p=%p\n",
+ timer->name, real_count,
+ timer->npt ? "disabled" : "enabled", timer);
+
+ if (timer->npt) {
+ real_count |= timer->npt_mask;
+ }
+
+ return real_count;
+}
+
+void cpu_tick_set_limit(CPUTimer *timer, uint64_t limit)
+{
+ int64_t now = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL);
+
+ uint64_t real_limit = limit & ~timer->disabled_mask;
+ timer->disabled = (limit & timer->disabled_mask) ? 1 : 0;
+
+ int64_t expires = cpu_to_timer_ticks(real_limit, timer->frequency) +
+ timer->clock_offset;
+
+ if (expires < now) {
+ expires = now + 1;
+ }
+
+ TIMER_DPRINTF("%s set_limit limit=0x%016lx (%s) p=%p "
+ "called with limit=0x%016lx at 0x%016lx (delta=0x%016lx)\n",
+ timer->name, real_limit,
+ timer->disabled ? "disabled" : "enabled",
+ timer, limit,
+ timer_to_cpu_ticks(now - timer->clock_offset,
+ timer->frequency),
+ timer_to_cpu_ticks(expires - now, timer->frequency));
+
+ if (!real_limit) {
+ TIMER_DPRINTF("%s set_limit limit=ZERO - not starting timer\n",
+ timer->name);
+ timer_del(timer->qtimer);
+ } else if (timer->disabled) {
+ timer_del(timer->qtimer);
+ } else {
+ timer_mod(timer->qtimer, expires);
+ }
+}
+
+SPARCCPU *sparc64_cpu_devinit(const char *cpu_model,
+ const char *default_cpu_model, uint64_t prom_addr)
+{
+ SPARCCPU *cpu;
+ CPUSPARCState *env;
+ ResetData *reset_info;
+
+ uint32_t tick_frequency = 100 * 1000000;
+ uint32_t stick_frequency = 100 * 1000000;
+ uint32_t hstick_frequency = 100 * 1000000;
+
+ if (cpu_model == NULL) {
+ cpu_model = default_cpu_model;
+ }
+ cpu = cpu_sparc_init(cpu_model);
+ if (cpu == NULL) {
+ fprintf(stderr, "Unable to find Sparc CPU definition\n");
+ exit(1);
+ }
+ env = &cpu->env;
+
+ env->tick = cpu_timer_create("tick", cpu, tick_irq,
+ tick_frequency, TICK_INT_DIS,
+ TICK_NPT_MASK);
+
+ env->stick = cpu_timer_create("stick", cpu, stick_irq,
+ stick_frequency, TICK_INT_DIS,
+ TICK_NPT_MASK);
+
+ env->hstick = cpu_timer_create("hstick", cpu, hstick_irq,
+ hstick_frequency, TICK_INT_DIS,
+ TICK_NPT_MASK);
+
+ reset_info = g_malloc0(sizeof(ResetData));
+ reset_info->cpu = cpu;
+ reset_info->prom_addr = prom_addr;
+ qemu_register_reset(main_cpu_reset, reset_info);
+
+ return cpu;
+}
diff --git a/hw/sparc64/sun4u.c b/hw/sparc64/sun4u.c
index 4663315..d1a6bca 100644
--- a/hw/sparc64/sun4u.c
+++ b/hw/sparc64/sun4u.c
@@ -38,25 +38,15 @@
#include "hw/boards.h"
#include "hw/nvram/sun_nvram.h"
#include "hw/nvram/chrp_nvram.h"
+#include "hw/sparc/sparc64.h"
#include "hw/nvram/fw_cfg.h"
#include "hw/sysbus.h"
#include "hw/ide.h"
#include "hw/loader.h"
#include "elf.h"
-#include "sysemu/block-backend.h"
-#include "exec/address-spaces.h"
#include "qemu/cutils.h"
-//#define DEBUG_IRQ
//#define DEBUG_EBUS
-//#define DEBUG_TIMER
-
-#ifdef DEBUG_IRQ
-#define CPUIRQ_DPRINTF(fmt, ...) \
- do { printf("CPUIRQ: " fmt , ## __VA_ARGS__); } while (0)
-#else
-#define CPUIRQ_DPRINTF(fmt, ...)
-#endif
#ifdef DEBUG_EBUS
#define EBUS_DPRINTF(fmt, ...) \
@@ -65,13 +55,6 @@
#define EBUS_DPRINTF(fmt, ...)
#endif
-#ifdef DEBUG_TIMER
-#define TIMER_DPRINTF(fmt, ...) \
- do { printf("TIMER: " fmt , ## __VA_ARGS__); } while (0)
-#else
-#define TIMER_DPRINTF(fmt, ...)
-#endif
-
#define KERNEL_LOAD_ADDR 0x00404000
#define CMDLINE_ADDR 0x003ff000
#define PROM_SIZE_MAX (4 * 1024 * 1024)
@@ -89,8 +72,6 @@
#define IVEC_MAX 0x40
-#define TICK_MAX 0x7fffffffffffffffULL
-
struct hwdef {
const char * const default_cpu_model;
uint16_t machine_id;
@@ -216,293 +197,11 @@ static uint64_t sun4u_load_kernel(const char *kernel_filename,
return kernel_size;
}
-void cpu_check_irqs(CPUSPARCState *env)
-{
- CPUState *cs;
- uint32_t pil = env->pil_in |
- (env->softint & ~(SOFTINT_TIMER | SOFTINT_STIMER));
-
- /* TT_IVEC has a higher priority (16) than TT_EXTINT (31..17) */
- if (env->ivec_status & 0x20) {
- return;
- }
- cs = CPU(sparc_env_get_cpu(env));
- /* check if TM or SM in SOFTINT are set
- setting these also causes interrupt 14 */
- if (env->softint & (SOFTINT_TIMER | SOFTINT_STIMER)) {
- pil |= 1 << 14;
- }
-
- /* The bit corresponding to psrpil is (1<< psrpil), the next bit
- is (2 << psrpil). */
- if (pil < (2 << env->psrpil)){
- if (cs->interrupt_request & CPU_INTERRUPT_HARD) {
- CPUIRQ_DPRINTF("Reset CPU IRQ (current interrupt %x)\n",
- env->interrupt_index);
- env->interrupt_index = 0;
- cpu_reset_interrupt(cs, CPU_INTERRUPT_HARD);
- }
- return;
- }
-
- if (cpu_interrupts_enabled(env)) {
-
- unsigned int i;
-
- for (i = 15; i > env->psrpil; i--) {
- if (pil & (1 << i)) {
- int old_interrupt = env->interrupt_index;
- int new_interrupt = TT_EXTINT | i;
-
- if (unlikely(env->tl > 0 && cpu_tsptr(env)->tt > new_interrupt
- && ((cpu_tsptr(env)->tt & 0x1f0) == TT_EXTINT))) {
- CPUIRQ_DPRINTF("Not setting CPU IRQ: TL=%d "
- "current %x >= pending %x\n",
- env->tl, cpu_tsptr(env)->tt, new_interrupt);
- } else if (old_interrupt != new_interrupt) {
- env->interrupt_index = new_interrupt;
- CPUIRQ_DPRINTF("Set CPU IRQ %d old=%x new=%x\n", i,
- old_interrupt, new_interrupt);
- cpu_interrupt(cs, CPU_INTERRUPT_HARD);
- }
- break;
- }
- }
- } else if (cs->interrupt_request & CPU_INTERRUPT_HARD) {
- CPUIRQ_DPRINTF("Interrupts disabled, pil=%08x pil_in=%08x softint=%08x "
- "current interrupt %x\n",
- pil, env->pil_in, env->softint, env->interrupt_index);
- env->interrupt_index = 0;
- cpu_reset_interrupt(cs, CPU_INTERRUPT_HARD);
- }
-}
-
-static void cpu_kick_irq(SPARCCPU *cpu)
-{
- CPUState *cs = CPU(cpu);
- CPUSPARCState *env = &cpu->env;
-
- cs->halted = 0;
- cpu_check_irqs(env);
- qemu_cpu_kick(cs);
-}
-
-static void cpu_set_ivec_irq(void *opaque, int irq, int level)
-{
- SPARCCPU *cpu = opaque;
- CPUSPARCState *env = &cpu->env;
- CPUState *cs;
-
- if (level) {
- if (!(env->ivec_status & 0x20)) {
- CPUIRQ_DPRINTF("Raise IVEC IRQ %d\n", irq);
- cs = CPU(cpu);
- cs->halted = 0;
- env->interrupt_index = TT_IVEC;
- env->ivec_status |= 0x20;
- env->ivec_data[0] = (0x1f << 6) | irq;
- env->ivec_data[1] = 0;
- env->ivec_data[2] = 0;
- cpu_interrupt(cs, CPU_INTERRUPT_HARD);
- }
- } else {
- if (env->ivec_status & 0x20) {
- CPUIRQ_DPRINTF("Lower IVEC IRQ %d\n", irq);
- cs = CPU(cpu);
- env->ivec_status &= ~0x20;
- cpu_reset_interrupt(cs, CPU_INTERRUPT_HARD);
- }
- }
-}
-
typedef struct ResetData {
SPARCCPU *cpu;
uint64_t prom_addr;
} ResetData;
-static CPUTimer *cpu_timer_create(const char *name, SPARCCPU *cpu,
- QEMUBHFunc *cb, uint32_t frequency,
- uint64_t disabled_mask, uint64_t npt_mask)
-{
- CPUTimer *timer = g_malloc0(sizeof (CPUTimer));
-
- timer->name = name;
- timer->frequency = frequency;
- timer->disabled_mask = disabled_mask;
- timer->npt_mask = npt_mask;
-
- timer->disabled = 1;
- timer->npt = 1;
- timer->clock_offset = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL);
-
- timer->qtimer = timer_new_ns(QEMU_CLOCK_VIRTUAL, cb, cpu);
-
- return timer;
-}
-
-static void cpu_timer_reset(CPUTimer *timer)
-{
- timer->disabled = 1;
- timer->clock_offset = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL);
-
- timer_del(timer->qtimer);
-}
-
-static void main_cpu_reset(void *opaque)
-{
- ResetData *s = (ResetData *)opaque;
- CPUSPARCState *env = &s->cpu->env;
- static unsigned int nr_resets;
-
- cpu_reset(CPU(s->cpu));
-
- cpu_timer_reset(env->tick);
- cpu_timer_reset(env->stick);
- cpu_timer_reset(env->hstick);
-
- env->gregs[1] = 0; // Memory start
- env->gregs[2] = ram_size; // Memory size
- env->gregs[3] = 0; // Machine description XXX
- if (nr_resets++ == 0) {
- /* Power on reset */
- env->pc = s->prom_addr + 0x20ULL;
- } else {
- env->pc = s->prom_addr + 0x40ULL;
- }
- env->npc = env->pc + 4;
-}
-
-static void tick_irq(void *opaque)
-{
- SPARCCPU *cpu = opaque;
- CPUSPARCState *env = &cpu->env;
-
- CPUTimer* timer = env->tick;
-
- if (timer->disabled) {
- CPUIRQ_DPRINTF("tick_irq: softint disabled\n");
- return;
- } else {
- CPUIRQ_DPRINTF("tick: fire\n");
- }
-
- env->softint |= SOFTINT_TIMER;
- cpu_kick_irq(cpu);
-}
-
-static void stick_irq(void *opaque)
-{
- SPARCCPU *cpu = opaque;
- CPUSPARCState *env = &cpu->env;
-
- CPUTimer* timer = env->stick;
-
- if (timer->disabled) {
- CPUIRQ_DPRINTF("stick_irq: softint disabled\n");
- return;
- } else {
- CPUIRQ_DPRINTF("stick: fire\n");
- }
-
- env->softint |= SOFTINT_STIMER;
- cpu_kick_irq(cpu);
-}
-
-static void hstick_irq(void *opaque)
-{
- SPARCCPU *cpu = opaque;
- CPUSPARCState *env = &cpu->env;
-
- CPUTimer* timer = env->hstick;
-
- if (timer->disabled) {
- CPUIRQ_DPRINTF("hstick_irq: softint disabled\n");
- return;
- } else {
- CPUIRQ_DPRINTF("hstick: fire\n");
- }
-
- env->softint |= SOFTINT_STIMER;
- cpu_kick_irq(cpu);
-}
-
-static int64_t cpu_to_timer_ticks(int64_t cpu_ticks, uint32_t frequency)
-{
- return muldiv64(cpu_ticks, NANOSECONDS_PER_SECOND, frequency);
-}
-
-static uint64_t timer_to_cpu_ticks(int64_t timer_ticks, uint32_t frequency)
-{
- return muldiv64(timer_ticks, frequency, NANOSECONDS_PER_SECOND);
-}
-
-void cpu_tick_set_count(CPUTimer *timer, uint64_t count)
-{
- uint64_t real_count = count & ~timer->npt_mask;
- uint64_t npt_bit = count & timer->npt_mask;
-
- int64_t vm_clock_offset = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) -
- cpu_to_timer_ticks(real_count, timer->frequency);
-
- TIMER_DPRINTF("%s set_count count=0x%016lx (npt %s) p=%p\n",
- timer->name, real_count,
- timer->npt ? "disabled" : "enabled", timer);
-
- timer->npt = npt_bit ? 1 : 0;
- timer->clock_offset = vm_clock_offset;
-}
-
-uint64_t cpu_tick_get_count(CPUTimer *timer)
-{
- uint64_t real_count = timer_to_cpu_ticks(
- qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) - timer->clock_offset,
- timer->frequency);
-
- TIMER_DPRINTF("%s get_count count=0x%016lx (npt %s) p=%p\n",
- timer->name, real_count,
- timer->npt ? "disabled" : "enabled", timer);
-
- if (timer->npt) {
- real_count |= timer->npt_mask;
- }
-
- return real_count;
-}
-
-void cpu_tick_set_limit(CPUTimer *timer, uint64_t limit)
-{
- int64_t now = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL);
-
- uint64_t real_limit = limit & ~timer->disabled_mask;
- timer->disabled = (limit & timer->disabled_mask) ? 1 : 0;
-
- int64_t expires = cpu_to_timer_ticks(real_limit, timer->frequency) +
- timer->clock_offset;
-
- if (expires < now) {
- expires = now + 1;
- }
-
- TIMER_DPRINTF("%s set_limit limit=0x%016lx (%s) p=%p "
- "called with limit=0x%016lx at 0x%016lx (delta=0x%016lx)\n",
- timer->name, real_limit,
- timer->disabled?"disabled":"enabled",
- timer, limit,
- timer_to_cpu_ticks(now - timer->clock_offset,
- timer->frequency),
- timer_to_cpu_ticks(expires - now, timer->frequency));
-
- if (!real_limit) {
- TIMER_DPRINTF("%s set_limit limit=ZERO - not starting timer\n",
- timer->name);
- timer_del(timer->qtimer);
- } else if (timer->disabled) {
- timer_del(timer->qtimer);
- } else {
- timer_mod(timer->qtimer, expires);
- }
-}
-
static void isa_irq_handler(void *opaque, int n, int level)
{
static const int isa_irq_to_ivec[16] = {
@@ -723,46 +422,6 @@ static const TypeInfo ram_info = {
.class_init = ram_class_init,
};
-static SPARCCPU *cpu_devinit(const char *cpu_model, const struct hwdef *hwdef)
-{
- SPARCCPU *cpu;
- CPUSPARCState *env;
- ResetData *reset_info;
-
- uint32_t tick_frequency = 100*1000000;
- uint32_t stick_frequency = 100*1000000;
- uint32_t hstick_frequency = 100*1000000;
-
- if (cpu_model == NULL) {
- cpu_model = hwdef->default_cpu_model;
- }
- cpu = cpu_sparc_init(cpu_model);
- if (cpu == NULL) {
- fprintf(stderr, "Unable to find Sparc CPU definition\n");
- exit(1);
- }
- env = &cpu->env;
-
- env->tick = cpu_timer_create("tick", cpu, tick_irq,
- tick_frequency, TICK_INT_DIS,
- TICK_NPT_MASK);
-
- env->stick = cpu_timer_create("stick", cpu, stick_irq,
- stick_frequency, TICK_INT_DIS,
- TICK_NPT_MASK);
-
- env->hstick = cpu_timer_create("hstick", cpu, hstick_irq,
- hstick_frequency, TICK_INT_DIS,
- TICK_NPT_MASK);
-
- reset_info = g_malloc0(sizeof(ResetData));
- reset_info->cpu = cpu;
- reset_info->prom_addr = hwdef->prom_addr;
- qemu_register_reset(main_cpu_reset, reset_info);
-
- return cpu;
-}
-
static void sun4uv_init(MemoryRegion *address_space_mem,
MachineState *machine,
const struct hwdef *hwdef)
@@ -781,14 +440,15 @@ static void sun4uv_init(MemoryRegion *address_space_mem,
FWCfgState *fw_cfg;
/* init CPUs */
- cpu = cpu_devinit(machine->cpu_model, hwdef);
+ cpu = sparc64_cpu_devinit(machine->cpu_model, hwdef->default_cpu_model,
+ hwdef->prom_addr);
/* set up devices */
ram_init(0, machine->ram_size);
prom_init(hwdef->prom_addr, bios_name);
- ivec_irqs = qemu_allocate_irqs(cpu_set_ivec_irq, cpu, IVEC_MAX);
+ ivec_irqs = qemu_allocate_irqs(sparc64_cpu_set_ivec_irq, cpu, IVEC_MAX);
pci_bus = pci_apb_init(APB_SPECIAL_BASE, APB_MEM_BASE, ivec_irqs, &pci_bus2,
&pci_bus3, &pbm_irqs);
pci_vga_init(pci_bus);
@@ -882,7 +542,6 @@ static void sun4uv_init(MemoryRegion *address_space_mem,
enum {
sun4u_id = 0,
sun4v_id = 64,
- niagara_id,
};
static const struct hwdef hwdefs[] = {
@@ -900,13 +559,6 @@ static const struct hwdef hwdefs[] = {
.prom_addr = 0x1fff0000000ULL,
.console_serial_base = 0,
},
- /* Sun4v generic Niagara machine */
- {
- .default_cpu_model = "Sun UltraSparc T1",
- .machine_id = niagara_id,
- .prom_addr = 0xfff0000000ULL,
- .console_serial_base = 0xfff0c2c000ULL,
- },
};
/* Sun4u hardware initialisation */
@@ -921,12 +573,6 @@ static void sun4v_init(MachineState *machine)
sun4uv_init(get_system_memory(), machine, &hwdefs[1]);
}
-/* Niagara hardware initialisation */
-static void niagara_init(MachineState *machine)
-{
- sun4uv_init(get_system_memory(), machine, &hwdefs[2]);
-}
-
static void sun4u_class_init(ObjectClass *oc, void *data)
{
MachineClass *mc = MACHINE_CLASS(oc);
@@ -960,22 +606,6 @@ static const TypeInfo sun4v_type = {
.class_init = sun4v_class_init,
};
-static void niagara_class_init(ObjectClass *oc, void *data)
-{
- MachineClass *mc = MACHINE_CLASS(oc);
-
- mc->desc = "Sun4v platform, Niagara";
- mc->init = niagara_init;
- mc->max_cpus = 1; /* XXX for now */
- mc->default_boot_order = "c";
-}
-
-static const TypeInfo niagara_type = {
- .name = MACHINE_TYPE_NAME("Niagara"),
- .parent = TYPE_MACHINE,
- .class_init = niagara_class_init,
-};
-
static void sun4u_register_types(void)
{
type_register_static(&ebus_info);
@@ -984,7 +614,6 @@ static void sun4u_register_types(void)
type_register_static(&sun4u_type);
type_register_static(&sun4v_type);
- type_register_static(&niagara_type);
}
type_init(sun4u_register_types)
diff --git a/hw/timer/Makefile.objs b/hw/timer/Makefile.objs
index 7ba8c23..c1e93a3 100644
--- a/hw/timer/Makefile.objs
+++ b/hw/timer/Makefile.objs
@@ -34,3 +34,5 @@ obj-$(CONFIG_ALLWINNER_A10_PIT) += allwinner-a10-pit.o
common-obj-$(CONFIG_STM32F2XX_TIMER) += stm32f2xx_timer.o
common-obj-$(CONFIG_ASPEED_SOC) += aspeed_timer.o
+
+common-obj-$(CONFIG_SUN4V_RTC) += sun4v-rtc.o
diff --git a/hw/timer/sun4v-rtc.c b/hw/timer/sun4v-rtc.c
new file mode 100644
index 0000000..31052322
--- /dev/null
+++ b/hw/timer/sun4v-rtc.c
@@ -0,0 +1,102 @@
+/*
+ * QEMU sun4v Real Time Clock device
+ *
+ * The sun4v_rtc device (sun4v tod clock)
+ *
+ * Copyright (c) 2016 Artyom Tarasenko
+ *
+ * This code is licensed under the GNU GPL v3 or (at your option) any later
+ * version.
+ */
+
+#include "qemu/osdep.h"
+#include "hw/hw.h"
+#include "hw/sysbus.h"
+#include "qemu/timer.h"
+#include "hw/timer/sun4v-rtc.h"
+
+//#define DEBUG_SUN4V_RTC
+
+#ifdef DEBUG_SUN4V_RTC
+#define DPRINTF(fmt, ...) \
+ do { printf("sun4v_rtc: " fmt , ## __VA_ARGS__); } while (0)
+#else
+#define DPRINTF(fmt, ...) do {} while (0)
+#endif
+
+#define TYPE_SUN4V_RTC "sun4v_rtc"
+#define SUN4V_RTC(obj) OBJECT_CHECK(Sun4vRtc, (obj), TYPE_SUN4V_RTC)
+
+typedef struct Sun4vRtc {
+ SysBusDevice parent_obj;
+
+ MemoryRegion iomem;
+} Sun4vRtc;
+
+static uint64_t sun4v_rtc_read(void *opaque, hwaddr addr,
+ unsigned size)
+{
+ uint64_t val = get_clock_realtime() / NANOSECONDS_PER_SECOND;
+ if (!(addr & 4ULL)) {
+ /* accessing the high 32 bits */
+ val >>= 32;
+ }
+ DPRINTF("read from " TARGET_FMT_plx " val %lx\n", addr, val);
+ return val;
+}
+
+static void sun4v_rtc_write(void *opaque, hwaddr addr,
+ uint64_t val, unsigned size)
+{
+ DPRINTF("write 0x%x to " TARGET_FMT_plx "\n", (unsigned)val, addr);
+}
+
+static const MemoryRegionOps sun4v_rtc_ops = {
+ .read = sun4v_rtc_read,
+ .write = sun4v_rtc_write,
+ .endianness = DEVICE_NATIVE_ENDIAN,
+};
+
+void sun4v_rtc_init(hwaddr addr)
+{
+ DeviceState *dev;
+ SysBusDevice *s;
+
+ dev = qdev_create(NULL, TYPE_SUN4V_RTC);
+ s = SYS_BUS_DEVICE(dev);
+
+ qdev_init_nofail(dev);
+
+ sysbus_mmio_map(s, 0, addr);
+}
+
+static int sun4v_rtc_init1(SysBusDevice *dev)
+{
+ Sun4vRtc *s = SUN4V_RTC(dev);
+
+ memory_region_init_io(&s->iomem, OBJECT(s), &sun4v_rtc_ops, s,
+ "sun4v-rtc", 0x08ULL);
+ sysbus_init_mmio(dev, &s->iomem);
+ return 0;
+}
+
+static void sun4v_rtc_class_init(ObjectClass *klass, void *data)
+{
+ SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass);
+
+ k->init = sun4v_rtc_init1;
+}
+
+static const TypeInfo sun4v_rtc_info = {
+ .name = TYPE_SUN4V_RTC,
+ .parent = TYPE_SYS_BUS_DEVICE,
+ .instance_size = sizeof(Sun4vRtc),
+ .class_init = sun4v_rtc_class_init,
+};
+
+static void sun4v_rtc_register_types(void)
+{
+ type_register_static(&sun4v_rtc_info);
+}
+
+type_init(sun4v_rtc_register_types)
diff --git a/include/hw/sparc/sparc64.h b/include/hw/sparc/sparc64.h
new file mode 100644
index 0000000..7748939
--- /dev/null
+++ b/include/hw/sparc/sparc64.h
@@ -0,0 +1,5 @@
+
+SPARCCPU *sparc64_cpu_devinit(const char *cpu_model,
+ const char *dflt_cpu_model, uint64_t prom_addr);
+
+void sparc64_cpu_set_ivec_irq(void *opaque, int irq, int level);
diff --git a/include/hw/timer/sun4v-rtc.h b/include/hw/timer/sun4v-rtc.h
new file mode 100644
index 0000000..407278f
--- /dev/null
+++ b/include/hw/timer/sun4v-rtc.h
@@ -0,0 +1 @@
+void sun4v_rtc_init(hwaddr addr);
diff --git a/linux-user/main.c b/linux-user/main.c
index c1d5eb4..94a636f 100644
--- a/linux-user/main.c
+++ b/linux-user/main.c
@@ -1166,7 +1166,7 @@ void cpu_loop (CPUSPARCState *env)
/* XXX: check env->error_code */
info.si_code = TARGET_SEGV_MAPERR;
if (trapnr == TT_DFAULT)
- info._sifields._sigfault._addr = env->dmmuregs[4];
+ info._sifields._sigfault._addr = env->dmmu.mmuregs[4];
else
info._sifields._sigfault._addr = cpu_tsptr(env)->tpc;
queue_signal(env, info.si_signo, QEMU_SI_FAULT, &info);
diff --git a/qemu-doc.texi b/qemu-doc.texi
index 02cb39d..0b2746f 100644
--- a/qemu-doc.texi
+++ b/qemu-doc.texi
@@ -2138,7 +2138,17 @@ Use the executable @file{qemu-system-sparc64} to simulate a Sun4u
(UltraSPARC PC-like machine), Sun4v (T1 PC-like machine), or generic
Niagara (T1) machine. The Sun4u emulator is mostly complete, being
able to run Linux, NetBSD and OpenBSD in headless (-nographic) mode. The
-Sun4v and Niagara emulators are still a work in progress.
+Sun4v emulator is still a work in progress.
+
+The Niagara T1 emulator makes use of firmware and OS binaries supplied in the S10image/ directory
+of the OpenSPARC T1 project @url{http://download.oracle.com/technetwork/systems/opensparc/OpenSPARCT1_Arch.1.5.tar.bz2}
+and is able to boot the disk.s10hw2 Solaris image.
+@example
+qemu-system-sparc64 -M niagara -L /path-to/S10image/ \
+ -nographic -m 256 \
+ -drive if=pflash,readonly=on,file=/S10image/disk.s10hw2
+@end example
+
QEMU emulates the following peripherals:
@@ -2173,7 +2183,7 @@ Set OpenBIOS variables in NVRAM, for example:
qemu-system-sparc64 -prom-env 'auto-boot?=false'
@end example
-@item -M [sun4u|sun4v|Niagara]
+@item -M [sun4u|sun4v|niagara]
Set the emulated machine type. The default is sun4u.
diff --git a/target/sparc/asi.h b/target/sparc/asi.h
index c9a1849..d8d6284 100644
--- a/target/sparc/asi.h
+++ b/target/sparc/asi.h
@@ -211,6 +211,7 @@
#define ASI_AFSR 0x4c /* Async fault status register */
#define ASI_AFAR 0x4d /* Async fault address register */
#define ASI_EC_TAG_DATA 0x4e /* E-cache tag/valid ram diag acc */
+#define ASI_HYP_SCRATCHPAD 0x4f /* (4V) Hypervisor scratchpad */
#define ASI_IMMU 0x50 /* Insn-MMU main register space */
#define ASI_IMMU_TSB_8KB_PTR 0x51 /* Insn-MMU 8KB TSB pointer reg */
#define ASI_IMMU_TSB_64KB_PTR 0x52 /* Insn-MMU 64KB TSB pointer reg */
diff --git a/target/sparc/cpu.c b/target/sparc/cpu.c
index d6583f1..d606eb5 100644
--- a/target/sparc/cpu.c
+++ b/target/sparc/cpu.c
@@ -57,9 +57,13 @@ static void sparc_cpu_reset(CPUState *s)
env->psrps = 1;
#endif
#ifdef TARGET_SPARC64
- env->pstate = PS_PRIV|PS_RED|PS_PEF|PS_AG;
+ env->pstate = PS_PRIV | PS_RED | PS_PEF;
+ if (!cpu_has_hypervisor(env)) {
+ env->pstate |= PS_AG;
+ }
env->hpstate = cpu_has_hypervisor(env) ? HS_PRIV : 0;
env->tl = env->maxtl;
+ env->gl = 2;
cpu_tsptr(env)->tt = TT_POWER_ON_RESET;
env->lsu = 0;
#else
@@ -744,14 +748,17 @@ void sparc_cpu_dump_state(CPUState *cs, FILE *f, fprintf_function cpu_fprintf,
cpu_print_cc(f, cpu_fprintf, cpu_get_ccr(env) << PSR_CARRY_SHIFT);
cpu_fprintf(f, " xcc: ");
cpu_print_cc(f, cpu_fprintf, cpu_get_ccr(env) << (PSR_CARRY_SHIFT - 4));
- cpu_fprintf(f, ") asi: %02x tl: %d pil: %x\n", env->asi, env->tl,
- env->psrpil);
+ cpu_fprintf(f, ") asi: %02x tl: %d pil: %x gl: %d\n", env->asi, env->tl,
+ env->psrpil, env->gl);
+ cpu_fprintf(f, "tbr: " TARGET_FMT_lx " hpstate: " TARGET_FMT_lx " htba: "
+ TARGET_FMT_lx "\n", env->tbr, env->hpstate, env->htba);
cpu_fprintf(f, "cansave: %d canrestore: %d otherwin: %d wstate: %d "
"cleanwin: %d cwp: %d\n",
env->cansave, env->canrestore, env->otherwin, env->wstate,
env->cleanwin, env->nwindows - 1 - env->cwp);
cpu_fprintf(f, "fsr: " TARGET_FMT_lx " y: " TARGET_FMT_lx " fprs: "
TARGET_FMT_lx "\n", env->fsr, env->y, env->fprs);
+
#else
cpu_fprintf(f, "psr: %08x (icc: ", cpu_get_psr(env));
cpu_print_cc(f, cpu_fprintf, cpu_get_psr(env));
diff --git a/target/sparc/cpu.h b/target/sparc/cpu.h
index 601c018..95a36a4 100644
--- a/target/sparc/cpu.h
+++ b/target/sparc/cpu.h
@@ -68,6 +68,8 @@
#define TT_DATA_ACCESS 0x32
#define TT_UNALIGNED 0x34
#define TT_PRIV_ACT 0x37
+#define TT_INSN_REAL_TRANSLATION_MISS 0x3e
+#define TT_DATA_REAL_TRANSLATION_MISS 0x3f
#define TT_EXTINT 0x40
#define TT_IVEC 0x60
#define TT_TMISS 0x64
@@ -77,6 +79,7 @@
#define TT_FILL 0xc0
#define TT_WOTHER (1 << 5)
#define TT_TRAP 0x100
+#define TT_HTRAP 0x180
#endif
#define PSR_NEG_SHIFT 23
@@ -227,7 +230,7 @@ enum {
#if !defined(TARGET_SPARC64)
#define NB_MMU_MODES 3
#else
-#define NB_MMU_MODES 7
+#define NB_MMU_MODES 6
typedef struct trap_state {
uint64_t tpc;
uint64_t tnpc;
@@ -302,21 +305,42 @@ enum {
#define TTE_W_OK_BIT (1ULL << 1)
#define TTE_GLOBAL_BIT (1ULL << 0)
+#define TTE_NFO_BIT_UA2005 (1ULL << 62)
+#define TTE_USED_BIT_UA2005 (1ULL << 47)
+#define TTE_LOCKED_BIT_UA2005 (1ULL << 61)
+#define TTE_SIDEEFFECT_BIT_UA2005 (1ULL << 11)
+#define TTE_PRIV_BIT_UA2005 (1ULL << 8)
+#define TTE_W_OK_BIT_UA2005 (1ULL << 6)
+
#define TTE_IS_VALID(tte) ((tte) & TTE_VALID_BIT)
#define TTE_IS_NFO(tte) ((tte) & TTE_NFO_BIT)
#define TTE_IS_USED(tte) ((tte) & TTE_USED_BIT)
#define TTE_IS_LOCKED(tte) ((tte) & TTE_LOCKED_BIT)
#define TTE_IS_SIDEEFFECT(tte) ((tte) & TTE_SIDEEFFECT_BIT)
+#define TTE_IS_SIDEEFFECT_UA2005(tte) ((tte) & TTE_SIDEEFFECT_BIT_UA2005)
#define TTE_IS_PRIV(tte) ((tte) & TTE_PRIV_BIT)
#define TTE_IS_W_OK(tte) ((tte) & TTE_W_OK_BIT)
+
+#define TTE_IS_NFO_UA2005(tte) ((tte) & TTE_NFO_BIT_UA2005)
+#define TTE_IS_USED_UA2005(tte) ((tte) & TTE_USED_BIT_UA2005)
+#define TTE_IS_LOCKED_UA2005(tte) ((tte) & TTE_LOCKED_BIT_UA2005)
+#define TTE_IS_SIDEEFFECT_UA2005(tte) ((tte) & TTE_SIDEEFFECT_BIT_UA2005)
+#define TTE_IS_PRIV_UA2005(tte) ((tte) & TTE_PRIV_BIT_UA2005)
+#define TTE_IS_W_OK_UA2005(tte) ((tte) & TTE_W_OK_BIT_UA2005)
+
#define TTE_IS_GLOBAL(tte) ((tte) & TTE_GLOBAL_BIT)
#define TTE_SET_USED(tte) ((tte) |= TTE_USED_BIT)
#define TTE_SET_UNUSED(tte) ((tte) &= ~TTE_USED_BIT)
#define TTE_PGSIZE(tte) (((tte) >> 61) & 3ULL)
+#define TTE_PGSIZE_UA2005(tte) ((tte) & 7ULL)
#define TTE_PA(tte) ((tte) & 0x1ffffffe000ULL)
+/* UltraSPARC T1 specific */
+#define TLB_UST1_IS_REAL_BIT (1ULL << 9) /* Real translation entry */
+#define TLB_UST1_IS_SUN4V_BIT (1ULL << 10) /* sun4u/sun4v TTE format switch */
+
#define SFSR_NF_BIT (1ULL << 24) /* JPS1 NoFault */
#define SFSR_TM_BIT (1ULL << 15) /* JPS1 TLB Miss */
#define SFSR_FT_VA_IMMU_BIT (1ULL << 13) /* USIIi VA out of range (IMMU) */
@@ -360,6 +384,9 @@ enum {
#define CACHE_CTRL_FD (1 << 22) /* Flush Data cache (Write only) */
#define CACHE_CTRL_DS (1 << 23) /* Data cache snoop enable */
+#define CONVERT_BIT(X, SRC, DST) \
+ (SRC > DST ? (X) / (SRC / DST) & (DST) : ((X) & SRC) * (DST / SRC))
+
typedef struct SparcTLBEntry {
uint64_t tag;
uint64_t tte;
@@ -380,7 +407,24 @@ struct CPUTimer
typedef struct CPUTimer CPUTimer;
typedef struct CPUSPARCState CPUSPARCState;
-
+#if defined(TARGET_SPARC64)
+typedef union {
+ uint64_t mmuregs[16];
+ struct {
+ uint64_t tsb_tag_target;
+ uint64_t mmu_primary_context;
+ uint64_t mmu_secondary_context;
+ uint64_t sfsr;
+ uint64_t sfar;
+ uint64_t tsb;
+ uint64_t tag_access;
+ uint64_t virtual_watchpoint;
+ uint64_t physical_watchpoint;
+ uint64_t sun4v_ctx_config[2];
+ uint64_t sun4v_tsb_pointers[4];
+ };
+} SparcV9MMU;
+#endif
struct CPUSPARCState {
target_ulong gregs[8]; /* general registers */
target_ulong *regwptr; /* pointer to current register window */
@@ -433,31 +477,8 @@ struct CPUSPARCState {
uint64_t lsu;
#define DMMU_E 0x8
#define IMMU_E 0x4
- //typedef struct SparcMMU
- union {
- uint64_t immuregs[16];
- struct {
- uint64_t tsb_tag_target;
- uint64_t unused_mmu_primary_context; // use DMMU
- uint64_t unused_mmu_secondary_context; // use DMMU
- uint64_t sfsr;
- uint64_t sfar;
- uint64_t tsb;
- uint64_t tag_access;
- } immu;
- };
- union {
- uint64_t dmmuregs[16];
- struct {
- uint64_t tsb_tag_target;
- uint64_t mmu_primary_context;
- uint64_t mmu_secondary_context;
- uint64_t sfsr;
- uint64_t sfar;
- uint64_t tsb;
- uint64_t tag_access;
- } dmmu;
- };
+ SparcV9MMU immu;
+ SparcV9MMU dmmu;
SparcTLBEntry itlb[64];
SparcTLBEntry dtlb[64];
uint32_t mmu_version;
@@ -487,6 +508,7 @@ struct CPUSPARCState {
uint64_t bgregs[8]; /* backup for normal global registers */
uint64_t igregs[8]; /* interrupt general registers */
uint64_t mgregs[8]; /* mmu general registers */
+ uint64_t glregs[8 * MAXTL_MAX];
uint64_t fprs;
uint64_t tick_cmpr, stick_cmpr;
CPUTimer *tick, *stick;
@@ -496,6 +518,7 @@ struct CPUSPARCState {
uint32_t gl; // UA2005
/* UA 2005 hyperprivileged registers */
uint64_t hpstate, htstate[MAXTL_MAX], hintp, htba, hver, hstick_cmpr, ssr;
+ uint64_t scratch[8];
CPUTimer *hstick; // UA 2005
/* Interrupt vector registers */
uint64_t ivec_status;
@@ -586,6 +609,7 @@ void cpu_put_ccr(CPUSPARCState *env1, target_ulong val);
target_ulong cpu_get_cwp64(CPUSPARCState *env1);
void cpu_put_cwp64(CPUSPARCState *env1, int cwp);
void cpu_change_pstate(CPUSPARCState *env1, uint32_t new_pstate);
+void cpu_gl_switch_gregs(CPUSPARCState *env, uint32_t new_gl);
#endif
int cpu_cwp_inc(CPUSPARCState *env1, int cwp);
int cpu_cwp_dec(CPUSPARCState *env1, int cwp);
@@ -645,8 +669,7 @@ int cpu_sparc_signal_handler(int host_signum, void *pinfo, void *puc);
#define MMU_KERNEL_IDX 2
#define MMU_KERNEL_SECONDARY_IDX 3
#define MMU_NUCLEUS_IDX 4
-#define MMU_HYPV_IDX 5
-#define MMU_PHYS_IDX 6
+#define MMU_PHYS_IDX 5
#else
#define MMU_USER_IDX 0
#define MMU_KERNEL_IDX 1
@@ -668,6 +691,11 @@ static inline int cpu_supervisor_mode(CPUSPARCState *env1)
{
return env1->pstate & PS_PRIV;
}
+#else
+static inline int cpu_supervisor_mode(CPUSPARCState *env1)
+{
+ return env1->psrs;
+}
#endif
static inline int cpu_mmu_index(CPUSPARCState *env, bool ifetch)
@@ -686,10 +714,10 @@ static inline int cpu_mmu_index(CPUSPARCState *env, bool ifetch)
? (env->lsu & IMMU_E) == 0 || (env->pstate & PS_RED) != 0
: (env->lsu & DMMU_E) == 0) {
return MMU_PHYS_IDX;
+ } else if (cpu_hypervisor_mode(env)) {
+ return MMU_PHYS_IDX;
} else if (env->tl > 0) {
return MMU_NUCLEUS_IDX;
- } else if (cpu_hypervisor_mode(env)) {
- return MMU_HYPV_IDX;
} else if (cpu_supervisor_mode(env)) {
return MMU_KERNEL_IDX;
} else {
@@ -704,8 +732,9 @@ static inline int cpu_interrupts_enabled(CPUSPARCState *env1)
if (env1->psret != 0)
return 1;
#else
- if (env1->pstate & PS_IE)
+ if ((env1->pstate & PS_IE) && !cpu_hypervisor_mode(env1)) {
return 1;
+ }
#endif
return 0;
@@ -734,6 +763,8 @@ trap_state* cpu_tsptr(CPUSPARCState* env);
#define TB_FLAG_MMU_MASK 7
#define TB_FLAG_FPU_ENABLED (1 << 4)
#define TB_FLAG_AM_ENABLED (1 << 5)
+#define TB_FLAG_SUPER (1 << 6)
+#define TB_FLAG_HYPER (1 << 7)
#define TB_FLAG_ASI_SHIFT 24
static inline void cpu_get_tb_cpu_state(CPUSPARCState *env, target_ulong *pc,
@@ -743,7 +774,17 @@ static inline void cpu_get_tb_cpu_state(CPUSPARCState *env, target_ulong *pc,
*pc = env->pc;
*cs_base = env->npc;
flags = cpu_mmu_index(env, false);
+#ifndef CONFIG_USER_ONLY
+ if (cpu_supervisor_mode(env)) {
+ flags |= TB_FLAG_SUPER;
+ }
+#endif
#ifdef TARGET_SPARC64
+#ifndef CONFIG_USER_ONLY
+ if (cpu_hypervisor_mode(env)) {
+ flags |= TB_FLAG_HYPER;
+ }
+#endif
if (env->pstate & PS_AM) {
flags |= TB_FLAG_AM_ENABLED;
}
diff --git a/target/sparc/helper.h b/target/sparc/helper.h
index 3ef38b9..b8f1e78 100644
--- a/target/sparc/helper.h
+++ b/target/sparc/helper.h
@@ -5,6 +5,7 @@ DEF_HELPER_1(rdpsr, tl, env)
DEF_HELPER_1(power_down, void, env)
#else
DEF_HELPER_FLAGS_2(wrpil, TCG_CALL_NO_RWG, void, env, tl)
+DEF_HELPER_2(wrgl, void, env, tl)
DEF_HELPER_2(wrpstate, void, env, tl)
DEF_HELPER_1(done, void, env)
DEF_HELPER_1(retry, void, env)
diff --git a/target/sparc/int64_helper.c b/target/sparc/int64_helper.c
index 29360fa..605747c 100644
--- a/target/sparc/int64_helper.c
+++ b/target/sparc/int64_helper.c
@@ -78,8 +78,10 @@ void sparc_cpu_do_interrupt(CPUState *cs)
static int count;
const char *name;
- if (intno < 0 || intno >= 0x180) {
+ if (intno < 0 || intno >= 0x1ff) {
name = "Unknown";
+ } else if (intno >= 0x180) {
+ name = "Hyperprivileged Trap Instruction";
} else if (intno >= 0x100) {
name = "Trap Instruction";
} else if (intno >= 0xc0) {
@@ -135,16 +137,42 @@ void sparc_cpu_do_interrupt(CPUState *cs)
tsptr->tnpc = env->npc;
tsptr->tt = intno;
+ if (cpu_has_hypervisor(env)) {
+ env->htstate[env->tl] = env->hpstate;
+ /* XXX OpenSPARC T1 - UltraSPARC T3 have MAXPTL=2
+ but this may change in the future */
+ if (env->tl > 2) {
+ env->hpstate |= HS_PRIV;
+ }
+ }
+
+ if (env->def->features & CPU_FEATURE_GL) {
+ tsptr->tstate |= (env->gl & 7ULL) << 40;
+ cpu_gl_switch_gregs(env, env->gl + 1);
+ env->gl++;
+ }
+
switch (intno) {
case TT_IVEC:
- cpu_change_pstate(env, PS_PEF | PS_PRIV | PS_IG);
+ if (!cpu_has_hypervisor(env)) {
+ cpu_change_pstate(env, PS_PEF | PS_PRIV | PS_IG);
+ }
break;
case TT_TFAULT:
case TT_DFAULT:
case TT_TMISS ... TT_TMISS + 3:
case TT_DMISS ... TT_DMISS + 3:
case TT_DPROT ... TT_DPROT + 3:
- cpu_change_pstate(env, PS_PEF | PS_PRIV | PS_MG);
+ if (cpu_has_hypervisor(env)) {
+ env->hpstate |= HS_PRIV;
+ env->pstate = PS_PEF | PS_PRIV;
+ } else {
+ cpu_change_pstate(env, PS_PEF | PS_PRIV | PS_MG);
+ }
+ break;
+ case TT_INSN_REAL_TRANSLATION_MISS ... TT_DATA_REAL_TRANSLATION_MISS:
+ case TT_HTRAP ... TT_HTRAP + 127:
+ env->hpstate |= HS_PRIV;
break;
default:
cpu_change_pstate(env, PS_PEF | PS_PRIV | PS_AG);
@@ -158,8 +186,13 @@ void sparc_cpu_do_interrupt(CPUState *cs)
} else if ((intno & 0x1c0) == TT_FILL) {
cpu_set_cwp(env, cpu_cwp_inc(env, env->cwp + 1));
}
- env->pc = env->tbr & ~0x7fffULL;
- env->pc |= ((env->tl > 1) ? 1 << 14 : 0) | (intno << 5);
+
+ if (cpu_hypervisor_mode(env)) {
+ env->pc = (env->htba & ~0x3fffULL) | (intno << 5);
+ } else {
+ env->pc = env->tbr & ~0x7fffULL;
+ env->pc |= ((env->tl > 1) ? 1 << 14 : 0) | (intno << 5);
+ }
env->npc = env->pc + 4;
cs->exception_index = -1;
}
diff --git a/target/sparc/ldst_helper.c b/target/sparc/ldst_helper.c
index a0171f7..2c05d6a 100644
--- a/target/sparc/ldst_helper.c
+++ b/target/sparc/ldst_helper.c
@@ -70,44 +70,47 @@
#define QT1 (env->qt1)
#if defined(TARGET_SPARC64) && !defined(CONFIG_USER_ONLY)
-/* Calculates TSB pointer value for fault page size 8k or 64k */
-static uint64_t ultrasparc_tsb_pointer(uint64_t tsb_register,
- uint64_t tag_access_register,
- int page_size)
+/* Calculates TSB pointer value for fault page size
+ * UltraSPARC IIi has fixed sizes (8k or 64k) for the page pointers
+ * UA2005 holds the page size configuration in mmu_ctx registers */
+static uint64_t ultrasparc_tsb_pointer(CPUSPARCState *env,
+ const SparcV9MMU *mmu, const int idx)
{
- uint64_t tsb_base = tsb_register & ~0x1fffULL;
+ uint64_t tsb_register;
+ int page_size;
+ if (cpu_has_hypervisor(env)) {
+ int tsb_index = 0;
+ int ctx = mmu->tag_access & 0x1fffULL;
+ uint64_t ctx_register = mmu->sun4v_ctx_config[ctx ? 1 : 0];
+ tsb_index = idx;
+ tsb_index |= ctx ? 2 : 0;
+ page_size = idx ? ctx_register >> 8 : ctx_register;
+ page_size &= 7;
+ tsb_register = mmu->sun4v_tsb_pointers[tsb_index];
+ } else {
+ page_size = idx;
+ tsb_register = mmu->tsb;
+ }
int tsb_split = (tsb_register & 0x1000ULL) ? 1 : 0;
int tsb_size = tsb_register & 0xf;
- /* discard lower 13 bits which hold tag access context */
- uint64_t tag_access_va = tag_access_register & ~0x1fffULL;
-
- /* now reorder bits */
- uint64_t tsb_base_mask = ~0x1fffULL;
- uint64_t va = tag_access_va;
+ uint64_t tsb_base_mask = (~0x1fffULL) << tsb_size;
- /* move va bits to correct position */
- if (page_size == 8*1024) {
- va >>= 9;
- } else if (page_size == 64*1024) {
- va >>= 12;
- }
-
- if (tsb_size) {
- tsb_base_mask <<= tsb_size;
- }
+ /* move va bits to correct position,
+ * the context bits will be masked out later */
+ uint64_t va = mmu->tag_access >> (3 * page_size + 9);
/* calculate tsb_base mask and adjust va if split is in use */
if (tsb_split) {
- if (page_size == 8*1024) {
+ if (idx == 0) {
va &= ~(1ULL << (13 + tsb_size));
- } else if (page_size == 64*1024) {
+ } else {
va |= (1ULL << (13 + tsb_size));
}
tsb_base_mask <<= 1;
}
- return ((tsb_base & tsb_base_mask) | (va & ~tsb_base_mask)) & ~0xfULL;
+ return ((tsb_register & tsb_base_mask) | (va & ~tsb_base_mask)) & ~0xfULL;
}
/* Calculates tag target register value by reordering bits
@@ -127,9 +130,8 @@ static void replace_tlb_entry(SparcTLBEntry *tlb,
if (TTE_IS_VALID(tlb->tte)) {
CPUState *cs = CPU(sparc_env_get_cpu(env1));
- mask = 0xffffffffffffe000ULL;
- mask <<= 3 * ((tlb->tte >> 61) & 3);
- size = ~mask + 1;
+ size = 8192ULL << 3 * TTE_PGSIZE(tlb->tte);
+ mask = 1ULL + ~size;
va = tlb->tag & mask;
@@ -202,12 +204,56 @@ static void demap_tlb(SparcTLBEntry *tlb, target_ulong demap_addr,
}
}
+static uint64_t sun4v_tte_to_sun4u(CPUSPARCState *env, uint64_t tag,
+ uint64_t sun4v_tte)
+{
+ uint64_t sun4u_tte;
+ if (!(cpu_has_hypervisor(env) && (tag & TLB_UST1_IS_SUN4V_BIT))) {
+ /* is already in the sun4u format */
+ return sun4v_tte;
+ }
+ sun4u_tte = TTE_PA(sun4v_tte) | (sun4v_tte & TTE_VALID_BIT);
+ sun4u_tte |= (sun4v_tte & 3ULL) << 61; /* TTE_PGSIZE */
+ sun4u_tte |= CONVERT_BIT(sun4v_tte, TTE_NFO_BIT_UA2005, TTE_NFO_BIT);
+ sun4u_tte |= CONVERT_BIT(sun4v_tte, TTE_USED_BIT_UA2005, TTE_USED_BIT);
+ sun4u_tte |= CONVERT_BIT(sun4v_tte, TTE_W_OK_BIT_UA2005, TTE_W_OK_BIT);
+ sun4u_tte |= CONVERT_BIT(sun4v_tte, TTE_SIDEEFFECT_BIT_UA2005,
+ TTE_SIDEEFFECT_BIT);
+ sun4u_tte |= CONVERT_BIT(sun4v_tte, TTE_PRIV_BIT_UA2005, TTE_PRIV_BIT);
+ sun4u_tte |= CONVERT_BIT(sun4v_tte, TTE_LOCKED_BIT_UA2005, TTE_LOCKED_BIT);
+ return sun4u_tte;
+}
+
static void replace_tlb_1bit_lru(SparcTLBEntry *tlb,
uint64_t tlb_tag, uint64_t tlb_tte,
- const char *strmmu, CPUSPARCState *env1)
+ const char *strmmu, CPUSPARCState *env1,
+ uint64_t addr)
{
unsigned int i, replace_used;
+ tlb_tte = sun4v_tte_to_sun4u(env1, addr, tlb_tte);
+ if (cpu_has_hypervisor(env1)) {
+ uint64_t new_vaddr = tlb_tag & ~0x1fffULL;
+ uint64_t new_size = 8192ULL << 3 * TTE_PGSIZE(tlb_tte);
+ uint32_t new_ctx = tlb_tag & 0x1fffU;
+ for (i = 0; i < 64; i++) {
+ uint32_t ctx = tlb[i].tag & 0x1fffU;
+ /* check if new mapping overlaps an existing one */
+ if (new_ctx == ctx) {
+ uint64_t vaddr = tlb[i].tag & ~0x1fffULL;
+ uint64_t size = 8192ULL << 3 * TTE_PGSIZE(tlb[i].tte);
+ if (new_vaddr == vaddr
+ || (new_vaddr < vaddr + size
+ && vaddr < new_vaddr + new_size)) {
+ DPRINTF_MMU("auto demap entry [%d] %lx->%lx\n", i, vaddr,
+ new_vaddr);
+ replace_tlb_entry(&tlb[i], tlb_tag, tlb_tte, env1);
+ return;
+ }
+ }
+
+ }
+ }
/* Try replacing invalid entry */
for (i = 0; i < 64; i++) {
if (!TTE_IS_VALID(tlb[i].tte)) {
@@ -247,9 +293,11 @@ static void replace_tlb_1bit_lru(SparcTLBEntry *tlb,
}
#ifdef DEBUG_MMU
- DPRINTF_MMU("%s lru replacement failed: no entries available\n", strmmu);
+ DPRINTF_MMU("%s lru replacement: no free entries available, "
+ "replacing the last one\n", strmmu);
#endif
- /* error state? */
+ /* corner case: the last entry is replaced anyway */
+ replace_tlb_entry(&tlb[63], tlb_tag, tlb_tte, env1);
}
#endif
@@ -294,6 +342,22 @@ static inline target_ulong asi_address_mask(CPUSPARCState *env,
}
return addr;
}
+
+#ifndef CONFIG_USER_ONLY
+static inline void do_check_asi(CPUSPARCState *env, int asi, uintptr_t ra)
+{
+ /* ASIs >= 0x80 are user mode.
+ * ASIs >= 0x30 are hyper mode (or super if hyper is not available).
+ * ASIs <= 0x2f are super mode.
+ */
+ if (asi < 0x80
+ && !cpu_hypervisor_mode(env)
+ && (!cpu_supervisor_mode(env)
+ || (asi >= 0x30 && cpu_has_hypervisor(env)))) {
+ cpu_raise_exception_ra(env, TT_PRIV_ACT, ra);
+ }
+}
+#endif /* !CONFIG_USER_ONLY */
#endif
static void do_check_align(CPUSPARCState *env, target_ulong addr,
@@ -1119,13 +1183,7 @@ uint64_t helper_ld_asi(CPUSPARCState *env, target_ulong addr,
asi &= 0xff;
- if ((asi < 0x80 && (env->pstate & PS_PRIV) == 0)
- || (cpu_has_hypervisor(env)
- && asi >= 0x30 && asi < 0x80
- && !(env->hpstate & HS_PRIV))) {
- cpu_raise_exception_ra(env, TT_PRIV_ACT, GETPC());
- }
-
+ do_check_asi(env, asi, GETPC());
do_check_align(env, addr, size - 1, GETPC());
addr = asi_address_mask(env, asi, addr);
@@ -1220,30 +1278,39 @@ uint64_t helper_ld_asi(CPUSPARCState *env, target_ulong addr,
case ASI_IMMU: /* I-MMU regs */
{
int reg = (addr >> 3) & 0xf;
-
- if (reg == 0) {
- /* I-TSB Tag Target register */
+ switch (reg) {
+ case 0:
+ /* 0x00 I-TSB Tag Target register */
ret = ultrasparc_tag_target(env->immu.tag_access);
- } else {
- ret = env->immuregs[reg];
+ break;
+ case 3: /* SFSR */
+ ret = env->immu.sfsr;
+ break;
+ case 5: /* TSB access */
+ ret = env->immu.tsb;
+ break;
+ case 6:
+ /* 0x30 I-TSB Tag Access register */
+ ret = env->immu.tag_access;
+ break;
+ default:
+ cpu_unassigned_access(cs, addr, false, false, 1, size);
+ ret = 0;
}
-
break;
}
case ASI_IMMU_TSB_8KB_PTR: /* I-MMU 8k TSB pointer */
{
/* env->immuregs[5] holds I-MMU TSB register value
env->immuregs[6] holds I-MMU Tag Access register value */
- ret = ultrasparc_tsb_pointer(env->immu.tsb, env->immu.tag_access,
- 8*1024);
+ ret = ultrasparc_tsb_pointer(env, &env->immu, 0);
break;
}
case ASI_IMMU_TSB_64KB_PTR: /* I-MMU 64k TSB pointer */
{
/* env->immuregs[5] holds I-MMU TSB register value
env->immuregs[6] holds I-MMU Tag Access register value */
- ret = ultrasparc_tsb_pointer(env->immu.tsb, env->immu.tag_access,
- 64*1024);
+ ret = ultrasparc_tsb_pointer(env, &env->immu, 1);
break;
}
case ASI_ITLB_DATA_ACCESS: /* I-MMU data access */
@@ -1263,12 +1330,38 @@ uint64_t helper_ld_asi(CPUSPARCState *env, target_ulong addr,
case ASI_DMMU: /* D-MMU regs */
{
int reg = (addr >> 3) & 0xf;
-
- if (reg == 0) {
- /* D-TSB Tag Target register */
+ switch (reg) {
+ case 0:
+ /* 0x00 D-TSB Tag Target register */
ret = ultrasparc_tag_target(env->dmmu.tag_access);
- } else {
- ret = env->dmmuregs[reg];
+ break;
+ case 1: /* 0x08 Primary Context */
+ ret = env->dmmu.mmu_primary_context;
+ break;
+ case 2: /* 0x10 Secondary Context */
+ ret = env->dmmu.mmu_secondary_context;
+ break;
+ case 3: /* SFSR */
+ ret = env->dmmu.sfsr;
+ break;
+ case 4: /* 0x20 SFAR */
+ ret = env->dmmu.sfar;
+ break;
+ case 5: /* 0x28 TSB access */
+ ret = env->dmmu.tsb;
+ break;
+ case 6: /* 0x30 D-TSB Tag Access register */
+ ret = env->dmmu.tag_access;
+ break;
+ case 7:
+ ret = env->dmmu.virtual_watchpoint;
+ break;
+ case 8:
+ ret = env->dmmu.physical_watchpoint;
+ break;
+ default:
+ cpu_unassigned_access(cs, addr, false, false, 1, size);
+ ret = 0;
}
break;
}
@@ -1276,16 +1369,14 @@ uint64_t helper_ld_asi(CPUSPARCState *env, target_ulong addr,
{
/* env->dmmuregs[5] holds D-MMU TSB register value
env->dmmuregs[6] holds D-MMU Tag Access register value */
- ret = ultrasparc_tsb_pointer(env->dmmu.tsb, env->dmmu.tag_access,
- 8*1024);
+ ret = ultrasparc_tsb_pointer(env, &env->dmmu, 0);
break;
}
case ASI_DMMU_TSB_64KB_PTR: /* D-MMU 64k TSB pointer */
{
/* env->dmmuregs[5] holds D-MMU TSB register value
env->dmmuregs[6] holds D-MMU Tag Access register value */
- ret = ultrasparc_tsb_pointer(env->dmmu.tsb, env->dmmu.tag_access,
- 64*1024);
+ ret = ultrasparc_tsb_pointer(env, &env->dmmu, 1);
break;
}
case ASI_DTLB_DATA_ACCESS: /* D-MMU data access */
@@ -1315,6 +1406,30 @@ uint64_t helper_ld_asi(CPUSPARCState *env, target_ulong addr,
}
break;
}
+ case ASI_SCRATCHPAD: /* UA2005 privileged scratchpad */
+ if (unlikely((addr >= 0x20) && (addr < 0x30))) {
+ /* Hyperprivileged access only */
+ cpu_unassigned_access(cs, addr, false, false, 1, size);
+ }
+ /* fall through */
+ case ASI_HYP_SCRATCHPAD: /* UA2005 hyperprivileged scratchpad */
+ {
+ unsigned int i = (addr >> 3) & 0x7;
+ ret = env->scratch[i];
+ break;
+ }
+ case ASI_MMU: /* UA2005 Context ID registers */
+ switch ((addr >> 3) & 0x3) {
+ case 1:
+ ret = env->dmmu.mmu_primary_context;
+ break;
+ case 2:
+ ret = env->dmmu.mmu_secondary_context;
+ break;
+ default:
+ cpu_unassigned_access(cs, addr, true, false, 1, size);
+ }
+ break;
case ASI_DCACHE_DATA: /* D-cache data */
case ASI_DCACHE_TAG: /* D-cache tag access */
case ASI_ESTATE_ERROR_EN: /* E-cache error enable */
@@ -1375,13 +1490,7 @@ void helper_st_asi(CPUSPARCState *env, target_ulong addr, target_ulong val,
asi &= 0xff;
- if ((asi < 0x80 && (env->pstate & PS_PRIV) == 0)
- || (cpu_has_hypervisor(env)
- && asi >= 0x30 && asi < 0x80
- && !(env->hpstate & HS_PRIV))) {
- cpu_raise_exception_ra(env, TT_PRIV_ACT, GETPC());
- }
-
+ do_check_asi(env, asi, GETPC());
do_check_align(env, addr, size - 1, GETPC());
addr = asi_address_mask(env, asi, addr);
@@ -1417,7 +1526,67 @@ void helper_st_asi(CPUSPARCState *env, target_ulong addr, target_ulong val,
case ASI_TWINX_SL: /* Secondary, twinx, LE */
/* These are always handled inline. */
g_assert_not_reached();
-
+ /* these ASIs have different functions on UltraSPARC-IIIi
+ * and UA2005 CPUs. Use the explicit numbers to avoid confusion
+ */
+ case 0x31:
+ case 0x32:
+ case 0x39:
+ case 0x3a:
+ if (cpu_has_hypervisor(env)) {
+ /* UA2005
+ * ASI_DMMU_CTX_ZERO_TSB_BASE_PS0
+ * ASI_DMMU_CTX_ZERO_TSB_BASE_PS1
+ * ASI_DMMU_CTX_NONZERO_TSB_BASE_PS0
+ * ASI_DMMU_CTX_NONZERO_TSB_BASE_PS1
+ */
+ int idx = ((asi & 2) >> 1) | ((asi & 8) >> 2);
+ env->dmmu.sun4v_tsb_pointers[idx] = val;
+ } else {
+ helper_raise_exception(env, TT_ILL_INSN);
+ }
+ break;
+ case 0x33:
+ case 0x3b:
+ if (cpu_has_hypervisor(env)) {
+ /* UA2005
+ * ASI_DMMU_CTX_ZERO_CONFIG
+ * ASI_DMMU_CTX_NONZERO_CONFIG
+ */
+ env->dmmu.sun4v_ctx_config[(asi & 8) >> 3] = val;
+ } else {
+ helper_raise_exception(env, TT_ILL_INSN);
+ }
+ break;
+ case 0x35:
+ case 0x36:
+ case 0x3d:
+ case 0x3e:
+ if (cpu_has_hypervisor(env)) {
+ /* UA2005
+ * ASI_IMMU_CTX_ZERO_TSB_BASE_PS0
+ * ASI_IMMU_CTX_ZERO_TSB_BASE_PS1
+ * ASI_IMMU_CTX_NONZERO_TSB_BASE_PS0
+ * ASI_IMMU_CTX_NONZERO_TSB_BASE_PS1
+ */
+ int idx = ((asi & 2) >> 1) | ((asi & 8) >> 2);
+ env->immu.sun4v_tsb_pointers[idx] = val;
+ } else {
+ helper_raise_exception(env, TT_ILL_INSN);
+ }
+ break;
+ case 0x37:
+ case 0x3f:
+ if (cpu_has_hypervisor(env)) {
+ /* UA2005
+ * ASI_IMMU_CTX_ZERO_CONFIG
+ * ASI_IMMU_CTX_NONZERO_CONFIG
+ */
+ env->immu.sun4v_ctx_config[(asi & 8) >> 3] = val;
+ } else {
+ helper_raise_exception(env, TT_ILL_INSN);
+ }
+ break;
case ASI_UPA_CONFIG: /* UPA config */
/* XXX */
return;
@@ -1429,7 +1598,7 @@ void helper_st_asi(CPUSPARCState *env, target_ulong addr, target_ulong val,
int reg = (addr >> 3) & 0xf;
uint64_t oldreg;
- oldreg = env->immuregs[reg];
+ oldreg = env->immu.mmuregs[reg];
switch (reg) {
case 0: /* RO */
return;
@@ -1456,10 +1625,11 @@ void helper_st_asi(CPUSPARCState *env, target_ulong addr, target_ulong val,
case 8:
return;
default:
+ cpu_unassigned_access(cs, addr, true, false, 1, size);
break;
}
- if (oldreg != env->immuregs[reg]) {
+ if (oldreg != env->immu.mmuregs[reg]) {
DPRINTF_MMU("immu change reg[%d]: 0x%016" PRIx64 " -> 0x%016"
PRIx64 "\n", reg, oldreg, env->immuregs[reg]);
}
@@ -1469,7 +1639,11 @@ void helper_st_asi(CPUSPARCState *env, target_ulong addr, target_ulong val,
return;
}
case ASI_ITLB_DATA_IN: /* I-MMU data in */
- replace_tlb_1bit_lru(env->itlb, env->immu.tag_access, val, "immu", env);
+ /* ignore real translation entries */
+ if (!(addr & TLB_UST1_IS_REAL_BIT)) {
+ replace_tlb_1bit_lru(env->itlb, env->immu.tag_access,
+ val, "immu", env, addr);
+ }
return;
case ASI_ITLB_DATA_ACCESS: /* I-MMU data access */
{
@@ -1477,8 +1651,11 @@ void helper_st_asi(CPUSPARCState *env, target_ulong addr, target_ulong val,
unsigned int i = (addr >> 3) & 0x3f;
- replace_tlb_entry(&env->itlb[i], env->immu.tag_access, val, env);
-
+ /* ignore real translation entries */
+ if (!(addr & TLB_UST1_IS_REAL_BIT)) {
+ replace_tlb_entry(&env->itlb[i], env->immu.tag_access,
+ sun4v_tte_to_sun4u(env, addr, val), env);
+ }
#ifdef DEBUG_MMU
DPRINTF_MMU("immu data access replaced entry [%i]\n", i);
dump_mmu(stdout, fprintf, env);
@@ -1493,7 +1670,7 @@ void helper_st_asi(CPUSPARCState *env, target_ulong addr, target_ulong val,
int reg = (addr >> 3) & 0xf;
uint64_t oldreg;
- oldreg = env->dmmuregs[reg];
+ oldreg = env->dmmu.mmuregs[reg];
switch (reg) {
case 0: /* RO */
case 4:
@@ -1526,13 +1703,17 @@ void helper_st_asi(CPUSPARCState *env, target_ulong addr, target_ulong val,
env->dmmu.tag_access = val;
break;
case 7: /* Virtual Watchpoint */
+ env->dmmu.virtual_watchpoint = val;
+ break;
case 8: /* Physical Watchpoint */
+ env->dmmu.physical_watchpoint = val;
+ break;
default:
- env->dmmuregs[reg] = val;
+ cpu_unassigned_access(cs, addr, true, false, 1, size);
break;
}
- if (oldreg != env->dmmuregs[reg]) {
+ if (oldreg != env->dmmu.mmuregs[reg]) {
DPRINTF_MMU("dmmu change reg[%d]: 0x%016" PRIx64 " -> 0x%016"
PRIx64 "\n", reg, oldreg, env->dmmuregs[reg]);
}
@@ -1542,14 +1723,21 @@ void helper_st_asi(CPUSPARCState *env, target_ulong addr, target_ulong val,
return;
}
case ASI_DTLB_DATA_IN: /* D-MMU data in */
- replace_tlb_1bit_lru(env->dtlb, env->dmmu.tag_access, val, "dmmu", env);
- return;
+ /* ignore real translation entries */
+ if (!(addr & TLB_UST1_IS_REAL_BIT)) {
+ replace_tlb_1bit_lru(env->dtlb, env->dmmu.tag_access,
+ val, "dmmu", env, addr);
+ }
+ return;
case ASI_DTLB_DATA_ACCESS: /* D-MMU data access */
{
unsigned int i = (addr >> 3) & 0x3f;
- replace_tlb_entry(&env->dtlb[i], env->dmmu.tag_access, val, env);
-
+ /* ignore real translation entries */
+ if (!(addr & TLB_UST1_IS_REAL_BIT)) {
+ replace_tlb_entry(&env->dtlb[i], env->dmmu.tag_access,
+ sun4v_tte_to_sun4u(env, addr, val), env);
+ }
#ifdef DEBUG_MMU
DPRINTF_MMU("dmmu data access replaced entry [%i]\n", i);
dump_mmu(stdout, fprintf, env);
@@ -1562,6 +1750,38 @@ void helper_st_asi(CPUSPARCState *env, target_ulong addr, target_ulong val,
case ASI_INTR_RECEIVE: /* Interrupt data receive */
env->ivec_status = val & 0x20;
return;
+ case ASI_SCRATCHPAD: /* UA2005 privileged scratchpad */
+ if (unlikely((addr >= 0x20) && (addr < 0x30))) {
+ /* Hyperprivileged access only */
+ cpu_unassigned_access(cs, addr, true, false, 1, size);
+ }
+ /* fall through */
+ case ASI_HYP_SCRATCHPAD: /* UA2005 hyperprivileged scratchpad */
+ {
+ unsigned int i = (addr >> 3) & 0x7;
+ env->scratch[i] = val;
+ return;
+ }
+ case ASI_MMU: /* UA2005 Context ID registers */
+ {
+ switch ((addr >> 3) & 0x3) {
+ case 1:
+ env->dmmu.mmu_primary_context = val;
+ env->immu.mmu_primary_context = val;
+ tlb_flush_by_mmuidx(CPU(cpu), MMU_USER_IDX, MMU_KERNEL_IDX, -1);
+ break;
+ case 2:
+ env->dmmu.mmu_secondary_context = val;
+ env->immu.mmu_secondary_context = val;
+ tlb_flush_by_mmuidx(CPU(cpu), MMU_USER_SECONDARY_IDX,
+ MMU_KERNEL_SECONDARY_IDX, -1);
+ break;
+ default:
+ cpu_unassigned_access(cs, addr, true, false, 1, size);
+ }
+ }
+ return;
+ case ASI_QUEUE: /* UA2005 CPU mondo queue */
case ASI_DCACHE_DATA: /* D-cache data */
case ASI_DCACHE_TAG: /* D-cache tag access */
case ASI_ESTATE_ERROR_EN: /* E-cache error enable */
@@ -1664,14 +1884,25 @@ void sparc_cpu_unassigned_access(CPUState *cs, hwaddr addr,
{
SPARCCPU *cpu = SPARC_CPU(cs);
CPUSPARCState *env = &cpu->env;
- int tt = is_exec ? TT_CODE_ACCESS : TT_DATA_ACCESS;
#ifdef DEBUG_UNASSIGNED
printf("Unassigned mem access to " TARGET_FMT_plx " from " TARGET_FMT_lx
"\n", addr, env->pc);
#endif
- cpu_raise_exception_ra(env, tt, GETPC());
+ if (is_exec) { /* XXX has_hypervisor */
+ if (env->lsu & (IMMU_E)) {
+ cpu_raise_exception_ra(env, TT_CODE_ACCESS, GETPC());
+ } else if (cpu_has_hypervisor(env) && !(env->hpstate & HS_PRIV)) {
+ cpu_raise_exception_ra(env, TT_INSN_REAL_TRANSLATION_MISS, GETPC());
+ }
+ } else {
+ if (env->lsu & (DMMU_E)) {
+ cpu_raise_exception_ra(env, TT_DATA_ACCESS, GETPC());
+ } else if (cpu_has_hypervisor(env) && !(env->hpstate & HS_PRIV)) {
+ cpu_raise_exception_ra(env, TT_DATA_REAL_TRANSLATION_MISS, GETPC());
+ }
+ }
}
#endif
#endif
diff --git a/target/sparc/machine.c b/target/sparc/machine.c
index aea6397..39e262c 100644
--- a/target/sparc/machine.c
+++ b/target/sparc/machine.c
@@ -148,8 +148,8 @@ const VMStateDescription vmstate_sparc_cpu = {
VMSTATE_UINT64_ARRAY(env.mmubpregs, SPARCCPU, 4),
#else
VMSTATE_UINT64(env.lsu, SPARCCPU),
- VMSTATE_UINT64_ARRAY(env.immuregs, SPARCCPU, 16),
- VMSTATE_UINT64_ARRAY(env.dmmuregs, SPARCCPU, 16),
+ VMSTATE_UINT64_ARRAY(env.immu.mmuregs, SPARCCPU, 16),
+ VMSTATE_UINT64_ARRAY(env.dmmu.mmuregs, SPARCCPU, 16),
VMSTATE_STRUCT_ARRAY(env.itlb, SPARCCPU, 64, 0,
vmstate_tlb_entry, SparcTLBEntry),
VMSTATE_STRUCT_ARRAY(env.dtlb, SPARCCPU, 64, 0,
diff --git a/target/sparc/mmu_helper.c b/target/sparc/mmu_helper.c
index 044e88c..8b4664d 100644
--- a/target/sparc/mmu_helper.c
+++ b/target/sparc/mmu_helper.c
@@ -456,23 +456,7 @@ static inline int ultrasparc_tag_match(SparcTLBEntry *tlb,
uint64_t address, uint64_t context,
hwaddr *physical)
{
- uint64_t mask;
-
- switch (TTE_PGSIZE(tlb->tte)) {
- default:
- case 0x0: /* 8k */
- mask = 0xffffffffffffe000ULL;
- break;
- case 0x1: /* 64k */
- mask = 0xffffffffffff0000ULL;
- break;
- case 0x2: /* 512k */
- mask = 0xfffffffffff80000ULL;
- break;
- case 0x3: /* 4M */
- mask = 0xffffffffffc00000ULL;
- break;
- }
+ uint64_t mask = -(8192ULL << 3 * TTE_PGSIZE(tlb->tte));
/* valid, context match, virtual address match? */
if (TTE_IS_VALID(tlb->tte) &&
@@ -757,6 +741,8 @@ void dump_mmu(FILE *f, fprintf_function cpu_fprintf, CPUSPARCState *env)
PRId64 "\n",
env->dmmu.mmu_primary_context,
env->dmmu.mmu_secondary_context);
+ (*cpu_fprintf)(f, "DMMU Tag Access: %" PRIx64 ", TSB Tag Target: %" PRIx64
+ "\n", env->dmmu.tag_access, env->dmmu.tsb_tag_target);
if ((env->lsu & DMMU_E) == 0) {
(*cpu_fprintf)(f, "DMMU disabled\n");
} else {
diff --git a/target/sparc/translate.c b/target/sparc/translate.c
index ead585e..655060c 100644
--- a/target/sparc/translate.c
+++ b/target/sparc/translate.c
@@ -72,9 +72,16 @@ typedef struct DisasContext {
target_ulong jump_pc[2]; /* used when JUMP_PC pc value is used */
int is_br;
int mem_idx;
- int fpu_enabled;
- int address_mask_32bit;
- int singlestep;
+ bool fpu_enabled;
+ bool address_mask_32bit;
+ bool singlestep;
+#ifndef CONFIG_USER_ONLY
+ bool supervisor;
+#ifdef TARGET_SPARC64
+ bool hypervisor;
+#endif
+#endif
+
uint32_t cc_op; /* current CC operation */
struct TranslationBlock *tb;
sparc_def_t *def;
@@ -283,10 +290,11 @@ static void gen_move_Q(DisasContext *dc, unsigned int rd, unsigned int rs)
#define hypervisor(dc) 0
#endif
#else
-#define supervisor(dc) (dc->mem_idx >= MMU_KERNEL_IDX)
#ifdef TARGET_SPARC64
-#define hypervisor(dc) (dc->mem_idx == MMU_HYPV_IDX)
+#define hypervisor(dc) (dc->hypervisor)
+#define supervisor(dc) (dc->supervisor | dc->hypervisor)
#else
+#define supervisor(dc) (dc->supervisor)
#endif
#endif
@@ -2134,7 +2142,11 @@ static DisasASI get_asi(DisasContext *dc, int insn, TCGMemOp memop)
case ASI_TWINX_NL:
case ASI_NUCLEUS_QUAD_LDD:
case ASI_NUCLEUS_QUAD_LDD_L:
- mem_idx = MMU_NUCLEUS_IDX;
+ if (hypervisor(dc)) {
+ mem_idx = MMU_PHYS_IDX;
+ } else {
+ mem_idx = MMU_NUCLEUS_IDX;
+ }
break;
case ASI_AIUP: /* As if user primary */
case ASI_AIUPL: /* As if user primary LE */
@@ -2309,8 +2321,19 @@ static void gen_st_asi(DisasContext *dc, TCGv src, TCGv addr,
case GET_ASI_EXCP:
break;
case GET_ASI_DTWINX: /* Reserved for stda. */
+#ifndef TARGET_SPARC64
gen_exception(dc, TT_ILL_INSN);
break;
+#else
+ if (!(dc->def->features & CPU_FEATURE_HYPV)) {
+ /* Pre OpenSPARC CPUs don't have these */
+ gen_exception(dc, TT_ILL_INSN);
+ return;
+ }
+ /* in OpenSPARC T1+ CPUs TWINX ASIs in store instructions
+ * are ST_BLKINIT_ ASIs */
+ /* fall through */
+#endif
case GET_ASI_DIRECT:
gen_address_mask(dc, addr);
tcg_gen_qemu_st_tl(src, addr, da.mem_idx, da.memop);
@@ -3286,7 +3309,7 @@ static void disas_sparc_insn(DisasContext * dc, unsigned int insn)
rs1 = GET_FIELD_SP(insn, 14, 18);
if (IS_IMM) {
- rs2 = GET_FIELD_SP(insn, 0, 6);
+ rs2 = GET_FIELD_SP(insn, 0, 7);
if (rs1 == 0) {
tcg_gen_movi_i32(trap, (rs2 & mask) + TT_TRAP);
/* Signal that the trap value is fully constant. */
@@ -3421,6 +3444,17 @@ static void disas_sparc_insn(DisasContext * dc, unsigned int insn)
case 0x19: /* System tick compare */
gen_store_gpr(dc, rd, cpu_stick_cmpr);
break;
+ case 0x1a: /* UltraSPARC-T1 Strand status */
+ /* XXX HYPV check maybe not enough, UA2005 & UA2007 describe
+ * this ASR as impl. dep
+ */
+ CHECK_IU_FEATURE(dc, HYPV);
+ {
+ TCGv t = gen_dest_gpr(dc, rd);
+ tcg_gen_movi_tl(t, 1UL);
+ gen_store_gpr(dc, rd, t);
+ }
+ break;
case 0x10: /* Performance Control */
case 0x11: /* Performance Instrumentation Counter */
case 0x12: /* Dispatch Control */
@@ -3445,7 +3479,8 @@ static void disas_sparc_insn(DisasContext * dc, unsigned int insn)
rs1 = GET_FIELD(insn, 13, 17);
switch (rs1) {
case 0: // hpstate
- // gen_op_rdhpstate();
+ tcg_gen_ld_i64(cpu_dst, cpu_env,
+ offsetof(CPUSPARCState, hpstate));
break;
case 1: // htstate
// gen_op_rdhtstate();
@@ -4535,8 +4570,7 @@ static void disas_sparc_insn(DisasContext * dc, unsigned int insn)
break;
case 16: // UA2005 gl
CHECK_IU_FEATURE(dc, GL);
- tcg_gen_st32_tl(cpu_tmp0, cpu_env,
- offsetof(CPUSPARCState, gl));
+ gen_helper_wrgl(cpu_env, cpu_tmp0);
break;
case 26: // UA2005 strand status
CHECK_IU_FEATURE(dc, HYPV);
@@ -4570,7 +4604,9 @@ static void disas_sparc_insn(DisasContext * dc, unsigned int insn)
tcg_gen_xor_tl(cpu_tmp0, cpu_src1, cpu_src2);
switch (rd) {
case 0: // hpstate
- // XXX gen_op_wrhpstate();
+ tcg_gen_st_i64(cpu_tmp0, cpu_env,
+ offsetof(CPUSPARCState,
+ hpstate));
save_state(dc);
gen_op_next_insn();
tcg_gen_exit_tb(0);
@@ -5710,9 +5746,15 @@ void gen_intermediate_code(CPUSPARCState * env, TranslationBlock * tb)
dc->fpu_enabled = tb_fpu_enabled(tb->flags);
dc->address_mask_32bit = tb_am_enabled(tb->flags);
dc->singlestep = (cs->singlestep_enabled || singlestep);
+#ifndef CONFIG_USER_ONLY
+ dc->supervisor = (tb->flags & TB_FLAG_SUPER) != 0;
+#endif
#ifdef TARGET_SPARC64
dc->fprs_dirty = 0;
dc->asi = (tb->flags >> TB_FLAG_ASI_SHIFT) & 0xff;
+#ifndef CONFIG_USER_ONLY
+ dc->hypervisor = (tb->flags & TB_FLAG_HYPER) != 0;
+#endif
#endif
num_insns = 0;
diff --git a/target/sparc/win_helper.c b/target/sparc/win_helper.c
index 2d5b546..71b3dd3 100644
--- a/target/sparc/win_helper.c
+++ b/target/sparc/win_helper.c
@@ -290,6 +290,10 @@ void helper_wrcwp(CPUSPARCState *env, target_ulong new_cwp)
static inline uint64_t *get_gregset(CPUSPARCState *env, uint32_t pstate)
{
+ if (env->def->features & CPU_FEATURE_GL) {
+ return env->glregs + (env->gl & 7) * 8;
+ }
+
switch (pstate) {
default:
trace_win_helper_gregset_error(pstate);
@@ -305,14 +309,40 @@ static inline uint64_t *get_gregset(CPUSPARCState *env, uint32_t pstate)
}
}
+static inline uint64_t *get_gl_gregset(CPUSPARCState *env, uint32_t gl)
+{
+ return env->glregs + (gl & 7) * 8;
+}
+
+/* Switch global register bank */
+void cpu_gl_switch_gregs(CPUSPARCState *env, uint32_t new_gl)
+{
+ uint64_t *src, *dst;
+ src = get_gl_gregset(env, new_gl);
+ dst = get_gl_gregset(env, env->gl);
+
+ if (src != dst) {
+ memcpy32(dst, env->gregs);
+ memcpy32(env->gregs, src);
+ }
+}
+
+void helper_wrgl(CPUSPARCState *env, target_ulong new_gl)
+{
+ cpu_gl_switch_gregs(env, new_gl & 7);
+ env->gl = new_gl & 7;
+}
+
void cpu_change_pstate(CPUSPARCState *env, uint32_t new_pstate)
{
uint32_t pstate_regs, new_pstate_regs;
uint64_t *src, *dst;
if (env->def->features & CPU_FEATURE_GL) {
- /* PS_AG is not implemented in this case */
- new_pstate &= ~PS_AG;
+ /* PS_AG, IG and MG are not implemented in this case */
+ new_pstate &= ~(PS_AG | PS_IG | PS_MG);
+ env->pstate = new_pstate;
+ return;
}
pstate_regs = env->pstate & 0xc01;
@@ -366,6 +396,12 @@ void helper_done(CPUSPARCState *env)
env->asi = (tsptr->tstate >> 24) & 0xff;
cpu_change_pstate(env, (tsptr->tstate >> 8) & 0xf3f);
cpu_put_cwp64(env, tsptr->tstate & 0xff);
+ if (cpu_has_hypervisor(env)) {
+ uint32_t new_gl = (tsptr->tstate >> 40) & 7;
+ env->hpstate = env->htstate[env->tl];
+ cpu_gl_switch_gregs(env, new_gl);
+ env->gl = new_gl;
+ }
env->tl--;
trace_win_helper_done(env->tl);
@@ -387,6 +423,12 @@ void helper_retry(CPUSPARCState *env)
env->asi = (tsptr->tstate >> 24) & 0xff;
cpu_change_pstate(env, (tsptr->tstate >> 8) & 0xf3f);
cpu_put_cwp64(env, tsptr->tstate & 0xff);
+ if (cpu_has_hypervisor(env)) {
+ uint32_t new_gl = (tsptr->tstate >> 40) & 7;
+ env->hpstate = env->htstate[env->tl];
+ cpu_gl_switch_gregs(env, new_gl);
+ env->gl = new_gl;
+ }
env->tl--;
trace_win_helper_retry(env->tl);