From e2f2f73f908beaeb70810fd8d7851b8cbcf5b098 Mon Sep 17 00:00:00 2001 From: Kumar Gala Date: Thu, 25 Mar 2021 18:24:50 -0500 Subject: [PATCH 1/6] qemu/nios2: Add Altera MAX 10 board support for Zephyr OS Exisitng 10m50_devboard is not supporting qemu-niso2 on Zephyr OS and the reason might be that the softcpu build may be different for Linux and Zephyr OS. So added support for Zephyr qemu-nios2 board. Signed-off-by: Ramakrishna Pallala --- hw/nios2/altera_10m50_zephyr.c | 154 +++++++++++++++++++++++++++++++++ hw/nios2/meson.build | 2 +- 2 files changed, 155 insertions(+), 1 deletion(-) create mode 100644 hw/nios2/altera_10m50_zephyr.c diff --git a/hw/nios2/altera_10m50_zephyr.c b/hw/nios2/altera_10m50_zephyr.c new file mode 100644 index 0000000000000..4b3465429fba2 --- /dev/null +++ b/hw/nios2/altera_10m50_zephyr.c @@ -0,0 +1,154 @@ +/* + * Copyright (c) 2018 Intel Corporation + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, see . + */ + +#include "qemu/osdep.h" +#include "qapi/error.h" +#include "qemu-common.h" +#include "cpu.h" +#include "hw/sysbus.h" +#include "hw/hw.h" +#include "hw/char/serial.h" +#include "sysemu/sysemu.h" +#include "sysemu/reset.h" +#include "hw/qdev-properties.h" +#include "hw/boards.h" +#include "hw/loader.h" +#include "exec/memory.h" +#include "exec/address-spaces.h" +#include "qemu/config-file.h" +#include "qemu/option.h" +#include "boot.h" + +#define DEBUG + +#ifdef DEBUG +# define DPRINTF(format, ...) printf(format, ## __VA_ARGS__) +#else +# define DPRINTF(format, ...) do { } while (0) +#endif + +#define ROM_BASE 0x0 +#define ROM_SIZE 32 + +#define RAM_BASE 0x400000 +#define RAM_SIZE 262144 + +#define U16550_0_BASE 0x440000 +#define U16550_0_IRQ_IDX 1 + +#define TIMER_0_BASE 0x440200 +#define TIMER_0_FREQ 50000000 +#define TIMER_0_IRQ_IDX 2 + +#define ALT_CPU_EXCEPTION_ADDR 0x00400020 +#define ALT_CPU_RESET_ADDR 0x00000000 + +#define EM_ALTERA_NIOS2 113 + +static struct { + uint32_t bootstrap_pc; +} boot_info_zephyr; + +static void main_cpu_reset(void *opaque) +{ + Nios2CPU *cpu = opaque; + CPUNios2State *env = &cpu->env; + + cpu_reset(CPU(cpu)); + env->regs[R_PC] = boot_info_zephyr.bootstrap_pc; +} + +static void altera_10m50_zephyr_init(MachineState *machine) +{ + const char *kernel_filename; + MemoryRegion *sysmem = get_system_memory(); + Nios2CPU *cpu; + DeviceState *dev; + qemu_irq irq[32]; + int kernel_size; + int i; + + MemoryRegion *rom = g_new(MemoryRegion, 1); + MemoryRegion *ram = g_new(MemoryRegion, 1); + + cpu = NIOS2_CPU(object_new(TYPE_NIOS2_CPU)); + object_property_set_bool(OBJECT(cpu), "mmu_present", false, + &error_abort); + object_property_set_bool(OBJECT(cpu), "realized", true, &error_abort); + + kernel_filename = current_machine->kernel_filename; + + memory_region_init_ram(rom, NULL, "nios2.rom", ROM_SIZE, &error_fatal); + memory_region_set_readonly(rom, true); + memory_region_add_subregion(sysmem, ROM_BASE, rom); + + memory_region_init_ram(ram, NULL, "nios2.ram", RAM_SIZE, &error_fatal); + memory_region_add_subregion(sysmem, RAM_BASE, ram); + + for (i = 0; i < 32; i++) { + irq[i] = qdev_get_gpio_in_named(DEVICE(cpu), "IRQ", i); + } + + /* Register: Altera 16550 UART */ + serial_mm_init(sysmem, U16550_0_BASE, 2, irq[U16550_0_IRQ_IDX], + 115200, serial_hd(0), DEVICE_NATIVE_ENDIAN); + + /* Register: Timer sys_clk_timer */ + dev = qdev_new("ALTR.timer"); + qdev_prop_set_uint32(dev, "clock-frequency", TIMER_0_FREQ); + sysbus_realize_and_unref(SYS_BUS_DEVICE(dev), &error_fatal); + sysbus_mmio_map(SYS_BUS_DEVICE(dev), 0, TIMER_0_BASE); + sysbus_connect_irq(SYS_BUS_DEVICE(dev), 0, irq[TIMER_0_IRQ_IDX]); + + cpu->reset_addr = ALT_CPU_RESET_ADDR; + cpu->exception_addr = ALT_CPU_EXCEPTION_ADDR; + cpu->fast_tlb_miss_addr = ALT_CPU_RESET_ADDR; + DPRINTF("\tcpu->env.reset_addr: \t\t%0x\n", cpu->reset_addr); + DPRINTF("\tcpu->env.exception_addr: \t%0x\n", cpu->exception_addr); + + if (kernel_filename) { + uint64_t entry; + + /* Boots a kernel elf binary. */ + kernel_size = load_elf(kernel_filename, NULL, NULL, NULL, + &entry, NULL, NULL, NULL, + 0, EM_ALTERA_NIOS2, 0, 0); + + boot_info_zephyr.bootstrap_pc = entry; + + /* Not an ELF image, try a RAW image. */ + if (kernel_size < 0) { + hwaddr uentry, loadaddr; + + kernel_size = load_uimage(kernel_filename, &uentry, + &loadaddr, 0, NULL, NULL); + boot_info_zephyr.bootstrap_pc = uentry; + } + + if (kernel_size < 0) { + fprintf(stderr, "qemu: could not load kernel '%s'\n", + kernel_filename); + exit(1); + } + } + qemu_register_reset(main_cpu_reset, cpu); + +} + +static void altera_10m50_zephyr_machine_init(MachineClass *mc) +{ + mc->desc = "Altera 10m50 for Zephyr."; + mc->init = altera_10m50_zephyr_init; + mc->is_default = 0; +} + +DEFINE_MACHINE("altera_10m50_zephyr", altera_10m50_zephyr_machine_init) diff --git a/hw/nios2/meson.build b/hw/nios2/meson.build index 6c58e8082b48c..bc1dfcd70cc5b 100644 --- a/hw/nios2/meson.build +++ b/hw/nios2/meson.build @@ -1,6 +1,6 @@ nios2_ss = ss.source_set() nios2_ss.add(files('boot.c')) -nios2_ss.add(when: 'CONFIG_NIOS2_10M50', if_true: files('10m50_devboard.c')) +nios2_ss.add(when: 'CONFIG_NIOS2_10M50', if_true: files('10m50_devboard.c', 'altera_10m50_zephyr.c')) nios2_ss.add(when: 'CONFIG_NIOS2_GENERIC_NOMMU', if_true: files('generic_nommu.c')) hw_arch += {'nios2': nios2_ss} From ac291af451c5b254ace1f609855bdd93785d0198 Mon Sep 17 00:00:00 2001 From: Yasushi SHOJI Date: Wed, 9 Mar 2016 12:22:16 +0100 Subject: [PATCH 2/6] hw/sparc: Add leon (at697) machine This commit add LEON2 support. It is taken from AdaCore Qemu repository at https://github.com/adacore/qemu. https://github.com/adacore/qemu/commit/9d94f4803 The following is the original commit message. > Part of K104-004. > > --- merged with --- > > Add plug-ins and GNATbus to leon > > --- merged with --- > > Add HostFs device to leon > > 2019-03-24 KONRAD Frederic > * hw/sparc/leon.c: fix load_elf parameters. > 2019-02-01 KONRAD Frederic > * hw/sparc/leon.c: drop plugin. > 2018-06-08 KONRAD Frederic > * hw/sparc/leon.c: fixed against serial_hds disparition > 2018-04-06 KONRAD Frederic > * hw/sparc/leon.c: Use cpu_type instead of cpu_model which has been > dropped. > 2018-02-12 KONRAD Frederic > * hw/sparc/leon.c: Update to new Chardev API. > * hw/sparc/leon.c: Update to hostfs, gnatbus, plugin source > reorganization. > * hw/sparc/leon.c: Update leon2_intctl_ack prototype. > > Change-Id: I594757ad137370479b152e5de8de7be7caf9a911 Signed-off-by: Yasushi SHOJI --- hw/sparc/leon.c | 878 ++++++++++++++++++++++++++++++++++++++++++ hw/sparc/meson.build | 1 + hw/sparc/trace-events | 13 + target/sparc/cpu.c | 2 +- 4 files changed, 893 insertions(+), 1 deletion(-) create mode 100644 hw/sparc/leon.c diff --git a/hw/sparc/leon.c b/hw/sparc/leon.c new file mode 100644 index 0000000000000..316bd7028836e --- /dev/null +++ b/hw/sparc/leon.c @@ -0,0 +1,878 @@ +/* + * QEMU Leon2 System Emulator + * + * Copyright (c) 2009-2013 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 "qemu/osdep.h" +#include "qapi/error.h" +#include "cpu.h" +#include "hw/hw.h" +#include "qemu/timer.h" +#include "chardev/char.h" +#include "sysemu/sysemu.h" +#include "hw/boards.h" +#include "hw/loader.h" +#include "elf.h" +#include "trace.h" +#include "hw/ptimer.h" +#include "exec/memory.h" +#include "exec/address-spaces.h" +#include "hw/adacore/gnat-bus.h" +#include "hw/adacore/hostfs.h" + +/* Default system clock. */ +#define CPU_CLK (50 * 1000 * 1000) + +/* Leon registers */ + +#define MEMORY_CONFIGURATION_REGISTER_1 0x00 +#define MEMORY_CONFIGURATION_REGISTER_2 0x04 +#define MEMORY_CONFIGURATION_REGISTER_3 0x08 +#define FAIL_STATUS_REGISTER 0x10 + +/* Cache Control register */ + +#define CACHE_CONTROL_REGISTER 0x14 +#define CCR_MASK 0x00e13fff +#define CCR_INIT 0xf7100000 + +/* Cache Control register fields */ + +#define CACHE_STATE_MASK 0x3 +#define CACHE_DISABLED 0x0 +#define CACHE_FROZEN 0x1 +#define CACHE_ENABLED 0x3 + +#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) */ + +/* Timers registers */ + +#define TIMER_1_COUNTER_REGISTER 0x40 +#define TIMER_1_RELOAD_REGISTER 0x44 +#define TIMER_1_CONTROL_REGISTER 0x48 +#define WATCHDOG_REGISTER 0x4c +#define TIMER_2_COUNTER_REGISTER 0x50 +#define TIMER_2_RELOAD_REGISTER 0x54 +#define TIMER_2_CONTROL_REGISTER 0x58 +#define PRESCALER_COUNTER_REGISTER 0x60 +#define PRESCALER_RELOAD_REGISTER 0x64 + +/* Timers registers fields */ + +#define TIMCTR_EN 0x01 /* Enable Counter */ +#define TIMCTR_RL 0x02 /* Reload Counter */ +#define TIMCTR_LD 0x04 /* Load Counter */ +#define TIMCTR_MASK 0x07 + +/* Interrupt controller registers */ + +#define INTERRUPT_MASK_AND_PRIORITY_REGISTER 0x90 +#define INTERRUPT_PENDING_REGISTER 0x94 +#define INTERRUPT_FORCE_REGISTER 0x98 +#define INTERRUPT_CLEAR_REGISTER 0x9c + +/* IO registers */ + +#define IO_PORT_DATA_REGISTER 0xa0 +#define IO_PORT_DIRECTION_REGISTER 0xa4 +#define IO_PORT_INTERRUPT_REGISTER 0xa8 + +/* UART registers */ + +#define UART_1_DATA_REGISTER 0x70 +#define UART_1_STATUS_REGISTER 0x74 +#define UART_1_CONTROL_REGISTER 0x78 +#define UART_1_SCALER_REGISTER 0x7c +#define UART_2_DATA_REGISTER 0x80 +#define UART_2_STATUS_REGISTER 0x84 +#define UART_2_CONTROL_REGISTER 0x88 +#define UART_2_SCALER_REGISTER 0x8c + +/* UART register fields */ + +#define UART_STATUS_DR 0x01 /* Data Ready */ +#define UART_STATUS_TS 0x02 /* Transmitter Shift Register Empty */ +#define UART_STATUS_TH 0x04 /* Transmitter Hold Register Empty */ +#define UART_STATUS_BR 0x08 /* Break Received */ +#define UART_STATUS_OV 0x10 /* Overrun */ +#define UART_STATUS_PE 0x20 /* Parity Error */ +#define UART_STATUS_FE 0x40 /* Framing Error */ + +#define UART_CONTROL_RE 0x001 /* Receiver Enable */ +#define UART_CONTROL_TE 0x002 /* Transmitter Enable */ +#define UART_CONTROL_RI 0x004 /* Receiver Interrupt Enable */ +#define UART_CONTROL_TI 0x008 /* Transmitter Interrupt Enable */ +#define UART_CONTROL_PS 0x010 /* Parity Select */ +#define UART_CONTROL_PE 0x020 /* Parity Enable */ +#define UART_CONTROL_FL 0x040 /* Flow Control */ +#define UART_CONTROL_LB 0x080 /* Loop Back */ +#define UART_CONTROL_EC 0x100 /* External Clock */ + + +#define PROM_FILENAME "u-boot.bin" + +#define MAX_PILS 16 + +#define FIFO_LENGTH 1024 + +typedef struct LeonUartState { + uint32_t status; + uint32_t control; + uint32_t scaler; + CharBackend chr; + qemu_irq irq; + + /* FIFO */ + char buffer[FIFO_LENGTH]; + int len; + int current; + +} LeonUartState; + +struct LeonTimerState { + QEMUBH *bh; + struct ptimer_state *ptimer; + + qemu_irq irq; + + int id; + + /* registers */ + uint32_t counter; + uint32_t reload; + uint32_t control; +}; + +struct LeonIntState { + uint32_t lvl_mask; + uint32_t pending; + uint32_t force; + CPUSPARCState *env; +}; + +typedef struct LeonIoState { + uint32_t mcfg[3]; + + struct LeonIntState intctl; + + uint32_t ccr; + uint32_t scar; + uint32_t wdg; + uint32_t iodata; + uint32_t iodir; + uint32_t ioit; + + struct LeonTimerState timer1; + struct LeonTimerState timer2; + + LeonUartState uart1; + LeonUartState uart2; +} LeonIoState; + + +typedef struct ResetData { + SPARCCPU *cpu; + uint32_t entry; /* save kernel entry in case of reset */ + uint32_t stack_pointer; +} ResetData; + +static void main_cpu_reset(void *opaque) +{ + ResetData *s = (ResetData *)opaque; + CPUState *cpu = CPU(s->cpu); + CPUSPARCState *env = &s->cpu->env; + + cpu_reset(cpu); + + cpu->halted = 0; + env->pc = s->entry; + env->npc = s->entry + 4; + env->regbase[6] = s->stack_pointer; +} + +static void leon_check_irqs(struct LeonIntState *s) +{ + uint32_t pend = 0; + uint32_t level0 = 0; + uint32_t level1 = 0; + CPUSPARCState *env = s->env; + CPUState *cs = CPU(sparc_env_get_cpu(env)); + + pend = (s->pending | s->force) & (s->lvl_mask & 0xfffe); + + level0 = pend & ~(s->lvl_mask >> 16); + level1 = pend & (s->lvl_mask >> 16); + + trace_leon_check_irqs(s->pending, s->force, + s->lvl_mask, level1, level0); + + /* Trigger level1 interrupt first and level0 if there is no level1 */ + if (level1 != 0) { + env->pil_in = level1; + } else { + env->pil_in = level0; + } + + if (env->pil_in && (env->interrupt_index == 0 || + (env->interrupt_index & ~15) == TT_EXTINT)) { + unsigned int i; + + for (i = 15; i > 0; i--) { + if (env->pil_in & (1 << i)) { + int old_interrupt = env->interrupt_index; + + env->interrupt_index = TT_EXTINT | i; + if (old_interrupt != env->interrupt_index) { + trace_leon_set_irq(i); + cpu_interrupt(cs, CPU_INTERRUPT_HARD); + } + break; + } + } + } else if (!env->pil_in && (env->interrupt_index & ~15) == TT_EXTINT) { + trace_leon_reset_irq(env->interrupt_index & 15); + env->interrupt_index = 0; + cpu_reset_interrupt(cs, CPU_INTERRUPT_HARD); + } +} + +static void leon2_intctl_ack(CPUSPARCState *env, int intno) +{ + struct LeonIntState *intctl = env->irq_manager; + uint32_t mask; + uint32_t state = 0; + + intno &= 15; + mask = 1 << intno; + + trace_leon_intctl_ack(intno); + + /* Clear registers. */ + intctl->pending &= ~mask; + intctl->force &= ~mask; + + /* Cache Control */ + if (intctl->env->cache_control & CACHE_CTRL_IF) { + /* Instruction cache state */ + state = intctl->env->cache_control & CACHE_STATE_MASK; + if (state == CACHE_ENABLED) { + state = CACHE_FROZEN; + } + + intctl->env->cache_control &= ~CACHE_STATE_MASK; + intctl->env->cache_control |= state; + } + + if (intctl->env->cache_control & CACHE_CTRL_DF) { + /* Data cache state */ + state = (intctl->env->cache_control >> 2) & CACHE_STATE_MASK; + if (state == CACHE_ENABLED) { + state = CACHE_FROZEN; + } + + intctl->env->cache_control &= ~(CACHE_STATE_MASK << 2); + intctl->env->cache_control |= (state << 2); + } + + leon_check_irqs(intctl); +} + +static void leon_set_irq(void *opaque, int irq, int level) +{ + struct LeonIntState *s = opaque; + + if (level) { + s->pending |= 1 << irq; + leon_check_irqs(s); + } +} + +static void leon_uart_check_irq(struct LeonUartState *s) +{ + if (((s->status & UART_STATUS_DR) && (s->control & UART_CONTROL_RI)) + || (!(s->status & UART_STATUS_TH) && (s->control & UART_CONTROL_TI))) { + qemu_irq_pulse(s->irq); + } +} + +static int uart_data_to_read(LeonUartState *s) +{ + return s->current < s->len; +} + +static char uart_pop(LeonUartState *s) +{ + char ret; + + if (s->len == 0) { + return 0; + } + + ret = s->buffer[s->current++]; + + if (s->current >= s->len) { + /* Flush */ + s->len = 0; + s->current = 0; + } + return ret; +} + +static void uart_add_to_fifo(LeonUartState *s, + const uint8_t *buffer, + int length) +{ + if (s->len + length > FIFO_LENGTH) { + abort(); + } + memcpy(s->buffer + s->len, buffer, length); + s->len += length; +} + +static int leon_uart_can_receive(void *opaque) +{ + LeonUartState *s = opaque; + + return FIFO_LENGTH - s->len; +} + +static void leon_uart_receive(void *opaque, const uint8_t *buf, int size) +{ + LeonUartState *s = opaque; + + uart_add_to_fifo(s, buf, size); + s->status |= UART_STATUS_DR; + leon_uart_check_irq(s); +} + +static void leon_uart_event(void *opaque, QEMUChrEvent event) +{ + trace_leon_uart_event(event); +} + +static uint32_t leon_uart_read_uad(struct LeonUartState *s) +{ + uint32_t ret = uart_pop(s); + + if (!uart_data_to_read(s)) { + s->status &= ~UART_STATUS_DR; + leon_uart_check_irq(s); + } + + return ret; +} + +static void leon_uart_init(Chardev *chr, + struct LeonUartState *s, + qemu_irq irq) +{ + s->control = 0; + s->irq = irq; + s->status = UART_STATUS_TS | UART_STATUS_TH; + + qemu_chr_fe_init(&s->chr, chr, &error_abort); + qemu_chr_fe_set_handlers(&s->chr, leon_uart_can_receive, leon_uart_receive, + leon_uart_event, NULL, s, NULL, true); +} + + +static void leon_timer_enable(struct LeonTimerState *s) +{ + ptimer_stop(s->ptimer); + + if (s->control & TIMCTR_LD) { + /* reload */ + s->counter = s->reload; + } + + if (!(s->control & TIMCTR_EN)) { + /* Timer disabled */ + trace_leon_timer_disabled(s->id, s->control); + return; + } + + /* ptimer is triggered when the counter reach 0 but GPTimer is triggered at + underflow. Set count + 1 to simulate the GPTimer behavior. */ + + trace_leon_timer_enable(s->id, s->counter + 1); + + ptimer_set_count(s->ptimer, s->counter + 1); + ptimer_run(s->ptimer, 1); +} + +static void leon_timer_hit(void *opaque) +{ + struct LeonTimerState *s = opaque; + + trace_leon_timer_hit(s->id); + + qemu_irq_pulse(s->irq); + + if (s->control & TIMCTR_RL) { + /* reload */ + s->control |= TIMCTR_LD; + leon_timer_enable(s); + } +} + +static uint32_t leon_timer_io_read(void *opaque, hwaddr addr) +{ + LeonIoState *s = opaque; + uint32_t ret; + + switch (addr) { + case PRESCALER_COUNTER_REGISTER: + ret = 0; + break; + case PRESCALER_RELOAD_REGISTER: + ret = s->scar; + break; + + case TIMER_1_COUNTER_REGISTER: + ret = ptimer_get_count(s->timer1.ptimer); + break; + + case TIMER_2_COUNTER_REGISTER: + ret = ptimer_get_count(s->timer2.ptimer); + break; + + case TIMER_1_RELOAD_REGISTER: + ret = s->timer1.reload; + break; + case TIMER_2_RELOAD_REGISTER: + ret = s->timer2.reload; + break; + + + case TIMER_1_CONTROL_REGISTER: + ret = s->timer1.control; + break; + case TIMER_2_CONTROL_REGISTER: + ret = s->timer2.control; + break; + + case WATCHDOG_REGISTER: + ret = s->wdg; + break; + + + default: + trace_leon_unknown_register("Timer:read", addr); + return 0; + } + + trace_leon_readl(addr, ret); + return ret; +} + +static void leon_timer_io_write(LeonIoState *s, hwaddr addr, + uint32_t val) +{ + trace_leon_writel(addr, val); + + switch (addr) { + case PRESCALER_COUNTER_REGISTER: + break; + case PRESCALER_RELOAD_REGISTER: + s->scar = val & 0x3ff; + val = CPU_CLK / (s->scar + 1); + ptimer_set_freq(s->timer1.ptimer, val); + ptimer_set_freq(s->timer2.ptimer, val); + break; + + case TIMER_1_COUNTER_REGISTER: + s->timer1.counter = val & 0x00ffffff; + leon_timer_enable(&s->timer1); + break; + case TIMER_2_COUNTER_REGISTER: + s->timer2.counter = val & 0x00ffffff; + leon_timer_enable(&s->timer2); + break; + + case TIMER_1_RELOAD_REGISTER: + s->timer1.reload = val & 0x00ffffff; + break; + case TIMER_2_RELOAD_REGISTER: + s->timer2.reload = val & 0x00ffffff; + break; + + + case TIMER_1_CONTROL_REGISTER: + s->timer1.control = val & TIMCTR_MASK; + leon_timer_enable(&s->timer1); + break; + case TIMER_2_CONTROL_REGISTER: + s->timer2.control = val & TIMCTR_MASK; + leon_timer_enable(&s->timer2); + break; + + case WATCHDOG_REGISTER: + s->wdg = val & 0x00ffffff; + break; + + default: + trace_leon_unknown_register("Timer:write", addr); + break; + } +} + +static void leon_timer_init(struct LeonTimerState *s, qemu_irq irq, int id) +{ + s->id = id; + s->counter = 0; + s->reload = 0; + s->control = 0; + s->irq = irq; + s->bh = qemu_bh_new(leon_timer_hit, s); + s->ptimer = ptimer_init(s->bh, PTIMER_POLICY_DEFAULT); + + ptimer_set_freq(s->ptimer, CPU_CLK); + +} + +static uint64_t leon_io_read(void *opaque, hwaddr addr, + unsigned size) +{ + LeonIoState *s = opaque; + uint64_t ret; + + switch (addr) { + case MEMORY_CONFIGURATION_REGISTER_1: + case MEMORY_CONFIGURATION_REGISTER_2: + case MEMORY_CONFIGURATION_REGISTER_3: + ret = s->mcfg[(addr - MEMORY_CONFIGURATION_REGISTER_1) >> 2]; + break; + case FAIL_STATUS_REGISTER: + ret = 0; + break; + case CACHE_CONTROL_REGISTER: + ret = s->intctl.env->cache_control; + break; + + case INTERRUPT_MASK_AND_PRIORITY_REGISTER: + ret = s->intctl.lvl_mask; + break; + case INTERRUPT_PENDING_REGISTER: + ret = s->intctl.pending; + break; + case INTERRUPT_FORCE_REGISTER: + ret = s->intctl.force; + break; + case INTERRUPT_CLEAR_REGISTER: + ret = 0; + break; + + case UART_1_DATA_REGISTER: + case UART_1_DATA_REGISTER + 3: /* when only one byte read */ + ret = leon_uart_read_uad(&s->uart1); + break; + case UART_1_CONTROL_REGISTER: + ret = s->uart1.control; + break; + case UART_1_SCALER_REGISTER: + ret = s->uart1.scaler; + break; + case UART_1_STATUS_REGISTER: + ret = s->uart1.status; + break; + + case UART_2_DATA_REGISTER: + case UART_2_DATA_REGISTER + 3: /* when only one byte read */ + ret = leon_uart_read_uad(&s->uart2); + break; + case UART_2_CONTROL_REGISTER: + ret = s->uart2.control; + break; + case UART_2_SCALER_REGISTER: + ret = s->uart2.scaler; + break; + case UART_2_STATUS_REGISTER: + ret = s->uart2.status; + break; + + case TIMER_1_RELOAD_REGISTER ... PRESCALER_RELOAD_REGISTER: + ret = leon_timer_io_read(s, addr); + break; + + case IO_PORT_DATA_REGISTER: + ret = s->iodata; + break; + case IO_PORT_DIRECTION_REGISTER: + ret = s->iodir; + break; + case IO_PORT_INTERRUPT_REGISTER: + ret = s->ioit; + break; + + default: + trace_leon_unknown_register("Leon:read", addr); + ret = 0; + break; + } + + trace_leon_readl(addr, ret); + + return ret; +} + +static void leon_io_write(void *opaque, hwaddr addr, + uint64_t value, unsigned size) +{ + LeonIoState *s = opaque; + + trace_leon_writel(addr, value); + + switch (addr) { + case MEMORY_CONFIGURATION_REGISTER_1: + case MEMORY_CONFIGURATION_REGISTER_2: + case MEMORY_CONFIGURATION_REGISTER_3: + s->mcfg[(addr - MEMORY_CONFIGURATION_REGISTER_1) >> 2] = value; + break; + case FAIL_STATUS_REGISTER: + break; + case CACHE_CONTROL_REGISTER: + /* These values must always be read as zeros */ + value &= ~CACHE_CTRL_FD; + value &= ~CACHE_CTRL_FI; + value &= ~CACHE_CTRL_IB; + value &= ~CACHE_CTRL_IP; + value &= ~CACHE_CTRL_DP; + s->intctl.env->cache_control = value; + break; + + case INTERRUPT_MASK_AND_PRIORITY_REGISTER: + s->intctl.lvl_mask = value; + break; + case INTERRUPT_PENDING_REGISTER: + /* Read Only */ + break; + case INTERRUPT_FORCE_REGISTER: + s->intctl.force = value & 0xfffe; + leon_check_irqs(&s->intctl); + break; + case INTERRUPT_CLEAR_REGISTER: + s->intctl.pending &= ~(value & 0xfffe); + leon_check_irqs(&s->intctl); + break; + + case UART_1_CONTROL_REGISTER: + s->uart1.control = value & 0x1ff; + break; + case UART_1_SCALER_REGISTER: + s->uart1.scaler = value & 0x3ff; + break; + case UART_1_DATA_REGISTER: + case UART_1_DATA_REGISTER + 3: /* when only one byte write */ + { + unsigned char c = value; + if (qemu_chr_fe_get_driver(&s->uart1.chr)) { + qemu_chr_fe_write(&s->uart1.chr, &c, 1); + } + } + break; + + case UART_2_CONTROL_REGISTER: + s->uart2.control = value & 0x1ff; + break; + case UART_2_SCALER_REGISTER: + s->uart2.scaler = value & 0x3ff; + break; + case UART_2_DATA_REGISTER: + case UART_2_DATA_REGISTER + 3: /* when only one byte write */ + { + unsigned char c = value; + if (qemu_chr_fe_get_driver(&s->uart2.chr)) { + qemu_chr_fe_write(&s->uart1.chr, &c, 1); + } + } + break; + + case TIMER_1_RELOAD_REGISTER ... PRESCALER_RELOAD_REGISTER: + leon_timer_io_write(s, addr, value); + break; + + case IO_PORT_DATA_REGISTER: + s->iodata = value & 0xffff; + break; + case IO_PORT_DIRECTION_REGISTER: + s->iodir = value & 0x3ffff; + break; + case IO_PORT_INTERRUPT_REGISTER: + s->ioit = value; + break; + + default: + trace_leon_unknown_register("Leon:write", addr); + } +} + +static const MemoryRegionOps leon_io_ops = { + .read = leon_io_read, + .write = leon_io_write, + .endianness = DEVICE_NATIVE_ENDIAN, + .valid = { + .min_access_size = 1, + .max_access_size = 4, + }, +}; + +static void at697_hw_init(MachineState *machine) +{ + ram_addr_t ram_size = machine->ram_size; + const char *kernel_filename = machine->kernel_filename; + SPARCCPU *cpu; + CPUSPARCState *env; + MemoryRegion *address_space_mem = get_system_memory(); + MemoryRegion *ram = g_new(MemoryRegion, 1); + MemoryRegion *ram2 = g_new(MemoryRegion, 1); + MemoryRegion *prom = g_new(MemoryRegion, 1); + MemoryRegion *iomem = g_new(MemoryRegion, 1); + ram_addr_t ram2_size; + int ret; + char *filename; + qemu_irq *cpu_irqs; + int bios_size; + int aligned_bios_size; + LeonIoState *s; + ResetData *reset_info; + + cpu = SPARC_CPU(cpu_create(machine->cpu_type)); + if (cpu == NULL) { + fprintf(stderr, "qemu: Unable to find Sparc CPU definition\n"); + exit(1); + } + env = &cpu->env; + + cpu_sparc_set_id(env, 0); + + /* Reset data */ + reset_info = g_malloc0(sizeof(ResetData)); + reset_info->cpu = cpu; + qemu_register_reset(main_cpu_reset, reset_info); + + s = g_malloc0(sizeof(struct LeonIoState)); + s->ccr = CCR_INIT; + s->intctl.env = env;; + + env->irq_manager = &s->intctl; + env->qemu_irq_ack = leon2_intctl_ack; + + cpu_irqs = qemu_allocate_irqs(leon_set_irq, &s->intctl, 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); + } + memory_region_init_ram(ram, NULL, "leon.ram", ram_size, &error_abort); + memory_region_add_subregion(address_space_mem, 0x40000000, ram); + + reset_info->stack_pointer = 0x40000000 + ram_size; + + /* Allocate RAM2. */ + ram2_size = 8 << 20; + memory_region_init_ram(ram2, NULL, "leon.ram2", ram2_size, &error_abort); + memory_region_add_subregion(address_space_mem, 0x20000000, ram2); + + /* 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 > 0) { + aligned_bios_size = + (bios_size + TARGET_PAGE_SIZE - 1) & TARGET_PAGE_MASK; + + memory_region_init_ram(prom, NULL, "leon.bios", aligned_bios_size, + &error_abort); + memory_region_set_readonly(prom, true); + memory_region_add_subregion(address_space_mem, 0x00000000, prom); + + ret = load_image_targphys(filename, 0x00000000, bios_size); + if (ret < 0 || ret > bios_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); + } + + + memory_region_init_io(iomem, NULL, &leon_io_ops, s, "leon_io", 0x1000); + memory_region_add_subregion(get_system_memory(), 0x80000000, iomem); + + leon_timer_init(&s->timer1, cpu_irqs[8], 1 /* id */); + leon_timer_init(&s->timer2, cpu_irqs[9], 2 /* id */); + + if (serial_hd(0)) { + leon_uart_init(serial_hd(0), &s->uart1, cpu_irqs[3]); + } + if (serial_hd(1)) { + leon_uart_init(serial_hd(1), &s->uart2, cpu_irqs[2]); + } + + /* HostFS */ + hostfs_create(0x80001000, get_system_memory()); + + /* Initialize the GnatBus Master */ + gnatbus_master_init(cpu_irqs, MAX_PILS); + gnatbus_device_init(); + + /* Can directly load an application. */ + if (kernel_filename != NULL) { + long kernel_size; + uint64_t entry; + + kernel_size = load_elf(kernel_filename, NULL, NULL, NULL, + &entry, NULL, NULL, NULL, + 1 /* big endian */, EM_SPARC, 0, 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; + } + } +} + +static void at697_generic_machine_init(MachineClass *mc) +{ + mc->desc = "Leon-2 Atmel 697"; + mc->init = at697_hw_init; + mc->default_cpu_type = SPARC_CPU_TYPE_NAME("LEON2"); +} + +DEFINE_MACHINE("at697", at697_generic_machine_init) diff --git a/hw/sparc/meson.build b/hw/sparc/meson.build index 19c442c90d9b1..13d90572a5755 100644 --- a/hw/sparc/meson.build +++ b/hw/sparc/meson.build @@ -2,5 +2,6 @@ sparc_ss = ss.source_set() sparc_ss.add(when: 'CONFIG_LEON3', if_true: files('leon3.c')) sparc_ss.add(when: 'CONFIG_SUN4M', if_true: files('sun4m.c')) sparc_ss.add(when: 'CONFIG_SUN4M', if_true: files('sun4m_iommu.c')) +sparc_ss.add(files('leon.c')) hw_arch += {'sparc': sparc_ss} diff --git a/hw/sparc/trace-events b/hw/sparc/trace-events index 00b0212c3bdb4..eed4a010a0388 100644 --- a/hw/sparc/trace-events +++ b/hw/sparc/trace-events @@ -19,3 +19,16 @@ leon3_set_irq(int intno) "Set CPU IRQ %d" leon3_reset_irq(int intno) "Reset CPU IRQ %d" int_helper_icache_freeze(void) "Instruction cache: freeze" int_helper_dcache_freeze(void) "Data cache: freeze" + +# hw/leon.c +leon_check_irqs(uint32_t pend, uint32_t force, uint32_t mask, uint32_t lvl1, uint32_t lvl2) "pend:0x%04x force:0x%04x mask:0x%04x lvl1:0x%04x lvl0:0x%04x" +leon_set_irq(int intno) "Set CPU IRQ %d" +leon_reset_irq(int intno) "Reset CPU IRQ %d" +leon_intctl_ack(int intno) "interrupt:%d" +leon_uart_event(int event) "uart event:%d" +leon_timer_disabled(int id, uint32_t config) "timer:%d Timer disable config 0x%x" +leon_timer_enable(int id, uint32_t count) "timer:%d set count 0x%x and run" +leon_timer_hit(int id) "timer:%d HIT" +leon_unknown_register(const char *op, uint64_t val) "%s unknown register 0x%"PRIx64"" +leon_readl(uint64_t addr, uint32_t val) "addr:0x%"PRIx64" val:0x%x" +leon_writel(uint64_t addr, uint32_t val) "addr:0x%"PRIx64" val:0x%x" diff --git a/target/sparc/cpu.c b/target/sparc/cpu.c index 55268ed2a19e1..3dd1b11537b0a 100644 --- a/target/sparc/cpu.c +++ b/target/sparc/cpu.c @@ -516,7 +516,7 @@ static const sparc_def_t sparc_defs[] = { .iu_version = 0xf2000000, .fpu_version = 4 << 17, /* FPU version 4 (Meiko) */ .mmu_version = 0xf2000000, - .mmu_bm = 0x00004000, + .mmu_bm = 0x00000000, .mmu_ctpr_mask = 0x007ffff0, .mmu_cxr_mask = 0x0000003f, .mmu_sfsr_mask = 0xffffffff, From 02363c265fa58f51fa27bf843ae155cd53c62acc Mon Sep 17 00:00:00 2001 From: Yasushi SHOJI Date: Thu, 5 Sep 2019 11:06:56 +0900 Subject: [PATCH 3/6] hw/sparc/leon: Fix compilation errors The previous commit, taken from AdaCore repository as is for future reference, depends on other commit in the AdaCore repository and fails to build. This commit is to port it to the vanilla QEmu. What it does is 4 folds: - Remove AdaCore specific headers and function calls. - Add necessarily headers to build. - Use env_cpu instead of CPU() as per 29a0af618ddd21f55df5753c3e16b0625f534b3c. - Update for intctl_ack API change introduced by 7922703623a989b59ce7f7b57a3c8ebe5c0f6b53. Signed-off-by: Yasushi SHOJI --- hw/sparc/leon.c | 19 +++++++------------ 1 file changed, 7 insertions(+), 12 deletions(-) diff --git a/hw/sparc/leon.c b/hw/sparc/leon.c index 316bd7028836e..cab394ba4b836 100644 --- a/hw/sparc/leon.c +++ b/hw/sparc/leon.c @@ -22,12 +22,16 @@ * THE SOFTWARE. */ #include "qemu/osdep.h" +#include "qemu-common.h" #include "qapi/error.h" #include "cpu.h" #include "hw/hw.h" +#include "hw/irq.h" #include "qemu/timer.h" #include "chardev/char.h" +#include "chardev/char-fe.h" #include "sysemu/sysemu.h" +#include "sysemu/reset.h" #include "hw/boards.h" #include "hw/loader.h" #include "elf.h" @@ -35,8 +39,6 @@ #include "hw/ptimer.h" #include "exec/memory.h" #include "exec/address-spaces.h" -#include "hw/adacore/gnat-bus.h" -#include "hw/adacore/hostfs.h" /* Default system clock. */ #define CPU_CLK (50 * 1000 * 1000) @@ -220,7 +222,7 @@ static void leon_check_irqs(struct LeonIntState *s) uint32_t level0 = 0; uint32_t level1 = 0; CPUSPARCState *env = s->env; - CPUState *cs = CPU(sparc_env_get_cpu(env)); + CPUState *cs = env_cpu(env); pend = (s->pending | s->force) & (s->lvl_mask & 0xfffe); @@ -260,9 +262,9 @@ static void leon_check_irqs(struct LeonIntState *s) } } -static void leon2_intctl_ack(CPUSPARCState *env, int intno) +static void leon2_intctl_ack(CPUSPARCState *env, void *irq_manager, int intno) { - struct LeonIntState *intctl = env->irq_manager; + struct LeonIntState *intctl = (struct LeonIntState *)irq_manager; uint32_t mask; uint32_t state = 0; @@ -839,13 +841,6 @@ static void at697_hw_init(MachineState *machine) leon_uart_init(serial_hd(1), &s->uart2, cpu_irqs[2]); } - /* HostFS */ - hostfs_create(0x80001000, get_system_memory()); - - /* Initialize the GnatBus Master */ - gnatbus_master_init(cpu_irqs, MAX_PILS); - gnatbus_device_init(); - /* Can directly load an application. */ if (kernel_filename != NULL) { long kernel_size; From f479dbfb8e03be3d3904ca27c0179f82ff11bfb5 Mon Sep 17 00:00:00 2001 From: Yasushi SHOJI Date: Fri, 17 May 2019 21:42:30 +0900 Subject: [PATCH 4/6] hw/sparc/leon: timer: Call leon_timer_io_read() for TIMER_1_COUNTER_REGISTER Accessing timer1 counter register (offset 0x40) always returned 0 because the address range check for leon_timer_io_read() was insufficient. Add TIMER_1_COUNTER_REGISTER at the case statement. We don't need to add TIMER_1_COUNTER_REGISTER for leon_timer_io_write() because timer1 counter register isn't writable. But, this commit also changes the range check for leon_timer_io_write() as well for consistency. The given address is checked in leon_timer_io_write() again, thus the change is effectively no-op for non-writable registers. Signed-off-by: Yasushi SHOJI --- hw/sparc/leon.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/hw/sparc/leon.c b/hw/sparc/leon.c index cab394ba4b836..d2dc8e55c82e6 100644 --- a/hw/sparc/leon.c +++ b/hw/sparc/leon.c @@ -616,7 +616,7 @@ static uint64_t leon_io_read(void *opaque, hwaddr addr, ret = s->uart2.status; break; - case TIMER_1_RELOAD_REGISTER ... PRESCALER_RELOAD_REGISTER: + case TIMER_1_COUNTER_REGISTER ... PRESCALER_RELOAD_REGISTER: ret = leon_timer_io_read(s, addr); break; @@ -713,7 +713,7 @@ static void leon_io_write(void *opaque, hwaddr addr, } break; - case TIMER_1_RELOAD_REGISTER ... PRESCALER_RELOAD_REGISTER: + case TIMER_1_COUNTER_REGISTER ... PRESCALER_RELOAD_REGISTER: leon_timer_io_write(s, addr, value); break; From bcdbfd0c6ff3261b45a3228fd25b248aabb04acf Mon Sep 17 00:00:00 2001 From: Yasushi SHOJI Date: Mon, 9 Dec 2019 11:56:20 +0900 Subject: [PATCH 5/6] hw/sparc/leon: Switch to transaction-based ptimer API Switch the leon code away from old ptimers to the new transaction-based ptimer API. This requires adding begin/commit calls around the various places that modify the ptimer state, and not calling leon_timer_enable() from leon_timer_hit() since leon_timer_hit() is called within a transcation enabled ptimer_tick(). Signed-off-by: Yasushi SHOJI --- hw/sparc/leon.c | 22 +++++++++++++++++----- 1 file changed, 17 insertions(+), 5 deletions(-) diff --git a/hw/sparc/leon.c b/hw/sparc/leon.c index d2dc8e55c82e6..384dde035b5fd 100644 --- a/hw/sparc/leon.c +++ b/hw/sparc/leon.c @@ -156,7 +156,6 @@ typedef struct LeonUartState { } LeonUartState; struct LeonTimerState { - QEMUBH *bh; struct ptimer_state *ptimer; qemu_irq irq; @@ -404,6 +403,7 @@ static void leon_uart_init(Chardev *chr, static void leon_timer_enable(struct LeonTimerState *s) { + ptimer_transaction_begin(s->ptimer); ptimer_stop(s->ptimer); if (s->control & TIMCTR_LD) { @@ -424,6 +424,7 @@ static void leon_timer_enable(struct LeonTimerState *s) ptimer_set_count(s->ptimer, s->counter + 1); ptimer_run(s->ptimer, 1); + ptimer_transaction_commit(s->ptimer); } static void leon_timer_hit(void *opaque) @@ -437,7 +438,10 @@ static void leon_timer_hit(void *opaque) if (s->control & TIMCTR_RL) { /* reload */ s->control |= TIMCTR_LD; - leon_timer_enable(s); + ptimer_stop(s->ptimer); + s->counter = s->reload; + ptimer_set_count(s->ptimer, s->counter + 1); + ptimer_run(s->ptimer, 1); } } @@ -455,11 +459,15 @@ static uint32_t leon_timer_io_read(void *opaque, hwaddr addr) break; case TIMER_1_COUNTER_REGISTER: + ptimer_transaction_begin(s->timer1.ptimer); ret = ptimer_get_count(s->timer1.ptimer); + ptimer_transaction_commit(s->timer1.ptimer); break; case TIMER_2_COUNTER_REGISTER: + ptimer_transaction_begin(s->timer2.ptimer); ret = ptimer_get_count(s->timer2.ptimer); + ptimer_transaction_commit(s->timer2.ptimer); break; case TIMER_1_RELOAD_REGISTER: @@ -502,8 +510,12 @@ static void leon_timer_io_write(LeonIoState *s, hwaddr addr, case PRESCALER_RELOAD_REGISTER: s->scar = val & 0x3ff; val = CPU_CLK / (s->scar + 1); + ptimer_transaction_begin(s->timer1.ptimer); ptimer_set_freq(s->timer1.ptimer, val); + ptimer_transaction_commit(s->timer1.ptimer); + ptimer_transaction_begin(s->timer2.ptimer); ptimer_set_freq(s->timer2.ptimer, val); + ptimer_transaction_commit(s->timer2.ptimer); break; case TIMER_1_COUNTER_REGISTER: @@ -549,11 +561,11 @@ static void leon_timer_init(struct LeonTimerState *s, qemu_irq irq, int id) s->reload = 0; s->control = 0; s->irq = irq; - s->bh = qemu_bh_new(leon_timer_hit, s); - s->ptimer = ptimer_init(s->bh, PTIMER_POLICY_DEFAULT); + s->ptimer = ptimer_init(leon_timer_hit, s, PTIMER_POLICY_DEFAULT); + ptimer_transaction_begin(s->ptimer); ptimer_set_freq(s->ptimer, CPU_CLK); - + ptimer_transaction_commit(s->ptimer); } static uint64_t leon_io_read(void *opaque, hwaddr addr, From bd937910d4f8ba38df5242290dfb45596c18abbc Mon Sep 17 00:00:00 2001 From: Kumar Gala Date: Thu, 25 Mar 2021 18:59:10 -0500 Subject: [PATCH 6/6] hw/sparc/leon: Fix compilation errors with qemu 6 changes Make updates to build due to upstream changes in qemu 6.0.0 Signed-off-by: Kumar Gala --- hw/sparc/leon.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/hw/sparc/leon.c b/hw/sparc/leon.c index 384dde035b5fd..55214523161ad 100644 --- a/hw/sparc/leon.c +++ b/hw/sparc/leon.c @@ -23,6 +23,7 @@ */ #include "qemu/osdep.h" #include "qemu-common.h" +#include "qemu/datadir.h" #include "qapi/error.h" #include "cpu.h" #include "hw/hw.h" @@ -757,6 +758,7 @@ static const MemoryRegionOps leon_io_ops = { static void at697_hw_init(MachineState *machine) { ram_addr_t ram_size = machine->ram_size; + const char *bios_name = machine->firmware ?: PROM_FILENAME; const char *kernel_filename = machine->kernel_filename; SPARCCPU *cpu; CPUSPARCState *env; @@ -815,9 +817,6 @@ static void at697_hw_init(MachineState *machine) memory_region_add_subregion(address_space_mem, 0x20000000, ram2); /* 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 > 0) {