Merge remote-tracking branch 'pmaydell/tags/pull-target-arm-20131217' into staging

target-arm queue:
 * AES instruction support for 32 bit ARM
 * pflash01: much better emulation of 2x16bit and similar configs
   where multiple flash devices are banked together
 * fixed CBAR handling on Zynq, Highbank
 * initial AArch64 KVM control support
 * first two chunks of patches for A64 instruction emulation
 * new board: canon-a1100 (Canon DIGIC SoC)
 * new board: cubieboard (Allwinner A10 SoC)

# gpg: Signature made Tue 17 Dec 2013 12:18:39 PM PST using RSA key ID 14360CDE
# gpg: Can't check signature: public key not found

# By Alexander Graf (14) and others
# Via Peter Maydell
* pmaydell/tags/pull-target-arm-20131217: (62 commits)
  MAINTAINERS: add myself to maintain allwinner-a10
  hw/arm: add cubieboard support
  hw/arm: add allwinner a10 SoC support
  hw/intc: add allwinner A10 interrupt controller
  hw/timer: add allwinner a10 timer
  vmstate: Add support for an array of ptimer_state *
  MAINTAINERS: Document 'Canon DIGIC' machine
  hw/arm/digic: add NOR ROM support
  hw/arm/digic: add UART support
  hw/arm/digic: add timer support
  hw/arm/digic: prepare DIGIC-based boards support
  hw/arm: add very initial support for Canon DIGIC SoC
  target-arm: A64: add support for logical (immediate) insns
  target-arm: A64: add support for 1-src CLS insn
  host-utils: add clrsb32/64 - count leading redundant sign bits
  target-arm: A64: add support for bitfield insns
  target-arm: A64: add support for 1-src REV insns
  target-arm: A64: add support for 1-src RBIT insn
  target-arm: A64: add support for 1-src data processing and CLZ
  target-arm: A64: add support for 2-src shift reg insns
  ...

Message-id: 1387312160-12318-1-git-send-email-peter.maydell@linaro.org
Signed-off-by: Anthony Liguori <aliguori@amazon.com>
This commit is contained in:
Anthony Liguori
2013-12-19 11:56:33 -08:00
50 changed files with 5229 additions and 694 deletions
+13
View File
@@ -219,6 +219,13 @@ F: *win32*
ARM Machines
------------
Allwinner-a10
M: Li Guang <lig.fnst@cn.fujitsu.com>
S: Maintained
F: hw/*/allwinner-a10*
F: include/hw/*/allwinner-a10*
F: hw/arm/cubieboard.c
Exynos
M: Evgeny Voevodin <e.voevodin@samsung.com>
M: Maksim Kozlov <m.kozlov@samsung.com>
@@ -233,6 +240,12 @@ S: Supported
F: hw/arm/highbank.c
F: hw/net/xgmac.c
Canon DIGIC
M: Antony Pavlov <antonynpavlov@gmail.com>
S: Maintained
F: include/hw/arm/digic.h
F: hw/*/digic*
Gumstix
M: qemu-devel@nongnu.org
S: Orphan
Vendored
+2 -2
View File
@@ -4438,7 +4438,7 @@ case "$target_name" in
aarch64)
TARGET_BASE_ARCH=arm
bflt="yes"
gdb_xml_files="aarch64-core.xml"
gdb_xml_files="aarch64-core.xml aarch64-fpu.xml"
;;
cris)
;;
@@ -4550,7 +4550,7 @@ case "$target_name" in
*)
esac
case "$target_name" in
arm|i386|x86_64|ppcemb|ppc|ppc64|s390x)
aarch64|arm|i386|x86_64|ppcemb|ppc|ppc64|s390x)
# Make sure the target and host cpus are compatible
if test "$kvm" = "yes" -a "$target_softmmu" = "yes" -a \
\( "$target_name" = "$cpu" -o \
+6
View File
@@ -0,0 +1,6 @@
# Default configuration for aarch64-softmmu
# We support all the 32 bit boards so need all their config
include arm-softmmu.mak
# Currently no 64-bit specific config requirements
+5
View File
@@ -64,6 +64,7 @@ CONFIG_XILINX_SPIPS=y
CONFIG_ARM11SCU=y
CONFIG_A9SCU=y
CONFIG_DIGIC=y
CONFIG_MARVELL_88W8618=y
CONFIG_OMAP=y
CONFIG_TSC210X=y
@@ -82,3 +83,7 @@ CONFIG_VERSATILE_I2C=y
CONFIG_SDHCI=y
CONFIG_INTEGRATOR_DEBUG=y
CONFIG_ALLWINNER_A10_PIT=y
CONFIG_ALLWINNER_A10_PIC=y
CONFIG_ALLWINNER_A10=y
+86
View File
@@ -0,0 +1,86 @@
<?xml version="1.0"?>
<!-- Copyright (C) 2009-2012 Free Software Foundation, Inc.
Contributed by ARM Ltd.
Copying and distribution of this file, with or without modification,
are permitted in any medium without royalty provided the copyright
notice and this notice are preserved. -->
<!DOCTYPE feature SYSTEM "gdb-target.dtd">
<feature name="org.gnu.gdb.aarch64.fpu">
<vector id="v2d" type="ieee_double" count="2"/>
<vector id="v2u" type="uint64" count="2"/>
<vector id="v2i" type="int64" count="2"/>
<vector id="v4f" type="ieee_single" count="4"/>
<vector id="v4u" type="uint32" count="4"/>
<vector id="v4i" type="int32" count="4"/>
<vector id="v8u" type="uint16" count="8"/>
<vector id="v8i" type="int16" count="8"/>
<vector id="v16u" type="uint8" count="16"/>
<vector id="v16i" type="int8" count="16"/>
<vector id="v1u" type="uint128" count="1"/>
<vector id="v1i" type="int128" count="1"/>
<union id="vnd">
<field name="f" type="v2d"/>
<field name="u" type="v2u"/>
<field name="s" type="v2i"/>
</union>
<union id="vns">
<field name="f" type="v4f"/>
<field name="u" type="v4u"/>
<field name="s" type="v4i"/>
</union>
<union id="vnh">
<field name="u" type="v8u"/>
<field name="s" type="v8i"/>
</union>
<union id="vnb">
<field name="u" type="v16u"/>
<field name="s" type="v16i"/>
</union>
<union id="vnq">
<field name="u" type="v1u"/>
<field name="s" type="v1i"/>
</union>
<union id="aarch64v">
<field name="d" type="vnd"/>
<field name="s" type="vns"/>
<field name="h" type="vnh"/>
<field name="b" type="vnb"/>
<field name="q" type="vnq"/>
</union>
<reg name="v0" bitsize="128" type="aarch64v" regnum="34"/>
<reg name="v1" bitsize="128" type="aarch64v" />
<reg name="v2" bitsize="128" type="aarch64v" />
<reg name="v3" bitsize="128" type="aarch64v" />
<reg name="v4" bitsize="128" type="aarch64v" />
<reg name="v5" bitsize="128" type="aarch64v" />
<reg name="v6" bitsize="128" type="aarch64v" />
<reg name="v7" bitsize="128" type="aarch64v" />
<reg name="v8" bitsize="128" type="aarch64v" />
<reg name="v9" bitsize="128" type="aarch64v" />
<reg name="v10" bitsize="128" type="aarch64v"/>
<reg name="v11" bitsize="128" type="aarch64v"/>
<reg name="v12" bitsize="128" type="aarch64v"/>
<reg name="v13" bitsize="128" type="aarch64v"/>
<reg name="v14" bitsize="128" type="aarch64v"/>
<reg name="v15" bitsize="128" type="aarch64v"/>
<reg name="v16" bitsize="128" type="aarch64v"/>
<reg name="v17" bitsize="128" type="aarch64v"/>
<reg name="v18" bitsize="128" type="aarch64v"/>
<reg name="v19" bitsize="128" type="aarch64v"/>
<reg name="v20" bitsize="128" type="aarch64v"/>
<reg name="v21" bitsize="128" type="aarch64v"/>
<reg name="v22" bitsize="128" type="aarch64v"/>
<reg name="v23" bitsize="128" type="aarch64v"/>
<reg name="v24" bitsize="128" type="aarch64v"/>
<reg name="v25" bitsize="128" type="aarch64v"/>
<reg name="v26" bitsize="128" type="aarch64v"/>
<reg name="v27" bitsize="128" type="aarch64v"/>
<reg name="v28" bitsize="128" type="aarch64v"/>
<reg name="v29" bitsize="128" type="aarch64v"/>
<reg name="v30" bitsize="128" type="aarch64v"/>
<reg name="v31" bitsize="128" type="aarch64v"/>
<reg name="fpsr" bitsize="32"/>
<reg name="fpcr" bitsize="32"/>
</feature>
+3
View File
@@ -1,7 +1,10 @@
obj-y += boot.o collie.o exynos4_boards.o gumstix.o highbank.o
obj-$(CONFIG_DIGIC) += digic_boards.o
obj-y += integratorcp.o kzm.o mainstone.o musicpal.o nseries.o
obj-y += omap_sx1.o palm.o realview.o spitz.o stellaris.o
obj-y += tosa.o versatilepb.o vexpress.o virt.o xilinx_zynq.o z2.o
obj-y += armv7m.o exynos4210.o pxa2xx.o pxa2xx_gpio.o pxa2xx_pic.o
obj-$(CONFIG_DIGIC) += digic.o
obj-y += omap1.o omap2.o strongarm.o
obj-$(CONFIG_ALLWINNER_A10) += allwinner-a10.o cubieboard.o
+103
View File
@@ -0,0 +1,103 @@
/*
* Allwinner A10 SoC emulation
*
* Copyright (C) 2013 Li Guang
* Written by Li Guang <lig.fnst@cn.fujitsu.com>
*
* 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.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* for more details.
*/
#include "hw/sysbus.h"
#include "hw/devices.h"
#include "hw/arm/allwinner-a10.h"
static void aw_a10_init(Object *obj)
{
AwA10State *s = AW_A10(obj);
object_initialize(&s->cpu, sizeof(s->cpu), "cortex-a8-" TYPE_ARM_CPU);
object_property_add_child(obj, "cpu", OBJECT(&s->cpu), NULL);
object_initialize(&s->intc, sizeof(s->intc), TYPE_AW_A10_PIC);
qdev_set_parent_bus(DEVICE(&s->intc), sysbus_get_default());
object_initialize(&s->timer, sizeof(s->timer), TYPE_AW_A10_PIT);
qdev_set_parent_bus(DEVICE(&s->timer), sysbus_get_default());
}
static void aw_a10_realize(DeviceState *dev, Error **errp)
{
AwA10State *s = AW_A10(dev);
SysBusDevice *sysbusdev;
uint8_t i;
qemu_irq fiq, irq;
Error *err = NULL;
object_property_set_bool(OBJECT(&s->cpu), true, "realized", &err);
if (err != NULL) {
error_propagate(errp, err);
return;
}
irq = qdev_get_gpio_in(DEVICE(&s->cpu), ARM_CPU_IRQ);
fiq = qdev_get_gpio_in(DEVICE(&s->cpu), ARM_CPU_FIQ);
object_property_set_bool(OBJECT(&s->intc), true, "realized", &err);
if (err != NULL) {
error_propagate(errp, err);
return;
}
sysbusdev = SYS_BUS_DEVICE(&s->intc);
sysbus_mmio_map(sysbusdev, 0, AW_A10_PIC_REG_BASE);
sysbus_connect_irq(sysbusdev, 0, irq);
sysbus_connect_irq(sysbusdev, 1, fiq);
for (i = 0; i < AW_A10_PIC_INT_NR; i++) {
s->irq[i] = qdev_get_gpio_in(DEVICE(&s->intc), i);
}
object_property_set_bool(OBJECT(&s->timer), true, "realized", &err);
if (err != NULL) {
error_propagate(errp, err);
return;
}
sysbusdev = SYS_BUS_DEVICE(&s->timer);
sysbus_mmio_map(sysbusdev, 0, AW_A10_PIT_REG_BASE);
sysbus_connect_irq(sysbusdev, 0, s->irq[22]);
sysbus_connect_irq(sysbusdev, 1, s->irq[23]);
sysbus_connect_irq(sysbusdev, 2, s->irq[24]);
sysbus_connect_irq(sysbusdev, 3, s->irq[25]);
sysbus_connect_irq(sysbusdev, 4, s->irq[67]);
sysbus_connect_irq(sysbusdev, 5, s->irq[68]);
serial_mm_init(get_system_memory(), AW_A10_UART0_REG_BASE, 2, s->irq[1],
115200, serial_hds[0], DEVICE_NATIVE_ENDIAN);
}
static void aw_a10_class_init(ObjectClass *oc, void *data)
{
DeviceClass *dc = DEVICE_CLASS(oc);
dc->realize = aw_a10_realize;
}
static const TypeInfo aw_a10_type_info = {
.name = TYPE_AW_A10,
.parent = TYPE_DEVICE,
.instance_size = sizeof(AwA10State),
.instance_init = aw_a10_init,
.class_init = aw_a10_class_init,
};
static void aw_a10_register_types(void)
{
type_register_static(&aw_a10_type_info);
}
type_init(aw_a10_register_types)
+144 -49
View File
@@ -17,18 +17,55 @@
#include "sysemu/device_tree.h"
#include "qemu/config-file.h"
/* Kernel boot protocol is specified in the kernel docs
* Documentation/arm/Booting and Documentation/arm64/booting.txt
* They have different preferred image load offsets from system RAM base.
*/
#define KERNEL_ARGS_ADDR 0x100
#define KERNEL_LOAD_ADDR 0x00010000
#define KERNEL64_LOAD_ADDR 0x00080000
typedef enum {
FIXUP_NONE = 0, /* do nothing */
FIXUP_TERMINATOR, /* end of insns */
FIXUP_BOARDID, /* overwrite with board ID number */
FIXUP_ARGPTR, /* overwrite with pointer to kernel args */
FIXUP_ENTRYPOINT, /* overwrite with kernel entry point */
FIXUP_GIC_CPU_IF, /* overwrite with GIC CPU interface address */
FIXUP_BOOTREG, /* overwrite with boot register address */
FIXUP_DSB, /* overwrite with correct DSB insn for cpu */
FIXUP_MAX,
} FixupType;
typedef struct ARMInsnFixup {
uint32_t insn;
FixupType fixup;
} ARMInsnFixup;
static const ARMInsnFixup bootloader_aarch64[] = {
{ 0x580000c0 }, /* ldr x0, arg ; Load the lower 32-bits of DTB */
{ 0xaa1f03e1 }, /* mov x1, xzr */
{ 0xaa1f03e2 }, /* mov x2, xzr */
{ 0xaa1f03e3 }, /* mov x3, xzr */
{ 0x58000084 }, /* ldr x4, entry ; Load the lower 32-bits of kernel entry */
{ 0xd61f0080 }, /* br x4 ; Jump to the kernel entry point */
{ 0, FIXUP_ARGPTR }, /* arg: .word @DTB Lower 32-bits */
{ 0 }, /* .word @DTB Higher 32-bits */
{ 0, FIXUP_ENTRYPOINT }, /* entry: .word @Kernel Entry Lower 32-bits */
{ 0 }, /* .word @Kernel Entry Higher 32-bits */
{ 0, FIXUP_TERMINATOR }
};
/* The worlds second smallest bootloader. Set r0-r2, then jump to kernel. */
static uint32_t bootloader[] = {
0xe3a00000, /* mov r0, #0 */
0xe59f1004, /* ldr r1, [pc, #4] */
0xe59f2004, /* ldr r2, [pc, #4] */
0xe59ff004, /* ldr pc, [pc, #4] */
0, /* Board ID */
0, /* Address of kernel args. Set by integratorcp_init. */
0 /* Kernel entry point. Set by integratorcp_init. */
static const ARMInsnFixup bootloader[] = {
{ 0xe3a00000 }, /* mov r0, #0 */
{ 0xe59f1004 }, /* ldr r1, [pc, #4] */
{ 0xe59f2004 }, /* ldr r2, [pc, #4] */
{ 0xe59ff004 }, /* ldr pc, [pc, #4] */
{ 0, FIXUP_BOARDID },
{ 0, FIXUP_ARGPTR },
{ 0, FIXUP_ENTRYPOINT },
{ 0, FIXUP_TERMINATOR }
};
/* Handling for secondary CPU boot in a multicore system.
@@ -48,39 +85,83 @@ static uint32_t bootloader[] = {
#define DSB_INSN 0xf57ff04f
#define CP15_DSB_INSN 0xee070f9a /* mcr cp15, 0, r0, c7, c10, 4 */
static uint32_t smpboot[] = {
0xe59f2028, /* ldr r2, gic_cpu_if */
0xe59f0028, /* ldr r0, startaddr */
0xe3a01001, /* mov r1, #1 */
0xe5821000, /* str r1, [r2] - set GICC_CTLR.Enable */
0xe3a010ff, /* mov r1, #0xff */
0xe5821004, /* str r1, [r2, 4] - set GIC_PMR.Priority to 0xff */
DSB_INSN, /* dsb */
0xe320f003, /* wfi */
0xe5901000, /* ldr r1, [r0] */
0xe1110001, /* tst r1, r1 */
0x0afffffb, /* beq <wfi> */
0xe12fff11, /* bx r1 */
0, /* gic_cpu_if: base address of GIC CPU interface */
0 /* bootreg: Boot register address is held here */
static const ARMInsnFixup smpboot[] = {
{ 0xe59f2028 }, /* ldr r2, gic_cpu_if */
{ 0xe59f0028 }, /* ldr r0, bootreg_addr */
{ 0xe3a01001 }, /* mov r1, #1 */
{ 0xe5821000 }, /* str r1, [r2] - set GICC_CTLR.Enable */
{ 0xe3a010ff }, /* mov r1, #0xff */
{ 0xe5821004 }, /* str r1, [r2, 4] - set GIC_PMR.Priority to 0xff */
{ 0, FIXUP_DSB }, /* dsb */
{ 0xe320f003 }, /* wfi */
{ 0xe5901000 }, /* ldr r1, [r0] */
{ 0xe1110001 }, /* tst r1, r1 */
{ 0x0afffffb }, /* beq <wfi> */
{ 0xe12fff11 }, /* bx r1 */
{ 0, FIXUP_GIC_CPU_IF }, /* gic_cpu_if: .word 0x.... */
{ 0, FIXUP_BOOTREG }, /* bootreg_addr: .word 0x.... */
{ 0, FIXUP_TERMINATOR }
};
static void write_bootloader(const char *name, hwaddr addr,
const ARMInsnFixup *insns, uint32_t *fixupcontext)
{
/* Fix up the specified bootloader fragment and write it into
* guest memory using rom_add_blob_fixed(). fixupcontext is
* an array giving the values to write in for the fixup types
* which write a value into the code array.
*/
int i, len;
uint32_t *code;
len = 0;
while (insns[len].fixup != FIXUP_TERMINATOR) {
len++;
}
code = g_new0(uint32_t, len);
for (i = 0; i < len; i++) {
uint32_t insn = insns[i].insn;
FixupType fixup = insns[i].fixup;
switch (fixup) {
case FIXUP_NONE:
break;
case FIXUP_BOARDID:
case FIXUP_ARGPTR:
case FIXUP_ENTRYPOINT:
case FIXUP_GIC_CPU_IF:
case FIXUP_BOOTREG:
case FIXUP_DSB:
insn = fixupcontext[fixup];
break;
default:
abort();
}
code[i] = tswap32(insn);
}
rom_add_blob_fixed(name, code, len * sizeof(uint32_t), addr);
g_free(code);
}
static void default_write_secondary(ARMCPU *cpu,
const struct arm_boot_info *info)
{
int n;
smpboot[ARRAY_SIZE(smpboot) - 1] = info->smp_bootreg_addr;
smpboot[ARRAY_SIZE(smpboot) - 2] = info->gic_cpu_if_addr;
for (n = 0; n < ARRAY_SIZE(smpboot); n++) {
/* Replace DSB with the pre-v7 DSB if necessary. */
if (!arm_feature(&cpu->env, ARM_FEATURE_V7) &&
smpboot[n] == DSB_INSN) {
smpboot[n] = CP15_DSB_INSN;
}
smpboot[n] = tswap32(smpboot[n]);
uint32_t fixupcontext[FIXUP_MAX];
fixupcontext[FIXUP_GIC_CPU_IF] = info->gic_cpu_if_addr;
fixupcontext[FIXUP_BOOTREG] = info->smp_bootreg_addr;
if (arm_feature(&cpu->env, ARM_FEATURE_V7)) {
fixupcontext[FIXUP_DSB] = DSB_INSN;
} else {
fixupcontext[FIXUP_DSB] = CP15_DSB_INSN;
}
rom_add_blob_fixed("smpboot", smpboot, sizeof(smpboot),
info->smp_loader_start);
write_bootloader("smpboot", info->smp_loader_start,
smpboot, fixupcontext);
}
static void default_reset_secondary(ARMCPU *cpu,
@@ -334,7 +415,12 @@ static void do_cpu_reset(void *opaque)
env->thumb = info->entry & 1;
} else {
if (CPU(cpu) == first_cpu) {
env->regs[15] = info->loader_start;
if (env->aarch64) {
env->pc = info->loader_start;
} else {
env->regs[15] = info->loader_start;
}
if (!info->dtb_filename) {
if (old_param) {
set_kernel_args_old(info);
@@ -354,11 +440,11 @@ void arm_load_kernel(ARMCPU *cpu, struct arm_boot_info *info)
CPUState *cs = CPU(cpu);
int kernel_size;
int initrd_size;
int n;
int is_linux = 0;
uint64_t elf_entry;
hwaddr entry;
hwaddr entry, kernel_load_offset;
int big_endian;
static const ARMInsnFixup *primary_loader;
/* Load the kernel. */
if (!info->kernel_filename) {
@@ -368,6 +454,14 @@ void arm_load_kernel(ARMCPU *cpu, struct arm_boot_info *info)
return;
}
if (arm_feature(&cpu->env, ARM_FEATURE_AARCH64)) {
primary_loader = bootloader_aarch64;
kernel_load_offset = KERNEL64_LOAD_ADDR;
} else {
primary_loader = bootloader;
kernel_load_offset = KERNEL_LOAD_ADDR;
}
info->dtb_filename = qemu_opt_get(qemu_get_machine_opts(), "dtb");
if (!info->secondary_cpu_reset_hook) {
@@ -408,9 +502,9 @@ void arm_load_kernel(ARMCPU *cpu, struct arm_boot_info *info)
&is_linux);
}
if (kernel_size < 0) {
entry = info->loader_start + KERNEL_LOAD_ADDR;
entry = info->loader_start + kernel_load_offset;
kernel_size = load_image_targphys(info->kernel_filename, entry,
info->ram_size - KERNEL_LOAD_ADDR);
info->ram_size - kernel_load_offset);
is_linux = 1;
}
if (kernel_size < 0) {
@@ -420,6 +514,8 @@ void arm_load_kernel(ARMCPU *cpu, struct arm_boot_info *info)
}
info->entry = entry;
if (is_linux) {
uint32_t fixupcontext[FIXUP_MAX];
if (info->initrd_filename) {
initrd_size = load_ramdisk(info->initrd_filename,
info->initrd_start,
@@ -441,7 +537,7 @@ void arm_load_kernel(ARMCPU *cpu, struct arm_boot_info *info)
}
info->initrd_size = initrd_size;
bootloader[4] = info->board_id;
fixupcontext[FIXUP_BOARDID] = info->board_id;
/* for device tree boot, we pass the DTB directly in r2. Otherwise
* we point to the kernel args.
@@ -456,9 +552,9 @@ void arm_load_kernel(ARMCPU *cpu, struct arm_boot_info *info)
if (load_dtb(dtb_start, info)) {
exit(1);
}
bootloader[5] = dtb_start;
fixupcontext[FIXUP_ARGPTR] = dtb_start;
} else {
bootloader[5] = info->loader_start + KERNEL_ARGS_ADDR;
fixupcontext[FIXUP_ARGPTR] = info->loader_start + KERNEL_ARGS_ADDR;
if (info->ram_size >= (1ULL << 32)) {
fprintf(stderr, "qemu: RAM size must be less than 4GB to boot"
" Linux kernel using ATAGS (try passing a device tree"
@@ -466,12 +562,11 @@ void arm_load_kernel(ARMCPU *cpu, struct arm_boot_info *info)
exit(1);
}
}
bootloader[6] = entry;
for (n = 0; n < sizeof(bootloader) / 4; n++) {
bootloader[n] = tswap32(bootloader[n]);
}
rom_add_blob_fixed("bootloader", bootloader, sizeof(bootloader),
info->loader_start);
fixupcontext[FIXUP_ENTRYPOINT] = entry;
write_bootloader("bootloader", info->loader_start,
primary_loader, fixupcontext);
if (info->nb_cpus > 1) {
info->write_secondary_boot(cpu, info);
}
+69
View File
@@ -0,0 +1,69 @@
/*
* cubieboard emulation
*
* Copyright (C) 2013 Li Guang
* Written by Li Guang <lig.fnst@cn.fujitsu.com>
*
* 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.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* for more details.
*/
#include "hw/sysbus.h"
#include "hw/devices.h"
#include "hw/boards.h"
#include "hw/arm/allwinner-a10.h"
static struct arm_boot_info cubieboard_binfo = {
.loader_start = AW_A10_SDRAM_BASE,
.board_id = 0x1008,
};
typedef struct CubieBoardState {
AwA10State *a10;
MemoryRegion sdram;
} CubieBoardState;
static void cubieboard_init(QEMUMachineInitArgs *args)
{
CubieBoardState *s = g_new(CubieBoardState, 1);
Error *err = NULL;
s->a10 = AW_A10(object_new(TYPE_AW_A10));
object_property_set_bool(OBJECT(s->a10), true, "realized", &err);
if (err != NULL) {
error_report("Couldn't realize Allwinner A10: %s\n",
error_get_pretty(err));
exit(1);
}
memory_region_init_ram(&s->sdram, NULL, "cubieboard.ram", args->ram_size);
vmstate_register_ram_global(&s->sdram);
memory_region_add_subregion(get_system_memory(), AW_A10_SDRAM_BASE,
&s->sdram);
cubieboard_binfo.ram_size = args->ram_size;
cubieboard_binfo.kernel_filename = args->kernel_filename;
cubieboard_binfo.kernel_cmdline = args->kernel_cmdline;
arm_load_kernel(&s->a10->cpu, &cubieboard_binfo);
}
static QEMUMachine cubieboard_machine = {
.name = "cubieboard",
.desc = "cubietech cubieboard",
.init = cubieboard_init,
};
static void cubieboard_machine_init(void)
{
qemu_register_machine(&cubieboard_machine);
}
machine_init(cubieboard_machine_init)
+115
View File
@@ -0,0 +1,115 @@
/*
* QEMU model of the Canon DIGIC SoC.
*
* Copyright (C) 2013 Antony Pavlov <antonynpavlov@gmail.com>
*
* This model is based on reverse engineering efforts
* made by CHDK (http://chdk.wikia.com) and
* Magic Lantern (http://www.magiclantern.fm) projects
* contributors.
*
* 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.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
*/
#include "hw/arm/digic.h"
#define DIGIC4_TIMER_BASE(n) (0xc0210000 + (n) * 0x100)
#define DIGIC_UART_BASE 0xc0800000
static void digic_init(Object *obj)
{
DigicState *s = DIGIC(obj);
DeviceState *dev;
int i;
object_initialize(&s->cpu, sizeof(s->cpu), "arm946-" TYPE_ARM_CPU);
object_property_add_child(obj, "cpu", OBJECT(&s->cpu), NULL);
for (i = 0; i < DIGIC4_NB_TIMERS; i++) {
#define DIGIC_TIMER_NAME_MLEN 11
char name[DIGIC_TIMER_NAME_MLEN];
object_initialize(&s->timer[i], sizeof(s->timer[i]), TYPE_DIGIC_TIMER);
dev = DEVICE(&s->timer[i]);
qdev_set_parent_bus(dev, sysbus_get_default());
snprintf(name, DIGIC_TIMER_NAME_MLEN, "timer[%d]", i);
object_property_add_child(obj, name, OBJECT(&s->timer[i]), NULL);
}
object_initialize(&s->uart, sizeof(s->uart), TYPE_DIGIC_UART);
dev = DEVICE(&s->uart);
qdev_set_parent_bus(dev, sysbus_get_default());
object_property_add_child(obj, "uart", OBJECT(&s->uart), NULL);
}
static void digic_realize(DeviceState *dev, Error **errp)
{
DigicState *s = DIGIC(dev);
Error *err = NULL;
SysBusDevice *sbd;
int i;
object_property_set_bool(OBJECT(&s->cpu), true, "reset-hivecs", &err);
if (err != NULL) {
error_propagate(errp, err);
return;
}
object_property_set_bool(OBJECT(&s->cpu), true, "realized", &err);
if (err != NULL) {
error_propagate(errp, err);
return;
}
for (i = 0; i < DIGIC4_NB_TIMERS; i++) {
object_property_set_bool(OBJECT(&s->timer[i]), true, "realized", &err);
if (err != NULL) {
error_propagate(errp, err);
return;
}
sbd = SYS_BUS_DEVICE(&s->timer[i]);
sysbus_mmio_map(sbd, 0, DIGIC4_TIMER_BASE(i));
}
object_property_set_bool(OBJECT(&s->uart), true, "realized", &err);
if (err != NULL) {
error_propagate(errp, err);
return;
}
sbd = SYS_BUS_DEVICE(&s->uart);
sysbus_mmio_map(sbd, 0, DIGIC_UART_BASE);
}
static void digic_class_init(ObjectClass *oc, void *data)
{
DeviceClass *dc = DEVICE_CLASS(oc);
dc->realize = digic_realize;
}
static const TypeInfo digic_type_info = {
.name = TYPE_DIGIC,
.parent = TYPE_DEVICE,
.instance_size = sizeof(DigicState),
.instance_init = digic_init,
.class_init = digic_class_init,
};
static void digic_register_types(void)
{
type_register_static(&digic_type_info);
}
type_init(digic_register_types)
+162
View File
@@ -0,0 +1,162 @@
/*
* QEMU model of the Canon DIGIC boards (cameras indeed :).
*
* Copyright (C) 2013 Antony Pavlov <antonynpavlov@gmail.com>
*
* This model is based on reverse engineering efforts
* made by CHDK (http://chdk.wikia.com) and
* Magic Lantern (http://www.magiclantern.fm) projects
* contributors.
*
* See docs here:
* http://magiclantern.wikia.com/wiki/Register_Map
*
* 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.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
*/
#include "hw/boards.h"
#include "exec/address-spaces.h"
#include "qemu/error-report.h"
#include "hw/arm/digic.h"
#include "hw/block/flash.h"
#include "hw/loader.h"
#include "sysemu/sysemu.h"
#include "sysemu/qtest.h"
#define DIGIC4_ROM0_BASE 0xf0000000
#define DIGIC4_ROM1_BASE 0xf8000000
#define DIGIC4_ROM_MAX_SIZE 0x08000000
typedef struct DigicBoardState {
DigicState *digic;
MemoryRegion ram;
} DigicBoardState;
typedef struct DigicBoard {
hwaddr ram_size;
void (*add_rom0)(DigicBoardState *, hwaddr, const char *);
const char *rom0_def_filename;
void (*add_rom1)(DigicBoardState *, hwaddr, const char *);
const char *rom1_def_filename;
} DigicBoard;
static void digic4_board_setup_ram(DigicBoardState *s, hwaddr ram_size)
{
memory_region_init_ram(&s->ram, NULL, "ram", ram_size);
memory_region_add_subregion(get_system_memory(), 0, &s->ram);
vmstate_register_ram_global(&s->ram);
}
static void digic4_board_init(DigicBoard *board)
{
Error *err = NULL;
DigicBoardState *s = g_new(DigicBoardState, 1);
s->digic = DIGIC(object_new(TYPE_DIGIC));
object_property_set_bool(OBJECT(s->digic), true, "realized", &err);
if (err != NULL) {
error_report("Couldn't realize DIGIC SoC: %s\n",
error_get_pretty(err));
exit(1);
}
digic4_board_setup_ram(s, board->ram_size);
if (board->add_rom0) {
board->add_rom0(s, DIGIC4_ROM0_BASE, board->rom0_def_filename);
}
if (board->add_rom1) {
board->add_rom1(s, DIGIC4_ROM1_BASE, board->rom1_def_filename);
}
}
static void digic_load_rom(DigicBoardState *s, hwaddr addr,
hwaddr max_size, const char *def_filename)
{
target_long rom_size;
const char *filename;
if (qtest_enabled()) {
/* qtest runs no code so don't attempt a ROM load which
* could fail and result in a spurious test failure.
*/
return;
}
if (bios_name) {
filename = bios_name;
} else {
filename = def_filename;
}
if (filename) {
char *fn = qemu_find_file(QEMU_FILE_TYPE_BIOS, filename);
if (!fn) {
error_report("Couldn't find rom image '%s'.\n", filename);
exit(1);
}
rom_size = load_image_targphys(fn, addr, max_size);
if (rom_size < 0 || rom_size > max_size) {
error_report("Couldn't load rom image '%s'.\n", filename);
exit(1);
}
}
}
/*
* Samsung K8P3215UQB
* 64M Bit (4Mx16) Page Mode / Multi-Bank NOR Flash Memory
*/
static void digic4_add_k8p3215uqb_rom(DigicBoardState *s, hwaddr addr,
const char *def_filename)
{
#define FLASH_K8P3215UQB_SIZE (4 * 1024 * 1024)
#define FLASH_K8P3215UQB_SECTOR_SIZE (64 * 1024)
pflash_cfi02_register(addr, NULL, "pflash", FLASH_K8P3215UQB_SIZE,
NULL, FLASH_K8P3215UQB_SECTOR_SIZE,
FLASH_K8P3215UQB_SIZE / FLASH_K8P3215UQB_SECTOR_SIZE,
DIGIC4_ROM_MAX_SIZE / FLASH_K8P3215UQB_SIZE,
4,
0x00EC, 0x007E, 0x0003, 0x0001,
0x0555, 0x2aa, 0);
digic_load_rom(s, addr, FLASH_K8P3215UQB_SIZE, def_filename);
}
static DigicBoard digic4_board_canon_a1100 = {
.ram_size = 64 * 1024 * 1024,
.add_rom1 = digic4_add_k8p3215uqb_rom,
.rom1_def_filename = "canon-a1100-rom1.bin",
};
static void canon_a1100_init(QEMUMachineInitArgs *args)
{
digic4_board_init(&digic4_board_canon_a1100);
}
static QEMUMachine canon_a1100 = {
.name = "canon-a1100",
.desc = "Canon PowerShot A1100 IS",
.init = &canon_a1100_init,
};
static void digic_register_machines(void)
{
qemu_register_machine(&canon_a1100);
}
machine_init(digic_register_machines)
+21 -12
View File
@@ -26,12 +26,13 @@
#include "hw/boards.h"
#include "sysemu/blockdev.h"
#include "exec/address-spaces.h"
#include "qemu/error-report.h"
#define SMP_BOOT_ADDR 0x100
#define SMP_BOOT_REG 0x40
#define GIC_BASE_ADDR 0xfff10000
#define SMP_BOOT_ADDR 0x100
#define SMP_BOOT_REG 0x40
#define MPCORE_PERIPHBASE 0xfff10000
#define NIRQ_GIC 160
#define NIRQ_GIC 160
/* Board init. */
@@ -54,7 +55,7 @@ static void hb_write_secondary(ARMCPU *cpu, const struct arm_boot_info *info)
0xe1110001, /* tst r1, r1 */
0x0afffffb, /* beq <wfi> */
0xe12fff11, /* bx r1 */
GIC_BASE_ADDR /* privbase: gic address. */
MPCORE_PERIPHBASE /* privbase: MPCore peripheral base address. */
};
for (n = 0; n < ARRAY_SIZE(smpboot); n++) {
smpboot[n] = tswap32(smpboot[n]);
@@ -229,15 +230,23 @@ static void calxeda_init(QEMUMachineInitArgs *args, enum cxmachines machine)
}
for (n = 0; n < smp_cpus; n++) {
ObjectClass *oc = cpu_class_by_name(TYPE_ARM_CPU, cpu_model);
ARMCPU *cpu;
cpu = cpu_arm_init(cpu_model);
if (cpu == NULL) {
fprintf(stderr, "Unable to find CPU definition\n");
Error *err = NULL;
cpu = ARM_CPU(object_new(object_class_get_name(oc)));
object_property_set_int(OBJECT(cpu), MPCORE_PERIPHBASE, "reset-cbar",
&err);
if (err) {
error_report("%s", error_get_pretty(err));
exit(1);
}
object_property_set_bool(OBJECT(cpu), true, "realized", &err);
if (err) {
error_report("%s", error_get_pretty(err));
exit(1);
}
/* This will become a QOM property eventually */
cpu->reset_cbar = GIC_BASE_ADDR;
cpu_irq[n] = qdev_get_gpio_in(DEVICE(cpu), ARM_CPU_IRQ);
}
@@ -279,7 +288,7 @@ static void calxeda_init(QEMUMachineInitArgs *args, enum cxmachines machine)
qdev_prop_set_uint32(dev, "num-irq", NIRQ_GIC);
qdev_init_nofail(dev);
busdev = SYS_BUS_DEVICE(dev);
sysbus_mmio_map(busdev, 0, GIC_BASE_ADDR);
sysbus_mmio_map(busdev, 0, MPCORE_PERIPHBASE);
for (n = 0; n < smp_cpus; n++) {
sysbus_connect_irq(busdev, n, cpu_irq[n]);
}
+34 -10
View File
@@ -480,6 +480,36 @@ static void vexpress_modify_dtb(const struct arm_boot_info *info, void *fdt)
}
}
/* Open code a private version of pflash registration since we
* need to set non-default device width for VExpress platform.
*/
static pflash_t *ve_pflash_cfi01_register(hwaddr base, const char *name,
DriveInfo *di)
{
DeviceState *dev = qdev_create(NULL, "cfi.pflash01");
if (di && qdev_prop_set_drive(dev, "drive", di->bdrv)) {
abort();
}
qdev_prop_set_uint32(dev, "num-blocks",
VEXPRESS_FLASH_SIZE / VEXPRESS_FLASH_SECT_SIZE);
qdev_prop_set_uint64(dev, "sector-length", VEXPRESS_FLASH_SECT_SIZE);
qdev_prop_set_uint8(dev, "width", 4);
qdev_prop_set_uint8(dev, "device-width", 2);
qdev_prop_set_uint8(dev, "big-endian", 0);
qdev_prop_set_uint16(dev, "id0", 0x89);
qdev_prop_set_uint16(dev, "id1", 0x18);
qdev_prop_set_uint16(dev, "id2", 0x00);
qdev_prop_set_uint16(dev, "id3", 0x00);
qdev_prop_set_string(dev, "name", name);
qdev_init_nofail(dev);
sysbus_mmio_map(SYS_BUS_DEVICE(dev), 0, base);
return OBJECT_CHECK(pflash_t, (dev), "cfi.pflash01");
}
static void vexpress_common_init(VEDBoardInfo *daughterboard,
QEMUMachineInitArgs *args)
{
@@ -561,11 +591,8 @@ static void vexpress_common_init(VEDBoardInfo *daughterboard,
sysbus_create_simple("pl111", map[VE_CLCD], pic[14]);
dinfo = drive_get_next(IF_PFLASH);
pflash0 = pflash_cfi01_register(map[VE_NORFLASH0], NULL, "vexpress.flash0",
VEXPRESS_FLASH_SIZE, dinfo ? dinfo->bdrv : NULL,
VEXPRESS_FLASH_SECT_SIZE,
VEXPRESS_FLASH_SIZE / VEXPRESS_FLASH_SECT_SIZE, 4,
0x00, 0x89, 0x00, 0x18, 0);
pflash0 = ve_pflash_cfi01_register(map[VE_NORFLASH0], "vexpress.flash0",
dinfo);
if (!pflash0) {
fprintf(stderr, "vexpress: error registering flash 0.\n");
exit(1);
@@ -580,11 +607,8 @@ static void vexpress_common_init(VEDBoardInfo *daughterboard,
}
dinfo = drive_get_next(IF_PFLASH);
if (!pflash_cfi01_register(map[VE_NORFLASH1], NULL, "vexpress.flash1",
VEXPRESS_FLASH_SIZE, dinfo ? dinfo->bdrv : NULL,
VEXPRESS_FLASH_SECT_SIZE,
VEXPRESS_FLASH_SIZE / VEXPRESS_FLASH_SECT_SIZE, 4,
0x00, 0x89, 0x00, 0x18, 0)) {
if (!ve_pflash_cfi01_register(map[VE_NORFLASH1], "vexpress.flash1",
dinfo)) {
fprintf(stderr, "vexpress: error registering flash 1.\n");
exit(1);
}
+17 -4
View File
@@ -25,6 +25,7 @@
#include "sysemu/blockdev.h"
#include "hw/loader.h"
#include "hw/ssi.h"
#include "qemu/error-report.h"
#define NUM_SPI_FLASHES 4
#define NUM_QSPI_FLASHES 2
@@ -35,6 +36,8 @@
#define IRQ_OFFSET 32 /* pic interrupts start from index 32 */
#define MPCORE_PERIPHBASE 0xF8F00000
static const int dma_irqs[8] = {
46, 47, 48, 49, 72, 73, 74, 75
};
@@ -102,6 +105,7 @@ static void zynq_init(QEMUMachineInitArgs *args)
const char *kernel_filename = args->kernel_filename;
const char *kernel_cmdline = args->kernel_cmdline;
const char *initrd_filename = args->initrd_filename;
ObjectClass *cpu_oc;
ARMCPU *cpu;
MemoryRegion *address_space_mem = get_system_memory();
MemoryRegion *ext_ram = g_new(MemoryRegion, 1);
@@ -110,15 +114,24 @@ static void zynq_init(QEMUMachineInitArgs *args)
SysBusDevice *busdev;
qemu_irq pic[64];
NICInfo *nd;
Error *err = NULL;
int n;
if (!cpu_model) {
cpu_model = "cortex-a9";
}
cpu_oc = cpu_class_by_name(TYPE_ARM_CPU, cpu_model);
cpu = cpu_arm_init(cpu_model);
if (!cpu) {
fprintf(stderr, "Unable to find CPU definition\n");
cpu = ARM_CPU(object_new(object_class_get_name(cpu_oc)));
object_property_set_int(OBJECT(cpu), MPCORE_PERIPHBASE, "reset-cbar", &err);
if (err) {
error_report("%s", error_get_pretty(err));
exit(1);
}
object_property_set_bool(OBJECT(cpu), true, "realized", &err);
if (err) {
error_report("%s", error_get_pretty(err));
exit(1);
}
@@ -154,7 +167,7 @@ static void zynq_init(QEMUMachineInitArgs *args)
qdev_prop_set_uint32(dev, "num-cpu", 1);
qdev_init_nofail(dev);
busdev = SYS_BUS_DEVICE(dev);
sysbus_mmio_map(busdev, 0, 0xF8F00000);
sysbus_mmio_map(busdev, 0, MPCORE_PERIPHBASE);
sysbus_connect_irq(busdev, 0,
qdev_get_gpio_in(DEVICE(cpu), ARM_CPU_IRQ));
+228 -32
View File
@@ -40,6 +40,7 @@
#include "hw/block/flash.h"
#include "block/block.h"
#include "qemu/timer.h"
#include "qemu/bitops.h"
#include "exec/address-spaces.h"
#include "qemu/host-utils.h"
#include "hw/sysbus.h"
@@ -71,7 +72,9 @@ struct pflash_t {
BlockDriverState *bs;
uint32_t nb_blocs;
uint64_t sector_len;
uint8_t width;
uint8_t bank_width;
uint8_t device_width; /* If 0, device width not specified. */
uint8_t max_device_width; /* max device width in bytes */
uint8_t be;
uint8_t wcycle; /* if 0, the flash is read normally */
int ro;
@@ -116,6 +119,119 @@ static void pflash_timer (void *opaque)
pfl->cmd = 0;
}
/* Perform a CFI query based on the bank width of the flash.
* If this code is called we know we have a device_width set for
* this flash.
*/
static uint32_t pflash_cfi_query(pflash_t *pfl, hwaddr offset)
{
int i;
uint32_t resp = 0;
hwaddr boff;
/* Adjust incoming offset to match expected device-width
* addressing. CFI query addresses are always specified in terms of
* the maximum supported width of the device. This means that x8
* devices and x8/x16 devices in x8 mode behave differently. For
* devices that are not used at their max width, we will be
* provided with addresses that use higher address bits than
* expected (based on the max width), so we will shift them lower
* so that they will match the addresses used when
* device_width==max_device_width.
*/
boff = offset >> (ctz32(pfl->bank_width) +
ctz32(pfl->max_device_width) - ctz32(pfl->device_width));
if (boff > pfl->cfi_len) {
return 0;
}
/* Now we will construct the CFI response generated by a single
* device, then replicate that for all devices that make up the
* bus. For wide parts used in x8 mode, CFI query responses
* are different than native byte-wide parts.
*/
resp = pfl->cfi_table[boff];
if (pfl->device_width != pfl->max_device_width) {
/* The only case currently supported is x8 mode for a
* wider part.
*/
if (pfl->device_width != 1 || pfl->bank_width > 4) {
DPRINTF("%s: Unsupported device configuration: "
"device_width=%d, max_device_width=%d\n",
__func__, pfl->device_width,
pfl->max_device_width);
return 0;
}
/* CFI query data is repeated, rather than zero padded for
* wide devices used in x8 mode.
*/
for (i = 1; i < pfl->max_device_width; i++) {
resp = deposit32(resp, 8 * i, 8, pfl->cfi_table[boff]);
}
}
/* Replicate responses for each device in bank. */
if (pfl->device_width < pfl->bank_width) {
for (i = pfl->device_width;
i < pfl->bank_width; i += pfl->device_width) {
resp = deposit32(resp, 8 * i, 8 * pfl->device_width, resp);
}
}
return resp;
}
/* Perform a device id query based on the bank width of the flash. */
static uint32_t pflash_devid_query(pflash_t *pfl, hwaddr offset)
{
int i;
uint32_t resp;
hwaddr boff;
/* Adjust incoming offset to match expected device-width
* addressing. Device ID read addresses are always specified in
* terms of the maximum supported width of the device. This means
* that x8 devices and x8/x16 devices in x8 mode behave
* differently. For devices that are not used at their max width,
* we will be provided with addresses that use higher address bits
* than expected (based on the max width), so we will shift them
* lower so that they will match the addresses used when
* device_width==max_device_width.
*/
boff = offset >> (ctz32(pfl->bank_width) +
ctz32(pfl->max_device_width) - ctz32(pfl->device_width));
/* Mask off upper bits which may be used in to query block
* or sector lock status at other addresses.
* Offsets 2/3 are block lock status, is not emulated.
*/
switch (boff & 0xFF) {
case 0:
resp = pfl->ident0;
DPRINTF("%s: Manufacturer Code %04x\n", __func__, ret);
break;
case 1:
resp = pfl->ident1;
DPRINTF("%s: Device ID Code %04x\n", __func__, ret);
break;
default:
DPRINTF("%s: Read Device Information offset=%x\n", __func__,
(unsigned)offset);
return 0;
break;
}
/* Replicate responses for each device in bank. */
if (pfl->device_width < pfl->bank_width) {
for (i = pfl->device_width;
i < pfl->bank_width; i += pfl->device_width) {
resp = deposit32(resp, 8 * i, 8 * pfl->device_width, resp);
}
}
return resp;
}
static uint32_t pflash_read (pflash_t *pfl, hwaddr offset,
int width, int be)
{
@@ -124,12 +240,6 @@ static uint32_t pflash_read (pflash_t *pfl, hwaddr offset,
uint8_t *p;
ret = -1;
boff = offset & 0xFF; /* why this here ?? */
if (pfl->width == 2)
boff = boff >> 1;
else if (pfl->width == 4)
boff = boff >> 2;
#if 0
DPRINTF("%s: reading offset " TARGET_FMT_plx " under cmd %02x width %d\n",
@@ -190,35 +300,88 @@ static uint32_t pflash_read (pflash_t *pfl, hwaddr offset,
case 0x60: /* Block /un)lock */
case 0x70: /* Status Register */
case 0xe8: /* Write block */
/* Status register read */
/* Status register read. Return status from each device in
* bank.
*/
ret = pfl->status;
if (width > 2) {
if (pfl->device_width && width > pfl->device_width) {
int shift = pfl->device_width * 8;
while (shift + pfl->device_width * 8 <= width * 8) {
ret |= pfl->status << shift;
shift += pfl->device_width * 8;
}
} else if (!pfl->device_width && width > 2) {
/* Handle 32 bit flash cases where device width is not
* set. (Existing behavior before device width added.)
*/
ret |= pfl->status << 16;
}
DPRINTF("%s: status %x\n", __func__, ret);
break;
case 0x90:
switch (boff) {
case 0:
ret = pfl->ident0 << 8 | pfl->ident1;
DPRINTF("%s: Manufacturer Code %04x\n", __func__, ret);
break;
case 1:
ret = pfl->ident2 << 8 | pfl->ident3;
DPRINTF("%s: Device ID Code %04x\n", __func__, ret);
break;
default:
DPRINTF("%s: Read Device Information boff=%x\n", __func__,
(unsigned)boff);
ret = 0;
break;
if (!pfl->device_width) {
/* Preserve old behavior if device width not specified */
boff = offset & 0xFF;
if (pfl->bank_width == 2) {
boff = boff >> 1;
} else if (pfl->bank_width == 4) {
boff = boff >> 2;
}
switch (boff) {
case 0:
ret = pfl->ident0 << 8 | pfl->ident1;
DPRINTF("%s: Manufacturer Code %04x\n", __func__, ret);
break;
case 1:
ret = pfl->ident2 << 8 | pfl->ident3;
DPRINTF("%s: Device ID Code %04x\n", __func__, ret);
break;
default:
DPRINTF("%s: Read Device Information boff=%x\n", __func__,
(unsigned)boff);
ret = 0;
break;
}
} else {
/* If we have a read larger than the bank_width, combine multiple
* manufacturer/device ID queries into a single response.
*/
int i;
for (i = 0; i < width; i += pfl->bank_width) {
ret = deposit32(ret, i * 8, pfl->bank_width * 8,
pflash_devid_query(pfl,
offset + i * pfl->bank_width));
}
}
break;
case 0x98: /* Query mode */
if (boff > pfl->cfi_len)
ret = 0;
else
ret = pfl->cfi_table[boff];
if (!pfl->device_width) {
/* Preserve old behavior if device width not specified */
boff = offset & 0xFF;
if (pfl->bank_width == 2) {
boff = boff >> 1;
} else if (pfl->bank_width == 4) {
boff = boff >> 2;
}
if (boff > pfl->cfi_len) {
ret = 0;
} else {
ret = pfl->cfi_table[boff];
}
} else {
/* If we have a read larger than the bank_width, combine multiple
* CFI queries into a single response.
*/
int i;
for (i = 0; i < width; i += pfl->bank_width) {
ret = deposit32(ret, i * 8, pfl->bank_width * 8,
pflash_cfi_query(pfl,
offset + i * pfl->bank_width));
}
}
break;
}
return ret;
@@ -378,6 +541,14 @@ static void pflash_write(pflash_t *pfl, hwaddr offset,
break;
case 0xe8:
/* Mask writeblock size based on device width, or bank width if
* device width not specified.
*/
if (pfl->device_width) {
value = extract32(value, 0, pfl->device_width * 8);
} else {
value = extract32(value, 0, pfl->bank_width * 8);
}
DPRINTF("%s: block write of %x bytes\n", __func__, value);
pfl->counter = value;
pfl->wcycle++;
@@ -613,6 +784,13 @@ static void pflash_cfi01_realize(DeviceState *dev, Error **errp)
pfl->ro = 0;
}
/* Default to devices being used at their maximum device width. This was
* assumed before the device_width support was added.
*/
if (!pfl->max_device_width) {
pfl->max_device_width = pfl->device_width;
}
pfl->timer = timer_new_ns(QEMU_CLOCK_VIRTUAL, pflash_timer, pfl);
pfl->wcycle = 0;
pfl->cmd = 0;
@@ -665,7 +843,7 @@ static void pflash_cfi01_realize(DeviceState *dev, Error **errp)
pfl->cfi_table[0x28] = 0x02;
pfl->cfi_table[0x29] = 0x00;
/* Max number of bytes in multi-bytes write */
if (pfl->width == 1) {
if (pfl->bank_width == 1) {
pfl->cfi_table[0x2A] = 0x08;
} else {
pfl->cfi_table[0x2A] = 0x0B;
@@ -706,7 +884,25 @@ static Property pflash_cfi01_properties[] = {
DEFINE_PROP_DRIVE("drive", struct pflash_t, bs),
DEFINE_PROP_UINT32("num-blocks", struct pflash_t, nb_blocs, 0),
DEFINE_PROP_UINT64("sector-length", struct pflash_t, sector_len, 0),
DEFINE_PROP_UINT8("width", struct pflash_t, width, 0),
/* width here is the overall width of this QEMU device in bytes.
* The QEMU device may be emulating a number of flash devices
* wired up in parallel; the width of each individual flash
* device should be specified via device-width. If the individual
* devices have a maximum width which is greater than the width
* they are being used for, this maximum width should be set via
* max-device-width (which otherwise defaults to device-width).
* So for instance a 32-bit wide QEMU flash device made from four
* 16-bit flash devices used in 8-bit wide mode would be configured
* with width = 4, device-width = 1, max-device-width = 2.
*
* If device-width is not specified we default to backwards
* compatible behaviour which is a bad emulation of two
* 16 bit devices making up a 32 bit wide QEMU device. This
* is deprecated for new uses of this device.
*/
DEFINE_PROP_UINT8("width", struct pflash_t, bank_width, 0),
DEFINE_PROP_UINT8("device-width", struct pflash_t, device_width, 0),
DEFINE_PROP_UINT8("max-device-width", struct pflash_t, max_device_width, 0),
DEFINE_PROP_UINT8("big-endian", struct pflash_t, be, 0),
DEFINE_PROP_UINT16("id0", struct pflash_t, ident0, 0),
DEFINE_PROP_UINT16("id1", struct pflash_t, ident1, 0),
@@ -745,8 +941,8 @@ pflash_t *pflash_cfi01_register(hwaddr base,
DeviceState *qdev, const char *name,
hwaddr size,
BlockDriverState *bs,
uint32_t sector_len, int nb_blocs, int width,
uint16_t id0, uint16_t id1,
uint32_t sector_len, int nb_blocs,
int bank_width, uint16_t id0, uint16_t id1,
uint16_t id2, uint16_t id3, int be)
{
DeviceState *dev = qdev_create(NULL, TYPE_CFI_PFLASH01);
@@ -756,7 +952,7 @@ pflash_t *pflash_cfi01_register(hwaddr base,
}
qdev_prop_set_uint32(dev, "num-blocks", nb_blocs);
qdev_prop_set_uint64(dev, "sector-length", sector_len);
qdev_prop_set_uint8(dev, "width", width);
qdev_prop_set_uint8(dev, "width", bank_width);
qdev_prop_set_uint8(dev, "big-endian", !!be);
qdev_prop_set_uint16(dev, "id0", id0);
qdev_prop_set_uint16(dev, "id1", id1);
+1
View File
@@ -14,6 +14,7 @@ obj-$(CONFIG_COLDFIRE) += mcf_uart.o
obj-$(CONFIG_OMAP) += omap_uart.o
obj-$(CONFIG_SH4) += sh_serial.o
obj-$(CONFIG_PSERIES) += spapr_vty.o
obj-$(CONFIG_DIGIC) += digic-uart.o
common-obj-$(CONFIG_ETRAXFS) += etraxfs_ser.o
common-obj-$(CONFIG_ISA_DEBUG) += debugcon.o
+195
View File
@@ -0,0 +1,195 @@
/*
* QEMU model of the Canon DIGIC UART block.
*
* Copyright (C) 2013 Antony Pavlov <antonynpavlov@gmail.com>
*
* This model is based on reverse engineering efforts
* made by CHDK (http://chdk.wikia.com) and
* Magic Lantern (http://www.magiclantern.fm) projects
* contributors.
*
* See "Serial terminal" docs here:
* http://magiclantern.wikia.com/wiki/Register_Map#Misc_Registers
*
* The QEMU model of the Milkymist UART block by Michael Walle
* is used as a template.
*
* 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.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
*/
#include "hw/hw.h"
#include "hw/sysbus.h"
#include "sysemu/char.h"
#include "hw/char/digic-uart.h"
enum {
ST_RX_RDY = (1 << 0),
ST_TX_RDY = (1 << 1),
};
static uint64_t digic_uart_read(void *opaque, hwaddr addr,
unsigned size)
{
DigicUartState *s = opaque;
uint64_t ret = 0;
addr >>= 2;
switch (addr) {
case R_RX:
s->reg_st &= ~(ST_RX_RDY);
ret = s->reg_rx;
break;
case R_ST:
ret = s->reg_st;
break;
default:
qemu_log_mask(LOG_UNIMP,
"digic-uart: read access to unknown register 0x"
TARGET_FMT_plx, addr << 2);
}
return ret;
}
static void digic_uart_write(void *opaque, hwaddr addr, uint64_t value,
unsigned size)
{
DigicUartState *s = opaque;
unsigned char ch = value;
addr >>= 2;
switch (addr) {
case R_TX:
if (s->chr) {
qemu_chr_fe_write_all(s->chr, &ch, 1);
}
break;
case R_ST:
/*
* Ignore write to R_ST.
*
* The point is that this register is actively used
* during receiving and transmitting symbols,
* but we don't know the function of most of bits.
*
* Ignoring writes to R_ST is only a simplification
* of the model. It has no perceptible side effects
* for existing guests.
*/
break;
default:
qemu_log_mask(LOG_UNIMP,
"digic-uart: write access to unknown register 0x"
TARGET_FMT_plx, addr << 2);
}
}
static const MemoryRegionOps uart_mmio_ops = {
.read = digic_uart_read,
.write = digic_uart_write,
.valid = {
.min_access_size = 4,
.max_access_size = 4,
},
.endianness = DEVICE_NATIVE_ENDIAN,
};
static int uart_can_rx(void *opaque)
{
DigicUartState *s = opaque;
return !(s->reg_st & ST_RX_RDY);
}
static void uart_rx(void *opaque, const uint8_t *buf, int size)
{
DigicUartState *s = opaque;
assert(uart_can_rx(opaque));
s->reg_st |= ST_RX_RDY;
s->reg_rx = *buf;
}
static void uart_event(void *opaque, int event)
{
}
static void digic_uart_reset(DeviceState *d)
{
DigicUartState *s = DIGIC_UART(d);
s->reg_rx = 0;
s->reg_st = ST_TX_RDY;
}
static void digic_uart_realize(DeviceState *dev, Error **errp)
{
DigicUartState *s = DIGIC_UART(dev);
s->chr = qemu_char_get_next_serial();
if (s->chr) {
qemu_chr_add_handlers(s->chr, uart_can_rx, uart_rx, uart_event, s);
}
}
static void digic_uart_init(Object *obj)
{
DigicUartState *s = DIGIC_UART(obj);
memory_region_init_io(&s->regs_region, OBJECT(s), &uart_mmio_ops, s,
TYPE_DIGIC_UART, 0x18);
sysbus_init_mmio(SYS_BUS_DEVICE(obj), &s->regs_region);
}
static const VMStateDescription vmstate_digic_uart = {
.name = "digic-uart",
.version_id = 1,
.minimum_version_id = 1,
.minimum_version_id_old = 1,
.fields = (VMStateField[]) {
VMSTATE_UINT32(reg_rx, DigicUartState),
VMSTATE_UINT32(reg_st, DigicUartState),
VMSTATE_END_OF_LIST()
}
};
static void digic_uart_class_init(ObjectClass *klass, void *data)
{
DeviceClass *dc = DEVICE_CLASS(klass);
dc->realize = digic_uart_realize;
dc->reset = digic_uart_reset;
dc->vmsd = &vmstate_digic_uart;
}
static const TypeInfo digic_uart_info = {
.name = TYPE_DIGIC_UART,
.parent = TYPE_SYS_BUS_DEVICE,
.instance_size = sizeof(DigicUartState),
.instance_init = digic_uart_init,
.class_init = digic_uart_class_init,
};
static void digic_uart_register_types(void)
{
type_register_static(&digic_uart_info);
}
type_init(digic_uart_register_types)
+1
View File
@@ -24,3 +24,4 @@ obj-$(CONFIG_OPENPIC_KVM) += openpic_kvm.o
obj-$(CONFIG_SH4) += sh_intc.o
obj-$(CONFIG_XICS) += xics.o
obj-$(CONFIG_XICS_KVM) += xics_kvm.o
obj-$(CONFIG_ALLWINNER_A10_PIC) += allwinner-a10-pic.o
+200
View File
@@ -0,0 +1,200 @@
/*
* Allwinner A10 interrupt controller device emulation
*
* Copyright (C) 2013 Li Guang
* Written by Li Guang <lig.fnst@cn.fujitsu.com>
*
* 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.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* for more details.
*/
#include "hw/sysbus.h"
#include "hw/devices.h"
#include "sysemu/sysemu.h"
#include "hw/intc/allwinner-a10-pic.h"
static void aw_a10_pic_update(AwA10PICState *s)
{
uint8_t i;
int irq = 0, fiq = 0;
for (i = 0; i < AW_A10_PIC_REG_NUM; i++) {
irq |= s->irq_pending[i] & ~s->mask[i];
fiq |= s->select[i] & s->irq_pending[i] & ~s->mask[i];
}
qemu_set_irq(s->parent_irq, !!irq);
qemu_set_irq(s->parent_fiq, !!fiq);
}
static void aw_a10_pic_set_irq(void *opaque, int irq, int level)
{
AwA10PICState *s = opaque;
if (level) {
set_bit(irq % 32, (void *)&s->irq_pending[irq / 32]);
}
aw_a10_pic_update(s);
}
static uint64_t aw_a10_pic_read(void *opaque, hwaddr offset, unsigned size)
{
AwA10PICState *s = opaque;
uint8_t index = (offset & 0xc) / 4;
switch (offset) {
case AW_A10_PIC_VECTOR:
return s->vector;
case AW_A10_PIC_BASE_ADDR:
return s->base_addr;
case AW_A10_PIC_PROTECT:
return s->protect;
case AW_A10_PIC_NMI:
return s->nmi;
case AW_A10_PIC_IRQ_PENDING ... AW_A10_PIC_IRQ_PENDING + 8:
return s->irq_pending[index];
case AW_A10_PIC_FIQ_PENDING ... AW_A10_PIC_FIQ_PENDING + 8:
return s->fiq_pending[index];
case AW_A10_PIC_SELECT ... AW_A10_PIC_SELECT + 8:
return s->select[index];
case AW_A10_PIC_ENABLE ... AW_A10_PIC_ENABLE + 8:
return s->enable[index];
case AW_A10_PIC_MASK ... AW_A10_PIC_MASK + 8:
return s->mask[index];
default:
qemu_log_mask(LOG_GUEST_ERROR,
"%s: Bad offset 0x%x\n", __func__, (int)offset);
break;
}
return 0;
}
static void aw_a10_pic_write(void *opaque, hwaddr offset, uint64_t value,
unsigned size)
{
AwA10PICState *s = opaque;
uint8_t index = (offset & 0xc) / 4;
switch (offset) {
case AW_A10_PIC_VECTOR:
s->vector = value & ~0x3;
break;
case AW_A10_PIC_BASE_ADDR:
s->base_addr = value & ~0x3;
case AW_A10_PIC_PROTECT:
s->protect = value;
break;
case AW_A10_PIC_NMI:
s->nmi = value;
break;
case AW_A10_PIC_IRQ_PENDING ... AW_A10_PIC_IRQ_PENDING + 8:
s->irq_pending[index] &= ~value;
break;
case AW_A10_PIC_FIQ_PENDING ... AW_A10_PIC_FIQ_PENDING + 8:
s->fiq_pending[index] &= ~value;
break;
case AW_A10_PIC_SELECT ... AW_A10_PIC_SELECT + 8:
s->select[index] = value;
break;
case AW_A10_PIC_ENABLE ... AW_A10_PIC_ENABLE + 8:
s->enable[index] = value;
break;
case AW_A10_PIC_MASK ... AW_A10_PIC_MASK + 8:
s->mask[index] = value;
break;
default:
qemu_log_mask(LOG_GUEST_ERROR,
"%s: Bad offset 0x%x\n", __func__, (int)offset);
break;
}
aw_a10_pic_update(s);
}
static const MemoryRegionOps aw_a10_pic_ops = {
.read = aw_a10_pic_read,
.write = aw_a10_pic_write,
.endianness = DEVICE_NATIVE_ENDIAN,
};
static const VMStateDescription vmstate_aw_a10_pic = {
.name = "a10.pic",
.version_id = 1,
.minimum_version_id = 1,
.minimum_version_id_old = 1,
.fields = (VMStateField[]) {
VMSTATE_UINT32(vector, AwA10PICState),
VMSTATE_UINT32(base_addr, AwA10PICState),
VMSTATE_UINT32(protect, AwA10PICState),
VMSTATE_UINT32(nmi, AwA10PICState),
VMSTATE_UINT32_ARRAY(irq_pending, AwA10PICState, AW_A10_PIC_REG_NUM),
VMSTATE_UINT32_ARRAY(fiq_pending, AwA10PICState, AW_A10_PIC_REG_NUM),
VMSTATE_UINT32_ARRAY(enable, AwA10PICState, AW_A10_PIC_REG_NUM),
VMSTATE_UINT32_ARRAY(select, AwA10PICState, AW_A10_PIC_REG_NUM),
VMSTATE_UINT32_ARRAY(mask, AwA10PICState, AW_A10_PIC_REG_NUM),
VMSTATE_END_OF_LIST()
}
};
static void aw_a10_pic_init(Object *obj)
{
AwA10PICState *s = AW_A10_PIC(obj);
SysBusDevice *dev = SYS_BUS_DEVICE(obj);
qdev_init_gpio_in(DEVICE(dev), aw_a10_pic_set_irq, AW_A10_PIC_INT_NR);
sysbus_init_irq(dev, &s->parent_irq);
sysbus_init_irq(dev, &s->parent_fiq);
memory_region_init_io(&s->iomem, OBJECT(s), &aw_a10_pic_ops, s,
TYPE_AW_A10_PIC, 0x400);
sysbus_init_mmio(dev, &s->iomem);
}
static void aw_a10_pic_reset(DeviceState *d)
{
AwA10PICState *s = AW_A10_PIC(d);
uint8_t i;
s->base_addr = 0;
s->protect = 0;
s->nmi = 0;
s->vector = 0;
for (i = 0; i < AW_A10_PIC_REG_NUM; i++) {
s->irq_pending[i] = 0;
s->fiq_pending[i] = 0;
s->select[i] = 0;
s->enable[i] = 0;
s->mask[i] = 0;
}
}
static void aw_a10_pic_class_init(ObjectClass *klass, void *data)
{
DeviceClass *dc = DEVICE_CLASS(klass);
dc->reset = aw_a10_pic_reset;
dc->desc = "allwinner a10 pic";
dc->vmsd = &vmstate_aw_a10_pic;
}
static const TypeInfo aw_a10_pic_info = {
.name = TYPE_AW_A10_PIC,
.parent = TYPE_SYS_BUS_DEVICE,
.instance_size = sizeof(AwA10PICState),
.instance_init = aw_a10_pic_init,
.class_init = aw_a10_pic_class_init,
};
static void aw_a10_register_types(void)
{
type_register_static(&aw_a10_pic_info);
}
type_init(aw_a10_register_types);
+3
View File
@@ -26,5 +26,8 @@ obj-$(CONFIG_OMAP) += omap_synctimer.o
obj-$(CONFIG_PXA2XX) += pxa2xx_timer.o
obj-$(CONFIG_SH4) += sh_timer.o
obj-$(CONFIG_TUSB6010) += tusb6010.o
obj-$(CONFIG_DIGIC) += digic-timer.o
obj-$(CONFIG_MC146818RTC) += mc146818rtc.o
obj-$(CONFIG_ALLWINNER_A10_PIT) += allwinner-a10-pit.o

Some files were not shown because too many files have changed in this diff Show More