On Mon, Jan 3, 2011 at 2:07 PM, Fabien Chouteau <chout...@adacore.com> wrote: > > Signed-off-by: Fabien Chouteau <chout...@adacore.com> > --- > Makefile.target | 5 +- > hw/leon3.c | 202 > ++++++++++++++++++++++++++++++++++++++++++++++ > target-sparc/cpu.h | 39 ++++++--- > target-sparc/helper.c | 7 +- > target-sparc/helper.h | 1 + > target-sparc/op_helper.c | 151 ++++++++++++++++++++++++++++++++++- > target-sparc/translate.c | 14 +++- > 7 files changed, 397 insertions(+), 22 deletions(-) > > diff --git a/Makefile.target b/Makefile.target > index 2800f47..f40e04f 100644 > --- a/Makefile.target > +++ b/Makefile.target > @@ -290,7 +290,10 @@ obj-sparc-y += cirrus_vga.o > else > obj-sparc-y = sun4m.o lance.o tcx.o sun4m_iommu.o slavio_intctl.o > obj-sparc-y += slavio_timer.o slavio_misc.o sparc32_dma.o > -obj-sparc-y += cs4231.o eccmemctl.o sbi.o sun4c_intctl.o > +obj-sparc-y += cs4231.o eccmemctl.o sbi.o sun4c_intctl.o leon3.o > + > +# GRLIB > +obj-sparc-y += grlib_gptimer.o grlib_irqmp.o grlib_apbuart.o > endif > > obj-arm-y = integratorcp.o versatilepb.o arm_pic.o arm_timer.o > diff --git a/hw/leon3.c b/hw/leon3.c > new file mode 100644 > index 0000000..d5fe863 > --- /dev/null > +++ b/hw/leon3.c > @@ -0,0 +1,202 @@ > +/* > + * QEMU Leon3 System Emulator > + * > + * Copyright (c) 2010-2011 AdaCore > + * > + * 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 "hw.h" > +#include "qemu-timer.h" > +#include "qemu-char.h" > +#include "sysemu.h" > +#include "boards.h" > +#include "loader.h" > +#include "elf.h" > + > +#include "grlib.h" > + > +//#define DEBUG_LEON3 > + > +#ifdef DEBUG_LEON3 > +#define DPRINTF(fmt, ...) \ > + do { printf("Leon3: " fmt , ## __VA_ARGS__); } while (0) > +#else > +#define DPRINTF(fmt, ...) > +#endif > + > +/* Default system clock. */ > +#define CPU_CLK (40 * 1000 * 1000) > + > +#define PROM_FILENAME "u-boot.bin" > + > +#define MAX_PILS 16 > + > +typedef struct ResetData { > + CPUState *env; > + uint64_t entry; /* save kernel entry in case of reset */
uint32_t should be enough. > +} ResetData; > + > +static void main_cpu_reset(void *opaque) > +{ > + ResetData *s = (ResetData *)opaque; > + assert(s != NULL); > + CPUState *env = s->env; > + assert(env != NULL); These asserts won't ever trigger. > + > + cpu_reset(env); > + > + env->halted = 0; > + env->pc = s->entry; > + env->npc = s->entry + 4; > +} > + > +static void leon3_irq_ack(void *irq_manager, int intno) > +{ > + grlib_irqmp_ack((DeviceState *)irq_manager, intno); > + leon3_cache_control_int(); > +} > + > +static void leon3_generic_hw_init(ram_addr_t ram_size, > + const char *boot_device, > + const char *kernel_filename, > + const char *kernel_cmdline, > + const char *initrd_filename, > + const char *cpu_model) > +{ > + CPUState *env; > + ram_addr_t ram_offset, prom_offset; > + int ret; > + char *filename; > + qemu_irq *cpu_irqs = NULL; > + int bios_size; > + int prom_size; > + int aligned_bios_size; > + ResetData *reset_info; > + > + /* Init CPU */ > + if (!cpu_model) { > + cpu_model = "LEON3"; > + } > + > + env = cpu_init(cpu_model); > + if (!env) { > + fprintf(stderr, "qemu: Unable to find Sparc CPU definition\n"); > + exit(1); > + } > + > + cpu_sparc_set_id(env, 0); > + > + /* Reset data */ > + reset_info = qemu_mallocz(sizeof(ResetData)); > + reset_info->env = env; > + qemu_register_reset(main_cpu_reset, reset_info); > + > + /* Allocate IRQ manager */ > + grlib_irqmp_create(0x80000200, env, &cpu_irqs, MAX_PILS); > + > + env->qemu_irq_ack = leon3_irq_ack; > + > + /* Allocate RAM */ > + if ((uint64_t)ram_size > (1UL << 30)) { > + fprintf(stderr, > + "qemu: Too much memory for this machine: %d, maximum 1G\n", > + (unsigned int)(ram_size / (1024 * 1024))); > + exit(1); > + } > + > + ram_offset = qemu_ram_alloc(NULL, "leon3.ram", ram_size); > + cpu_register_physical_memory(0x40000000, ram_size, ram_offset | > IO_MEM_RAM); > + > + /* Allocate BIOS */ > + prom_size = 8 * 1024 * 1024; /* 8Mb */ > + prom_offset = qemu_ram_alloc(NULL, "Leon3.bios", prom_size); > + cpu_register_physical_memory(0x00000000, prom_size, > + prom_offset | IO_MEM_ROM); > + > + /* Load boot prom */ > + if (bios_name == NULL) { > + bios_name = PROM_FILENAME; > + } > + filename = qemu_find_file(QEMU_FILE_TYPE_BIOS, bios_name); > + > + bios_size = get_image_size(filename); > + > + if (bios_size > prom_size) { > + fprintf(stderr, "qemu: could not load prom '%s': file too big \n", > + filename); > + exit(1); > + } > + > + if (bios_size > 0) { > + aligned_bios_size = > + (bios_size + TARGET_PAGE_SIZE - 1) & TARGET_PAGE_MASK; > + > + ret = load_image_targphys(filename, 0x00000000, bios_size); > + if (ret < 0 || ret > prom_size) { > + fprintf(stderr, "qemu: could not load prom '%s'\n", filename); > + exit(1); > + } > + } > + else if (kernel_filename == NULL) { > + fprintf(stderr,"Can't read bios image %s\n", filename); > + exit(1); > + } > + > + /* Can directly load an application. */ > + if (kernel_filename != NULL) { > + long kernel_size; > + uint64_t entry; uint32_t > + > + kernel_size = load_elf(kernel_filename, NULL, NULL, &entry, NULL, > NULL, > + 1 /* big endian */, ELF_MACHINE, 0); > + if (kernel_size < 0) { > + fprintf(stderr, "qemu: could not load kernel '%s'\n", > + kernel_filename); > + exit(1); > + } > + if (bios_size <= 0) { > + /* If there is no bios/monitor, start the application. */ > + env->pc = entry; > + env->npc = entry + 4; > + reset_info->entry = entry; > + } > + } > + > + /* Allocate timers */ > + grlib_gptimer_create(0x80000300, 2, CPU_CLK, cpu_irqs, 6); > + > + /* Allocate uart */ > + if (serial_hds[0]) { > + grlib_apbuart_create(0x80000100, serial_hds[0], cpu_irqs[3]); > + } > +} > + > +QEMUMachine leon3_generic_machine = { > + .name = "leon3_generic", > + .desc = "Leon-3 generic", > + .init = leon3_generic_hw_init, > + .use_scsi = 0, > +}; > + > +static void leon3_machine_init(void) > +{ > + qemu_register_machine(&leon3_generic_machine); > +} > + > +machine_init(leon3_machine_init); > diff --git a/target-sparc/cpu.h b/target-sparc/cpu.h > index 7e0d17c..7795be4 100644 > --- a/target-sparc/cpu.h > +++ b/target-sparc/cpu.h > @@ -251,20 +251,21 @@ typedef struct sparc_def_t { > uint32_t maxtl; > } sparc_def_t; > > -#define CPU_FEATURE_FLOAT (1 << 0) > -#define CPU_FEATURE_FLOAT128 (1 << 1) > -#define CPU_FEATURE_SWAP (1 << 2) > -#define CPU_FEATURE_MUL (1 << 3) > -#define CPU_FEATURE_DIV (1 << 4) > -#define CPU_FEATURE_FLUSH (1 << 5) > -#define CPU_FEATURE_FSQRT (1 << 6) > -#define CPU_FEATURE_FMUL (1 << 7) > -#define CPU_FEATURE_VIS1 (1 << 8) > -#define CPU_FEATURE_VIS2 (1 << 9) > -#define CPU_FEATURE_FSMULD (1 << 10) > -#define CPU_FEATURE_HYPV (1 << 11) > -#define CPU_FEATURE_CMT (1 << 12) > -#define CPU_FEATURE_GL (1 << 13) > +#define CPU_FEATURE_FLOAT (1 << 0) > +#define CPU_FEATURE_FLOAT128 (1 << 1) > +#define CPU_FEATURE_SWAP (1 << 2) > +#define CPU_FEATURE_MUL (1 << 3) > +#define CPU_FEATURE_DIV (1 << 4) > +#define CPU_FEATURE_FLUSH (1 << 5) > +#define CPU_FEATURE_FSQRT (1 << 6) > +#define CPU_FEATURE_FMUL (1 << 7) > +#define CPU_FEATURE_VIS1 (1 << 8) > +#define CPU_FEATURE_VIS2 (1 << 9) > +#define CPU_FEATURE_FSMULD (1 << 10) > +#define CPU_FEATURE_HYPV (1 << 11) > +#define CPU_FEATURE_CMT (1 << 12) > +#define CPU_FEATURE_GL (1 << 13) > +#define CPU_FEATURE_TA0_SHUTDOWN (1 << 14) /* Shutdown on "ta 0x0" */ > #ifndef TARGET_SPARC64 > #define CPU_DEFAULT_FEATURES (CPU_FEATURE_FLOAT | CPU_FEATURE_SWAP | \ > CPU_FEATURE_MUL | CPU_FEATURE_DIV | \ > @@ -436,6 +437,12 @@ typedef struct CPUSPARCState { > #define SOFTINT_REG_MASK (SOFTINT_STIMER|SOFTINT_INTRMASK|SOFTINT_TIMER) > #endif > sparc_def_t *def; > + > + void *irq_manager; > + void (*qemu_irq_ack) (void *irq_manager, int intno); > + > + /* Leon3 cache control */ > + uint32_t cache_control; > } CPUSPARCState; > > #ifndef NO_CPU_IO_DEFS > @@ -471,6 +478,10 @@ int cpu_cwp_inc(CPUState *env1, int cwp); > int cpu_cwp_dec(CPUState *env1, int cwp); > void cpu_set_cwp(CPUState *env1, int new_cwp); > > +void leon3_cache_control_st(target_ulong addr, uint64_t val, int size); > +uint64_t leon3_cache_control_ld(target_ulong addr, int size); > +void leon3_cache_control_int(void); Is there any need to export these? > + > /* sun4m.c, sun4u.c */ > void cpu_check_irqs(CPUSPARCState *env); > > diff --git a/target-sparc/helper.c b/target-sparc/helper.c > index e84c312..49bdb58 100644 > --- a/target-sparc/helper.c > +++ b/target-sparc/helper.c > @@ -784,6 +784,7 @@ void cpu_reset(CPUSPARCState *env) > env->pc = 0; > env->npc = env->pc + 4; > #endif > + env->cache_control = 0; > } > > static int cpu_sparc_register(CPUSPARCState *env, const char *cpu_model) > @@ -1288,20 +1289,20 @@ static const sparc_def_t sparc_defs[] = { > .mmu_sfsr_mask = 0xffffffff, > .mmu_trcr_mask = 0xffffffff, > .nwindows = 8, > - .features = CPU_DEFAULT_FEATURES, > + .features = CPU_DEFAULT_FEATURES | CPU_FEATURE_TA0_SHUTDOWN, > }, > { > .name = "LEON3", > .iu_version = 0xf3000000, > .fpu_version = 4 << 17, /* FPU version 4 (Meiko) */ > .mmu_version = 0xf3000000, > - .mmu_bm = 0x00004000, > + .mmu_bm = 0x00000000, > .mmu_ctpr_mask = 0x007ffff0, > .mmu_cxr_mask = 0x0000003f, > .mmu_sfsr_mask = 0xffffffff, > .mmu_trcr_mask = 0xffffffff, > .nwindows = 8, > - .features = CPU_DEFAULT_FEATURES, > + .features = CPU_DEFAULT_FEATURES | CPU_FEATURE_TA0_SHUTDOWN, > }, > #endif > }; > diff --git a/target-sparc/helper.h b/target-sparc/helper.h > index 6f103e7..004eaaa 100644 > --- a/target-sparc/helper.h > +++ b/target-sparc/helper.h > @@ -83,6 +83,7 @@ DEF_HELPER_0(fcmpeq_fcc2, void) > DEF_HELPER_0(fcmpeq_fcc3, void) > #endif > DEF_HELPER_1(raise_exception, void, int) > +DEF_HELPER_0(shutdown, void) > #define F_HELPER_0_0(name) DEF_HELPER_0(f ## name, void) > #define F_HELPER_DQ_0_0(name) \ > F_HELPER_0_0(name ## d); \ > diff --git a/target-sparc/op_helper.c b/target-sparc/op_helper.c > index be3c1e0..0390aec 100644 > --- a/target-sparc/op_helper.c > +++ b/target-sparc/op_helper.c > @@ -1,6 +1,7 @@ > #include "exec.h" > #include "host-utils.h" > #include "helper.h" > +#include "sysemu.h" > > //#define DEBUG_MMU > //#define DEBUG_MXCC > @@ -9,6 +10,7 @@ > //#define DEBUG_ASI > //#define DEBUG_PCALL > //#define DEBUG_PSTATE > +//#define DEBUG_CACHE_CONTROL > > #ifdef DEBUG_MMU > #define DPRINTF_MMU(fmt, ...) \ > @@ -36,6 +38,13 @@ > #define DPRINTF_PSTATE(fmt, ...) do {} while (0) > #endif > > +#ifdef DEBUG_CACHE_CONTROL > +#define DPRINTF_CACHE_CONTROL(fmt, ...) \ > + do { printf("CACHE_CONTROL: " fmt , ## __VA_ARGS__); } while (0) > +#else > +#define DPRINTF_CACHE_CONTROL(fmt, ...) do {} while (0) > +#endif > + > #ifdef TARGET_SPARC64 > #ifndef TARGET_ABI32 > #define AM_CHECK(env1) ((env1)->pstate & PS_AM) > @@ -294,6 +303,11 @@ void HELPER(raise_exception)(int tt) > raise_exception(tt); > } > > +void helper_shutdown(void) > +{ > + qemu_system_shutdown_request(); > +} > + > void helper_check_align(target_ulong addr, uint32_t align) > { > if (addr & align) { > @@ -1609,8 +1623,13 @@ uint64_t helper_ld_asi(target_ulong addr, int asi, int > size, int sign) > > helper_check_align(addr, size - 1); > switch (asi) { > - case 2: /* SuperSparc MXCC registers */ > + case 2: /* SuperSparc MXCC registers and Leon3 cache control */ > switch (addr) { > + case 0x00: /* Leon3 Cache Control */ > + case 0x08: /* Leon3 Instruction Cache config */ > + case 0x0C: /* Leon3 Date Cache config */ > + ret = leon3_cache_control_ld(addr, size); > + break; > case 0x01c00a00: /* MXCC control register */ > if (size == 8) > ret = env->mxccregs[3]; > @@ -1838,8 +1857,14 @@ void helper_st_asi(target_ulong addr, uint64_t val, > int asi, int size) > { > helper_check_align(addr, size - 1); > switch(asi) { > - case 2: /* SuperSparc MXCC registers */ > + case 2: /* SuperSparc MXCC registers and Leon3 cache control */ > switch (addr) { > + case 0x00: /* Leon3 Cache Control */ > + case 0x08: /* Leon3 Instruction Cache config */ > + case 0x0C: /* Leon3 Date Cache config */ > + leon3_cache_control_st(addr, val, size); > + break; > + > case 0x01c00000: /* MXCC stream data register 0 */ > if (size == 8) > env->mxccdata[0] = val; > @@ -4135,6 +4160,13 @@ void do_interrupt(CPUState *env) > env->pc = env->tbr; > env->npc = env->pc + 4; > env->exception_index = -1; > + > +#if !defined(CONFIG_USER_ONLY) > + /* IRQ acknowledgment */ > + if ((intno & ~15) == TT_EXTINT && env->qemu_irq_ack != NULL) { > + env->qemu_irq_ack(env->irq_manager, intno); > + } > +#endif > } > #endif > > @@ -4329,3 +4361,118 @@ void helper_tick_set_limit(void *opaque, uint64_t > limit) > #endif > } > #endif > + > +/* Leon3 cache control */ > + > +/* Cache control: emulate the behavior of cache control registers but without > + any effect on the emulated CPU */ > + > +#define CACHE_STATE_MASK 0x3 > +#define CACHE_DISABLED 0x0 > +#define CACHE_FROZEN 0x1 > +#define CACHE_ENABLED 0x3 > + > +/* Cache Control register fields */ > + > +#define CACHE_CTRL_IF (1 << 4) /* Instruction Cache Freeze on Interrupt */ > +#define CACHE_CTRL_DF (1 << 5) /* Data Cache Freeze on Interrupt */ > +#define CACHE_CTRL_DP (1 << 14) /* Data cache flush pending */ > +#define CACHE_CTRL_IP (1 << 15) /* Instruction cache flush pending */ > +#define CACHE_CTRL_IB (1 << 16) /* Instruction burst fetch */ > +#define CACHE_CTRL_FI (1 << 21) /* Flush Instruction cache (Write only) */ > +#define CACHE_CTRL_FD (1 << 22) /* Flush Data cache (Write only) */ > +#define CACHE_CTRL_DS (1 << 23) /* Data cache snoop enable */ These should be at the top of the file. > + > + > +void leon3_cache_control_int(void) > +{ > + uint32_t state = 0; > + > + if (env->cache_control & CACHE_CTRL_IF) { > + /* Instruction cache state */ > + state = env->cache_control & CACHE_STATE_MASK; > + if (state == CACHE_ENABLED) { > + state = CACHE_FROZEN; > + DPRINTF_CACHE_CONTROL("Instruction cache: freeze\n"); > + } > + > + env->cache_control &= ~CACHE_STATE_MASK; > + env->cache_control |= state; > + } > + > + if (env->cache_control & CACHE_CTRL_DF) { > + /* Data cache state */ > + state = (env->cache_control >> 2) & CACHE_STATE_MASK; > + if (state == CACHE_ENABLED) { > + state = CACHE_FROZEN; > + DPRINTF_CACHE_CONTROL("Data cache: freeze\n"); > + } > + > + env->cache_control &= ~(CACHE_STATE_MASK << 2); > + env->cache_control |= (state << 2); > + } > +} > + > +void leon3_cache_control_st(target_ulong addr, uint64_t val, int size) > +{ > + DPRINTF_CACHE_CONTROL("st addr:%08x, val:%" PRIx64 ", size:%d\n", > + addr, val, size); > + > + if (size != 4) { > + DPRINTF_CACHE_CONTROL("32bits only\n"); > + return; > + } > + > + switch (addr) { > + case 0x00: /* Cache control */ > + > + /* These values must always be read as zeros */ > + val &= ~CACHE_CTRL_FD; > + val &= ~CACHE_CTRL_FI; > + val &= ~CACHE_CTRL_IB; > + val &= ~CACHE_CTRL_IP; > + val &= ~CACHE_CTRL_DP; > + > + env->cache_control = val; > + break; > + case 0x04: /* Instruction cache configuration */ > + case 0x08: /* Data cache configuration */ > + /* Read Only */ > + break; > + default: > + DPRINTF_CACHE_CONTROL("write unknown register %08x\n", addr); > + break; > + }; > +} > + > +uint64_t leon3_cache_control_ld(target_ulong addr, int size) > +{ > + uint64_t ret = 0; > + > + if (size != 4) { > + DPRINTF_CACHE_CONTROL("32bits only\n"); > + return 0; > + } > + > + switch (addr) { > + case 0x00: /* Cache control */ > + ret = env->cache_control; > + break; > + > + /* Configuration registers are read and only always keep those > + predefined values */ > + > + case 0x04: /* Instruction cache configuration */ > + ret = 0x10220000; > + break; > + case 0x08: /* Data cache configuration */ > + ret = 0x18220000; > + break; > + default: > + DPRINTF_CACHE_CONTROL("read unknown register %08x\n", addr); > + break; > + }; > + DPRINTF_CACHE_CONTROL("st addr:%08x, ret:%" PRIx64 ", size:%d\n", > + addr, ret, size); > + return ret; > +} > diff --git a/target-sparc/translate.c b/target-sparc/translate.c > index 23f9519..b0e8044 100644 > --- a/target-sparc/translate.c > +++ b/target-sparc/translate.c > @@ -1997,8 +1997,9 @@ static void disas_sparc_insn(DisasContext * dc) > } else > tcg_gen_mov_tl(cpu_dst, cpu_src1); > } > + > cond = GET_FIELD(insn, 3, 6); > - if (cond == 0x8) { > + if (cond == 0x8) { /* Trap Always */ > save_state(dc, cpu_cond); > if ((dc->def->features & CPU_FEATURE_HYPV) && > supervisor(dc)) > @@ -2007,7 +2008,16 @@ static void disas_sparc_insn(DisasContext * dc) > tcg_gen_andi_tl(cpu_dst, cpu_dst, V8_TRAP_MASK); > tcg_gen_addi_tl(cpu_dst, cpu_dst, TT_TRAP); > tcg_gen_trunc_tl_i32(cpu_tmp32, cpu_dst); > - gen_helper_raise_exception(cpu_tmp32); > + > + if (rs2 == 0 > + && Please merge with the next or previous line. > + dc->def->features & CPU_FEATURE_TA0_SHUTDOWN) { > + > + gen_helper_shutdown(); > + > + } else { > + gen_helper_raise_exception(cpu_tmp32); > + } > } else if (cond != 0) { > TCGv r_cond = tcg_temp_new(); > int l1; > -- > 1.7.1 > > >