From patchwork Mon Dec 6 09:26:06 2010 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Fabien Chouteau X-Patchwork-Id: 74325 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Received: from lists.gnu.org (lists.gnu.org [199.232.76.165]) (using TLSv1 with cipher DHE-RSA-AES256-SHA (256/256 bits)) (Client did not present a certificate) by ozlabs.org (Postfix) with ESMTPS id C1411B70A9 for ; Mon, 6 Dec 2010 20:36:36 +1100 (EST) Received: from localhost ([127.0.0.1]:36990 helo=lists.gnu.org) by lists.gnu.org with esmtp (Exim 4.43) id 1PPXUm-0005xW-F1 for incoming@patchwork.ozlabs.org; Mon, 06 Dec 2010 04:36:32 -0500 Received: from [140.186.70.92] (port=37518 helo=eggs.gnu.org) by lists.gnu.org with esmtp (Exim 4.43) id 1PPXLY-0003H6-Ng for qemu-devel@nongnu.org; Mon, 06 Dec 2010 04:27:02 -0500 Received: from Debian-exim by eggs.gnu.org with spam-scanned (Exim 4.71) (envelope-from ) id 1PPXLU-0006UW-Qa for qemu-devel@nongnu.org; Mon, 06 Dec 2010 04:27:00 -0500 Received: from mel.act-europe.fr ([194.98.77.210]:32861) by eggs.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1PPXLU-0006UC-Cz for qemu-devel@nongnu.org; Mon, 06 Dec 2010 04:26:56 -0500 Received: from localhost (localhost [127.0.0.1]) by filtered-smtp.eu.adacore.com (Postfix) with ESMTP id BC0D5CB02A4; Mon, 6 Dec 2010 10:26:43 +0100 (CET) X-Quarantine-ID: X-Virus-Scanned: amavisd-new at eu.adacore.com X-Amavis-Alert: BAD HEADER, Duplicate header field: "In-Reply-To" Received: from mel.act-europe.fr ([127.0.0.1]) by localhost (smtp.eu.adacore.com [127.0.0.1]) (amavisd-new, port 10024) with ESMTP id XrPPbW7AuluC; Mon, 6 Dec 2010 10:26:43 +0100 (CET) Received: from PomPomGalli.act-europe.fr (pompomgalli.act-europe.fr [10.10.1.88]) (using TLSv1 with cipher DHE-RSA-AES256-SHA (256/256 bits)) (No client certificate requested) by mel.act-europe.fr (Postfix) with ESMTP id 99CB9CB026E; Mon, 6 Dec 2010 10:26:43 +0100 (CET) From: Fabien Chouteau To: qemu-devel@nongnu.org Date: Mon, 6 Dec 2010 10:26:06 +0100 Message-Id: <7c24465a442966eba3ae4e2bb03da4463e0f8644.1291397462.git.chouteau@adacore.com> X-Mailer: git-send-email 1.7.1 In-Reply-To: <3ecbaf8fc01bebd2b2f2fb2c709642b2a1db7ff0.1291397462.git.chouteau@adacore.com> References: <5c4895c6f9c6ebd80262c494563726dd59a7ff93.1291397462.git.chouteau@adacore.com> <3ecbaf8fc01bebd2b2f2fb2c709642b2a1db7ff0.1291397462.git.chouteau@adacore.com> In-Reply-To: References: X-detected-operating-system: by eggs.gnu.org: GNU/Linux 2.6 (newer, 3) Cc: Fabien Chouteau Subject: [Qemu-devel] [PATCH 5/6] [RFC] Emulation of Leon3. X-BeenThere: qemu-devel@nongnu.org X-Mailman-Version: 2.1.5 Precedence: list List-Id: qemu-devel.nongnu.org List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Sender: qemu-devel-bounces+incoming=patchwork.ozlabs.org@nongnu.org Errors-To: qemu-devel-bounces+incoming=patchwork.ozlabs.org@nongnu.org Signed-off-by: Fabien Chouteau --- Makefile.target | 5 +- hw/leon3.c | 310 ++++++++++++++++++++++++++++++++++++++++++++++ target-sparc/cpu.h | 10 ++ target-sparc/helper.c | 2 +- target-sparc/op_helper.c | 30 ++++- 5 files changed, 353 insertions(+), 4 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..ba61081 --- /dev/null +++ b/hw/leon3.c @@ -0,0 +1,310 @@ +/* + * QEMU Leon3 System Emulator + * + * Copyright (c) 2010 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 Leon3State +{ + uint32_t cache_control; + uint32_t inst_cache_conf; + uint32_t data_cache_conf; + + uint64_t entry; /* save kernel entry in case of reset */ +} Leon3State; + +Leon3State leon3_state; + +/* Cache control: emulate the behavior of cache control registers but without + any effect on the emulated CPU */ + +#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 */ + +void leon3_cache_control_int(void) +{ + uint32_t state = 0; + + if (leon3_state.cache_control & CACHE_CTRL_IF) { + /* Instruction cache state */ + state = leon3_state.cache_control & 0x3; + if (state == CACHE_ENABLED) { + state = CACHE_FROZEN; + DPRINTF("Instruction cache: freeze\n"); + } + + leon3_state.cache_control &= ~0x3; + leon3_state.cache_control |= state; + } + + if (leon3_state.cache_control & CACHE_CTRL_DF) { + /* Data cache state */ + state = (leon3_state.cache_control >> 2) & 0x3; + if (state == CACHE_ENABLED) { + state = CACHE_FROZEN; + DPRINTF("Data cache: freeze\n"); + } + + leon3_state.cache_control &= ~(0x3 << 2); + leon3_state.cache_control |= (state << 2); + } +} + +void leon3_cache_control_st(target_ulong addr, uint64_t val, int size) +{ + DPRINTF("cc st addr:%lu, val:0x%x, size:%d\n", (long unsigned int)addr, + (unsigned int)val, size); + + if (size != 4) { + DPRINTF(" CC 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; + + leon3_state.cache_control = val; + break; + case 0x04: /* Instruction cache configuration */ + case 0x08: /* Data cache configuration */ + /* Read Only */ + break; + default: + DPRINTF(" CC write unknown register 0x%04x\n", (int)addr); + break; + }; +} + +uint64_t leon3_cache_control_ld(target_ulong addr, int size) +{ + uint64_t ret = 0; + + if (size != 4) { + DPRINTF(" CC 32bits only\n"); + return 0; + } + + switch (addr) { + case 0x00: /* Cache control */ + ret = leon3_state.cache_control; + break; + case 0x04: /* Instruction cache configuration */ + ret = leon3_state.inst_cache_conf; + break; + case 0x08: /* Data cache configuration */ + ret = leon3_state.data_cache_conf; + break; + default: + DPRINTF(" CC read unknown register 0x%04x\n", (int)addr); + break; + }; + DPRINTF("cc ld addr:%lu, size:%d, ret:%lu\n", (long unsigned int)addr, + size, (long unsigned int)ret ); + return ret; +} + +void leon3_shutdown(void) +{ + qemu_system_shutdown_request(); +} + +static void main_cpu_reset(void *opaque) +{ + CPUState *env = opaque; + + cpu_reset(env); + + env->halted = 0; + env->pc = leon3_state.entry; + env->npc = leon3_state.entry + 4; + + /* Initialize cache control */ + leon3_state.cache_control = 0x0; + + /* Configuration registers are read and only always keep those predefined + values */ + leon3_state.inst_cache_conf = 0x10220000; + leon3_state.data_cache_conf = 0x18220000; +} + +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; + + /* 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); + + qemu_register_reset(main_cpu_reset, env); + + /* Allocate IRQ manager */ + grlib_irqmp_create(0x80000200, env, &cpu_irqs, MAX_PILS); + + /* 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; + + 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; + leon3_state.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..6020ffd 100644 --- a/target-sparc/cpu.h +++ b/target-sparc/cpu.h @@ -474,6 +474,16 @@ void cpu_set_cwp(CPUState *env1, int new_cwp); /* sun4m.c, sun4u.c */ void cpu_check_irqs(CPUSPARCState *env); +/* grlib_irqmp.c */ +void grlib_irqmp_ack(CPUSPARCState *env, int intno); + +/* leon3.c */ +void leon3_shutdown(void); +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); + + #if defined (TARGET_SPARC64) static inline int compare_masked(uint64_t x, uint64_t y, uint64_t mask) diff --git a/target-sparc/helper.c b/target-sparc/helper.c index e84c312..3bf990f 100644 --- a/target-sparc/helper.c +++ b/target-sparc/helper.c @@ -1295,7 +1295,7 @@ static const sparc_def_t sparc_defs[] = { .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, diff --git a/target-sparc/op_helper.c b/target-sparc/op_helper.c index be3c1e0..85df077 100644 --- a/target-sparc/op_helper.c +++ b/target-sparc/op_helper.c @@ -1609,8 +1609,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 +1843,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; @@ -4081,6 +4092,13 @@ void do_interrupt(CPUState *env) { int cwp, intno = env->exception_index; +#if !defined(CONFIG_USER_ONLY) + /* Leon3 shutdown */ + if (intno == 0x80 && env->version == 0xf3000000) { + leon3_shutdown(); + } +#endif + #ifdef DEBUG_PCALL if (qemu_loglevel_mask(CPU_LOG_INT)) { static int count; @@ -4135,6 +4153,14 @@ 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 for Leon3 */ + if (env->version == 0xf3000000 && (intno & ~15) == TT_EXTINT) { + grlib_irqmp_ack (env, intno); + leon3_cache_control_int(); + } +#endif } #endif