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)
 -----BEGIN PGP SIGNATURE-----
 Version: GnuPG v1.4.11 (GNU/Linux)
 
 iQIcBAABCAAGBQJSsLGfAAoJEDwlJe0UNgzehVQQAK1nL/AjTSaOhu7o2ZI+3OIO
 SSYuJk61QIE6RTLgV9SV+ifSjeOec3ngFpbv0Kc+M02xSEmEJmIHa5ZXLPDLYKMP
 7v5zVmS/my3lDsBj6OW/M+VOv1HULm+8jVwVqwm9+ijcmG+Z4gVadvXRcnjKW9se
 KUaD27X28+by3RjE9w/IXJbGUvvEpwdzFvUBN9B2lQgsmM7z8ankzZL9TIHS+Jyx
 HquJ8FIeBYMoAk/lWD9REPXgYjl7QjD7NE0ue2CUL5A8MwInbxlypMkHxjsk2DWg
 1C6gqK/d8fWHRT+a5KAKAbW4Atn9tSRW7hiPv2BIKYuDkfTictBIhHoRdX3rPlJc
 DmM8+aYRSjL27cwNzLbgX3ZSWMIlP7p2d0Mi3aWtFDUgXp1S+JOmdJmQAbDgjzn0
 FEDfBJR5bLeIXT1gpSLZJmxOJjSfnhCQM/hNS2ArTLBO/vNL2lIEEFzQprqantep
 l++l4GfqNOf2XglHRZP2M24/a3qXzQefaCZXH2+ESW3hegsZKZ8A0UsNr0oWenF7
 C2Kf78EmMMCEql9Gz5kmhbQIeLE1tXKopeZcWOKkbOYuPZk1KDNIzlz7MPZwu8LJ
 6iYA8lczNUTXpu0ErRfalR+HWGy5jqbXyE0/IUamYQOOmVi2SnZlx9ewmmKI5tDO
 MGrrlkf3ojOv2hukzaUk
 =I3Of
 -----END PGP SIGNATURE-----

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>
master
Anthony Liguori 2013-12-19 11:56:33 -08:00
commit 3dc7e2a3fe
50 changed files with 5229 additions and 694 deletions

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

4
configure vendored
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 \

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

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
gdb-xml/aarch64-fpu.xml Normal file
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>

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
hw/arm/allwinner-a10.c Normal file
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)

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
hw/arm/cubieboard.c Normal file
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
hw/arm/digic.c Normal file
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
hw/arm/digic_boards.c Normal file
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)

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]);
}

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);
}

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));

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);

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
hw/char/digic-uart.c Normal file
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)

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
hw/intc/allwinner-a10-pic.c Normal file
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);

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

View File

@ -0,0 +1,254 @@
/*
* Allwinner A10 timer 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 "sysemu/sysemu.h"
#include "hw/timer/allwinner-a10-pit.h"
static uint64_t a10_pit_read(void *opaque, hwaddr offset, unsigned size)
{
AwA10PITState *s = AW_A10_PIT(opaque);
uint8_t index;
switch (offset) {
case AW_A10_PIT_TIMER_IRQ_EN:
return s->irq_enable;
case AW_A10_PIT_TIMER_IRQ_ST:
return s->irq_status;
case AW_A10_PIT_TIMER_BASE ... AW_A10_PIT_TIMER_BASE_END:
index = offset & 0xf0;
index >>= 4;
index -= 1;
switch (offset & 0x0f) {
case AW_A10_PIT_TIMER_CONTROL:
return s->control[index];
case AW_A10_PIT_TIMER_INTERVAL:
return s->interval[index];
case AW_A10_PIT_TIMER_COUNT:
s->count[index] = ptimer_get_count(s->timer[index]);
return s->count[index];
default:
qemu_log_mask(LOG_GUEST_ERROR,
"%s: Bad offset 0x%x\n", __func__, (int)offset);
break;
}
case AW_A10_PIT_WDOG_CONTROL:
break;
case AW_A10_PIT_WDOG_MODE:
break;
case AW_A10_PIT_COUNT_LO:
return s->count_lo;
case AW_A10_PIT_COUNT_HI:
return s->count_hi;
case AW_A10_PIT_COUNT_CTL:
return s->count_ctl;
default:
qemu_log_mask(LOG_GUEST_ERROR,
"%s: Bad offset 0x%x\n", __func__, (int)offset);
break;
}
return 0;
}
static void a10_pit_write(void *opaque, hwaddr offset, uint64_t value,
unsigned size)
{
AwA10PITState *s = AW_A10_PIT(opaque);
uint8_t index;
switch (offset) {
case AW_A10_PIT_TIMER_IRQ_EN:
s->irq_enable = value;
break;
case AW_A10_PIT_TIMER_IRQ_ST:
s->irq_status &= ~value;
break;
case AW_A10_PIT_TIMER_BASE ... AW_A10_PIT_TIMER_BASE_END:
index = offset & 0xf0;
index >>= 4;
index -= 1;
switch (offset & 0x0f) {
case AW_A10_PIT_TIMER_CONTROL:
s->control[index] = value;
if (s->control[index] & AW_A10_PIT_TIMER_RELOAD) {
ptimer_set_count(s->timer[index], s->interval[index]);
}
if (s->control[index] & AW_A10_PIT_TIMER_EN) {
int oneshot = 0;
if (s->control[index] & AW_A10_PIT_TIMER_MODE) {
oneshot = 1;
}
ptimer_run(s->timer[index], oneshot);
} else {
ptimer_stop(s->timer[index]);
}
break;
case AW_A10_PIT_TIMER_INTERVAL:
s->interval[index] = value;
ptimer_set_limit(s->timer[index], s->interval[index], 1);
break;
case AW_A10_PIT_TIMER_COUNT:
s->count[index] = value;
break;
default:
qemu_log_mask(LOG_GUEST_ERROR,
"%s: Bad offset 0x%x\n", __func__, (int)offset);
}
break;
case AW_A10_PIT_WDOG_CONTROL:
s->watch_dog_control = value;
break;
case AW_A10_PIT_WDOG_MODE:
s->watch_dog_mode = value;
break;
case AW_A10_PIT_COUNT_LO:
s->count_lo = value;
break;
case AW_A10_PIT_COUNT_HI:
s->count_hi = value;
break;
case AW_A10_PIT_COUNT_CTL:
s->count_ctl = value;
if (s->count_ctl & AW_A10_PIT_COUNT_RL_EN) {
uint64_t tmp_count = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL);
s->count_lo = tmp_count;
s->count_hi = tmp_count >> 32;
s->count_ctl &= ~AW_A10_PIT_COUNT_RL_EN;
}
if (s->count_ctl & AW_A10_PIT_COUNT_CLR_EN) {
s->count_lo = 0;
s->count_hi = 0;
s->count_ctl &= ~AW_A10_PIT_COUNT_CLR_EN;
}
break;
default:
qemu_log_mask(LOG_GUEST_ERROR,
"%s: Bad offset 0x%x\n", __func__, (int)offset);
break;
}
}
static const MemoryRegionOps a10_pit_ops = {
.read = a10_pit_read,
.write = a10_pit_write,
.endianness = DEVICE_NATIVE_ENDIAN,
};
static const VMStateDescription vmstate_a10_pit = {
.name = "a10.pit",
.version_id = 1,
.minimum_version_id = 1,
.minimum_version_id_old = 1,
.fields = (VMStateField[]) {
VMSTATE_UINT32(irq_enable, AwA10PITState),
VMSTATE_UINT32(irq_status, AwA10PITState),
VMSTATE_UINT32_ARRAY(control, AwA10PITState, AW_A10_PIT_TIMER_NR),
VMSTATE_UINT32_ARRAY(interval, AwA10PITState, AW_A10_PIT_TIMER_NR),
VMSTATE_UINT32_ARRAY(count, AwA10PITState, AW_A10_PIT_TIMER_NR),
VMSTATE_UINT32(watch_dog_mode, AwA10PITState),
VMSTATE_UINT32(watch_dog_control, AwA10PITState),
VMSTATE_UINT32(count_lo, AwA10PITState),
VMSTATE_UINT32(count_hi, AwA10PITState),
VMSTATE_UINT32(count_ctl, AwA10PITState),
VMSTATE_PTIMER_ARRAY(timer, AwA10PITState, AW_A10_PIT_TIMER_NR),
VMSTATE_END_OF_LIST()
}
};
static void a10_pit_reset(DeviceState *dev)
{
AwA10PITState *s = AW_A10_PIT(dev);
uint8_t i;
s->irq_enable = 0;
s->irq_status = 0;
for (i = 0; i < 6; i++) {
s->control[i] = AW_A10_PIT_DEFAULT_CLOCK;
s->interval[i] = 0;
s->count[i] = 0;
ptimer_stop(s->timer[i]);
}
s->watch_dog_mode = 0;
s->watch_dog_control = 0;
s->count_lo = 0;
s->count_hi = 0;
s->count_ctl = 0;
}
static void a10_pit_timer_cb(void *opaque)
{
AwA10PITState *s = AW_A10_PIT(opaque);
uint8_t i;
for (i = 0; i < AW_A10_PIT_TIMER_NR; i++) {
if (s->control[i] & AW_A10_PIT_TIMER_EN) {
s->irq_status |= 1 << i;
if (s->control[i] & AW_A10_PIT_TIMER_MODE) {
ptimer_stop(s->timer[i]);
s->control[i] &= ~AW_A10_PIT_TIMER_EN;
}
qemu_irq_pulse(s->irq[i]);
}
}
}
static void a10_pit_init(Object *obj)
{
AwA10PITState *s = AW_A10_PIT(obj);
SysBusDevice *sbd = SYS_BUS_DEVICE(obj);
QEMUBH * bh[AW_A10_PIT_TIMER_NR];
uint8_t i;
for (i = 0; i < AW_A10_PIT_TIMER_NR; i++) {
sysbus_init_irq(sbd, &s->irq[i]);
}
memory_region_init_io(&s->iomem, OBJECT(s), &a10_pit_ops, s,
TYPE_AW_A10_PIT, 0x400);
sysbus_init_mmio(sbd, &s->iomem);
for (i = 0; i < AW_A10_PIT_TIMER_NR; i++) {
bh[i] = qemu_bh_new(a10_pit_timer_cb, s);
s->timer[i] = ptimer_init(bh[i]);
ptimer_set_freq(s->timer[i], 240000);
}
}
static void a10_pit_class_init(ObjectClass *klass, void *data)
{
DeviceClass *dc = DEVICE_CLASS(klass);
dc->reset = a10_pit_reset;
dc->desc = "allwinner a10 timer";
dc->vmsd = &vmstate_a10_pit;
}
static const TypeInfo a10_pit_info = {
.name = TYPE_AW_A10_PIT,
.parent = TYPE_SYS_BUS_DEVICE,
.instance_size = sizeof(AwA10PITState),
.instance_init = a10_pit_init,
.class_init = a10_pit_class_init,
};
static void a10_register_types(void)
{
type_register_static(&a10_pit_info);
}
type_init(a10_register_types);

163
hw/timer/digic-timer.c Normal file
View File

@ -0,0 +1,163 @@
/*
* QEMU model of the Canon DIGIC timer 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 "Timer/Clock Module" docs here:
* http://magiclantern.wikia.com/wiki/Register_Map
*
* The QEMU model of the OSTimer in PKUnity SoC by Guan Xuetao
* 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/sysbus.h"
#include "hw/ptimer.h"
#include "qemu/main-loop.h"
#include "hw/timer/digic-timer.h"
static const VMStateDescription vmstate_digic_timer = {
.name = "digic.timer",
.version_id = 1,
.minimum_version_id = 1,
.minimum_version_id_old = 1,
.fields = (VMStateField[]) {
VMSTATE_PTIMER(ptimer, DigicTimerState),
VMSTATE_UINT32(control, DigicTimerState),
VMSTATE_UINT32(relvalue, DigicTimerState),
VMSTATE_END_OF_LIST()
}
};
static void digic_timer_reset(DeviceState *dev)
{
DigicTimerState *s = DIGIC_TIMER(dev);
ptimer_stop(s->ptimer);
s->control = 0;
s->relvalue = 0;
}
static uint64_t digic_timer_read(void *opaque, hwaddr offset, unsigned size)
{
DigicTimerState *s = opaque;
uint64_t ret = 0;
switch (offset) {
case DIGIC_TIMER_CONTROL:
ret = s->control;
break;
case DIGIC_TIMER_RELVALUE:
ret = s->relvalue;
break;
case DIGIC_TIMER_VALUE:
ret = ptimer_get_count(s->ptimer) & 0xffff;
break;
default:
qemu_log_mask(LOG_UNIMP,
"digic-timer: read access to unknown register 0x"
TARGET_FMT_plx, offset);
}
return ret;
}
static void digic_timer_write(void *opaque, hwaddr offset,
uint64_t value, unsigned size)
{
DigicTimerState *s = opaque;
switch (offset) {
case DIGIC_TIMER_CONTROL:
if (value & DIGIC_TIMER_CONTROL_RST) {
digic_timer_reset((DeviceState *)s);
break;
}
if (value & DIGIC_TIMER_CONTROL_EN) {
ptimer_run(s->ptimer, 0);
}
s->control = (uint32_t)value;
break;
case DIGIC_TIMER_RELVALUE:
s->relvalue = extract32(value, 0, 16);
ptimer_set_limit(s->ptimer, s->relvalue, 1);
break;
case DIGIC_TIMER_VALUE:
break;
default:
qemu_log_mask(LOG_UNIMP,
"digic-timer: read access to unknown register 0x"
TARGET_FMT_plx, offset);
}
}
static const MemoryRegionOps digic_timer_ops = {
.read = digic_timer_read,
.write = digic_timer_write,
.impl = {
.min_access_size = 4,
.max_access_size = 4,
},
.endianness = DEVICE_NATIVE_ENDIAN,
};
static void digic_timer_init(Object *obj)
{
DigicTimerState *s = DIGIC_TIMER(obj);
s->ptimer = ptimer_init(NULL);
/*
* FIXME: there is no documentation on Digic timer
* frequency setup so let it always run at 1 MHz
*/
ptimer_set_freq(s->ptimer, 1 * 1000 * 1000);
memory_region_init_io(&s->iomem, OBJECT(s), &digic_timer_ops, s,
TYPE_DIGIC_TIMER, 0x100);
sysbus_init_mmio(SYS_BUS_DEVICE(obj), &s->iomem);
}
static void digic_timer_class_init(ObjectClass *klass, void *class_data)
{
DeviceClass *dc = DEVICE_CLASS(klass);
dc->reset = digic_timer_reset;
dc->vmsd = &vmstate_digic_timer;
}
static const TypeInfo digic_timer_info = {
.name = TYPE_DIGIC_TIMER,
.parent = TYPE_SYS_BUS_DEVICE,
.instance_size = sizeof(DigicTimerState),
.instance_init = digic_timer_init,
.class_init = digic_timer_class_init,
};
static void digic_timer_register_type(void)
{
type_register_static(&digic_timer_info);
}
type_init(digic_timer_register_type)

View File

@ -0,0 +1,35 @@
#ifndef ALLWINNER_H_
#include "qemu-common.h"
#include "qemu/error-report.h"
#include "hw/char/serial.h"
#include "hw/arm/arm.h"
#include "hw/timer/allwinner-a10-pit.h"
#include "hw/intc/allwinner-a10-pic.h"
#include "sysemu/sysemu.h"
#include "exec/address-spaces.h"
#define AW_A10_PIC_REG_BASE 0x01c20400
#define AW_A10_PIT_REG_BASE 0x01c20c00
#define AW_A10_UART0_REG_BASE 0x01c28000
#define AW_A10_SDRAM_BASE 0x40000000
#define TYPE_AW_A10 "allwinner-a10"
#define AW_A10(obj) OBJECT_CHECK(AwA10State, (obj), TYPE_AW_A10)
typedef struct AwA10State {
/*< private >*/
DeviceState parent_obj;
/*< public >*/
ARMCPU cpu;
qemu_irq irq[AW_A10_PIC_INT_NR];
AwA10PITState timer;
AwA10PICState intc;
} AwA10State;
#define ALLWINNER_H_
#endif

43
include/hw/arm/digic.h Normal file
View File

@ -0,0 +1,43 @@
/*
* Misc Canon DIGIC declarations.
*
* Copyright (C) 2013 Antony Pavlov <antonynpavlov@gmail.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.
*
*/
#ifndef HW_ARM_DIGIC_H
#define HW_ARM_DIGIC_H
#include "cpu.h"
#include "hw/timer/digic-timer.h"
#include "hw/char/digic-uart.h"
#define TYPE_DIGIC "digic"
#define DIGIC(obj) OBJECT_CHECK(DigicState, (obj), TYPE_DIGIC)
#define DIGIC4_NB_TIMERS 3
typedef struct DigicState {
/*< private >*/
DeviceState parent_obj;
/*< public >*/
ARMCPU cpu;
DigicTimerState timer[DIGIC4_NB_TIMERS];
DigicUartState uart;
} DigicState;
#endif /* HW_ARM_DIGIC_H */

View File

@ -0,0 +1,47 @@
/*
* Canon DIGIC UART block declarations.
*
* Copyright (C) 2013 Antony Pavlov <antonynpavlov@gmail.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.
*
*/
#ifndef HW_CHAR_DIGIC_UART_H
#define HW_CHAR_DIGIC_UART_H
#include "hw/sysbus.h"
#include "qemu/typedefs.h"
#define TYPE_DIGIC_UART "digic-uart"
#define DIGIC_UART(obj) \
OBJECT_CHECK(DigicUartState, (obj), TYPE_DIGIC_UART)
enum {
R_TX = 0x00,
R_RX,
R_ST = (0x14 >> 2),
R_MAX
};
typedef struct DigicUartState {
/*< private >*/
SysBusDevice parent_obj;
/*< public >*/
MemoryRegion regs_region;
CharDriverState *chr;
uint32_t reg_rx;
uint32_t reg_st;
} DigicUartState;
#endif /* HW_CHAR_DIGIC_UART_H */

View File

@ -0,0 +1,40 @@
#ifndef AW_A10_PIC_H
#define AW_A10_PIC_H
#define TYPE_AW_A10_PIC "allwinner-a10-pic"
#define AW_A10_PIC(obj) OBJECT_CHECK(AwA10PICState, (obj), TYPE_AW_A10_PIC)
#define AW_A10_PIC_VECTOR 0
#define AW_A10_PIC_BASE_ADDR 4
#define AW_A10_PIC_PROTECT 8
#define AW_A10_PIC_NMI 0xc
#define AW_A10_PIC_IRQ_PENDING 0x10
#define AW_A10_PIC_FIQ_PENDING 0x20
#define AW_A10_PIC_SELECT 0x30
#define AW_A10_PIC_ENABLE 0x40
#define AW_A10_PIC_MASK 0x50
#define AW_A10_PIC_INT_NR 95
#define AW_A10_PIC_REG_NUM DIV_ROUND_UP(AW_A10_PIC_INT_NR, 32)
typedef struct AwA10PICState {
/*< private >*/
SysBusDevice parent_obj;
/*< public >*/
MemoryRegion iomem;
qemu_irq parent_fiq;
qemu_irq parent_irq;
uint32_t vector;
uint32_t base_addr;
uint32_t protect;
uint32_t nmi;
uint32_t irq_pending[AW_A10_PIC_REG_NUM];
uint32_t fiq_pending[AW_A10_PIC_REG_NUM];
uint32_t select[AW_A10_PIC_REG_NUM];
uint32_t enable[AW_A10_PIC_REG_NUM];
uint32_t mask[AW_A10_PIC_REG_NUM];
/*priority setting here*/
} AwA10PICState;
#endif

View File

@ -36,4 +36,8 @@ extern const VMStateDescription vmstate_ptimer;
.offset = vmstate_offset_pointer(_state, _field, ptimer_state), \
}
#define VMSTATE_PTIMER_ARRAY(_f, _s, _n) \
VMSTATE_ARRAY_OF_POINTER_TO_STRUCT(_f, _s, _n, 0, \
vmstate_ptimer, ptimer_state)
#endif

View File

@ -0,0 +1,58 @@
#ifndef AW_A10_PIT_H
#define AW_A10_PIT_H
#include "hw/ptimer.h"
#define TYPE_AW_A10_PIT "allwinner-A10-timer"
#define AW_A10_PIT(obj) OBJECT_CHECK(AwA10PITState, (obj), TYPE_AW_A10_PIT)
#define AW_A10_PIT_TIMER_NR 6
#define AW_A10_PIT_TIMER_IRQ 0x1
#define AW_A10_PIT_WDOG_IRQ 0x100
#define AW_A10_PIT_TIMER_IRQ_EN 0
#define AW_A10_PIT_TIMER_IRQ_ST 0x4
#define AW_A10_PIT_TIMER_CONTROL 0x0
#define AW_A10_PIT_TIMER_EN 0x1
#define AW_A10_PIT_TIMER_RELOAD 0x2
#define AW_A10_PIT_TIMER_MODE 0x80
#define AW_A10_PIT_TIMER_INTERVAL 0x4
#define AW_A10_PIT_TIMER_COUNT 0x8
#define AW_A10_PIT_WDOG_CONTROL 0x90
#define AW_A10_PIT_WDOG_MODE 0x94
#define AW_A10_PIT_COUNT_CTL 0xa0
#define AW_A10_PIT_COUNT_RL_EN 0x2
#define AW_A10_PIT_COUNT_CLR_EN 0x1
#define AW_A10_PIT_COUNT_LO 0xa4
#define AW_A10_PIT_COUNT_HI 0xa8
#define AW_A10_PIT_TIMER_BASE 0x10
#define AW_A10_PIT_TIMER_BASE_END \
(AW_A10_PIT_TIMER_BASE * 6 + AW_A10_PIT_TIMER_COUNT)
#define AW_A10_PIT_DEFAULT_CLOCK 0x4
typedef struct AwA10PITState {
/*< private >*/
SysBusDevice parent_obj;
/*< public >*/
qemu_irq irq[AW_A10_PIT_TIMER_NR];
ptimer_state * timer[AW_A10_PIT_TIMER_NR];
MemoryRegion iomem;
uint32_t irq_enable;
uint32_t irq_status;
uint32_t control[AW_A10_PIT_TIMER_NR];
uint32_t interval[AW_A10_PIT_TIMER_NR];
uint32_t count[AW_A10_PIT_TIMER_NR];
uint32_t watch_dog_mode;
uint32_t watch_dog_control;
uint32_t count_lo;
uint32_t count_hi;
uint32_t count_ctl;
} AwA10PITState;
#endif

View File

@ -0,0 +1,46 @@
/*
* Canon DIGIC timer block declarations.
*
* Copyright (C) 2013 Antony Pavlov <antonynpavlov@gmail.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.
*
*/
#ifndef HW_TIMER_DIGIC_TIMER_H
#define HW_TIMER_DIGIC_TIMER_H
#include "hw/sysbus.h"
#include "qemu/typedefs.h"
#include "hw/ptimer.h"
#define TYPE_DIGIC_TIMER "digic-timer"
#define DIGIC_TIMER(obj) OBJECT_CHECK(DigicTimerState, (obj), TYPE_DIGIC_TIMER)
#define DIGIC_TIMER_CONTROL 0x00
#define DIGIC_TIMER_CONTROL_RST 0x80000000
#define DIGIC_TIMER_CONTROL_EN 0x00000001
#define DIGIC_TIMER_RELVALUE 0x08
#define DIGIC_TIMER_VALUE 0x0c
typedef struct DigicTimerState {
/*< private >*/
SysBusDevice parent_obj;
/*< public >*/
MemoryRegion iomem;
ptimer_state *ptimer;
uint32_t control;
uint32_t relvalue;
} DigicTimerState;
#endif /* HW_TIMER_DIGIC_TIMER_H */

View File

@ -339,6 +339,16 @@ extern const VMStateInfo vmstate_info_bitmap;
.offset = vmstate_offset_array(_state, _field, _type, _num), \
}
#define VMSTATE_ARRAY_OF_POINTER_TO_STRUCT(_f, _s, _n, _v, _vmsd, _type) { \
.name = (stringify(_f)), \
.version_id = (_v), \
.num = (_n), \
.vmsd = &(_vmsd), \
.size = sizeof(_type *), \
.flags = VMS_ARRAY|VMS_STRUCT|VMS_ARRAY_OF_POINTER, \
.offset = vmstate_offset_array(_s, _f, _type*, _n), \
}
#define VMSTATE_STRUCT_ARRAY_TEST(_field, _state, _num, _test, _version, _vmsd, _type) { \
.name = (stringify(_field)), \
.num = (_num), \

View File

@ -227,6 +227,38 @@ static inline int cto64(uint64_t val)
return ctz64(~val);
}
/**
* clrsb32 - count leading redundant sign bits in a 32-bit value.
* @val: The value to search
*
* Returns the number of bits following the sign bit that are equal to it.
* No special cases; output range is [0-31].
*/
static inline int clrsb32(uint32_t val)
{
#if QEMU_GNUC_PREREQ(4, 7)
return __builtin_clrsb(val);
#else
return clz32(val ^ ((int32_t)val >> 1)) - 1;
#endif
}
/**
* clrsb64 - count leading redundant sign bits in a 64-bit value.
* @val: The value to search
*
* Returns the number of bits following the sign bit that are equal to it.
* No special cases; output range is [0-63].
*/
static inline int clrsb64(uint64_t val)
{
#if QEMU_GNUC_PREREQ(4, 7)
return __builtin_clrsbll(val);
#else
return clz64(val ^ ((int64_t)val >> 1)) - 1;
#endif
}
/**
* ctpop8 - count the population of one bits in an 8-bit value.
* @val: The value to search

View File

@ -1171,7 +1171,7 @@ static int target_setup_sigframe(struct target_rt_sigframe *sf,
}
__put_user(env->xregs[31], &sf->uc.tuc_mcontext.sp);
__put_user(env->pc, &sf->uc.tuc_mcontext.pc);
__put_user(env->pstate, &sf->uc.tuc_mcontext.pstate);
__put_user(pstate_read(env), &sf->uc.tuc_mcontext.pstate);
__put_user(/*current->thread.fault_address*/ 0,
&sf->uc.tuc_mcontext.fault_address);
@ -1210,6 +1210,7 @@ static int target_restore_sigframe(CPUARMState *env,
struct target_aux_context *aux =
(struct target_aux_context *)sf->uc.tuc_mcontext.__reserved;
uint32_t magic, size;
uint64_t pstate;
target_to_host_sigset(&set, &sf->uc.tuc_sigmask);
sigprocmask(SIG_SETMASK, &set, NULL);
@ -1220,7 +1221,8 @@ static int target_restore_sigframe(CPUARMState *env,
__get_user(env->xregs[31], &sf->uc.tuc_mcontext.sp);
__get_user(env->pc, &sf->uc.tuc_mcontext.pc);
__get_user(env->pstate, &sf->uc.tuc_mcontext.pstate);
__get_user(pstate, &sf->uc.tuc_mcontext.pstate);
pstate_write(env, pstate);
__get_user(magic, &aux->fpsimd.head.magic);
__get_user(size, &aux->fpsimd.head.size);

View File

@ -1,8 +1,11 @@
obj-y += arm-semi.o
obj-$(CONFIG_SOFTMMU) += machine.o
obj-$(CONFIG_KVM) += kvm.o
obj-$(call land,$(CONFIG_KVM),$(call lnot,$(TARGET_AARCH64))) += kvm32.o
obj-$(call land,$(CONFIG_KVM),$(TARGET_AARCH64)) += kvm64.o
obj-$(call lnot,$(CONFIG_KVM)) += kvm-stub.o
obj-y += translate.o op_helper.o helper.o cpu.o
obj-y += neon_helper.o iwmmxt_helper.o
obj-y += gdbstub.o
obj-$(TARGET_AARCH64) += cpu64.o translate-a64.o gdbstub64.o
obj-$(TARGET_AARCH64) += cpu64.o translate-a64.o helper-a64.o gdbstub64.o
obj-y += crypto_helper.o

View File

@ -139,6 +139,7 @@ typedef struct ARMCPU {
uint32_t ccsidr[16];
uint32_t reset_cbar;
uint32_t reset_auxcr;
bool reset_hivecs;
} ARMCPU;
#define TYPE_AARCH64_CPU "aarch64-cpu"

View File

@ -21,6 +21,7 @@
#include "cpu.h"
#include "qemu-common.h"
#include "hw/qdev-properties.h"
#include "qapi/qmp/qerror.h"
#if !defined(CONFIG_USER_ONLY)
#include "hw/loader.h"
#endif
@ -88,6 +89,12 @@ static void arm_cpu_reset(CPUState *s)
if (arm_feature(env, ARM_FEATURE_AARCH64)) {
/* 64 bit CPUs always start in 64 bit mode */
env->aarch64 = 1;
#if defined(CONFIG_USER_ONLY)
env->pstate = PSTATE_MODE_EL0t;
#else
env->pstate = PSTATE_D | PSTATE_A | PSTATE_I | PSTATE_F
| PSTATE_MODE_EL1h;
#endif
}
#if defined(CONFIG_USER_ONLY)
@ -120,6 +127,11 @@ static void arm_cpu_reset(CPUState *s)
env->regs[15] = pc & ~1;
}
}
if (env->cp15.c1_sys & (1 << 13)) {
env->regs[15] = 0xFFFF0000;
}
env->vfp.xregs[ARM_VFP_FPEXC] = 0;
#endif
set_flush_to_zero(1, &env->vfp.standard_fp_status);
@ -231,6 +243,30 @@ static void arm_cpu_initfn(Object *obj)
}
}
static Property arm_cpu_reset_cbar_property =
DEFINE_PROP_UINT32("reset-cbar", ARMCPU, reset_cbar, 0);
static Property arm_cpu_reset_hivecs_property =
DEFINE_PROP_BOOL("reset-hivecs", ARMCPU, reset_hivecs, false);
static void arm_cpu_post_init(Object *obj)
{
ARMCPU *cpu = ARM_CPU(obj);
Error *err = NULL;
if (arm_feature(&cpu->env, ARM_FEATURE_CBAR)) {
qdev_property_add_static(DEVICE(obj), &arm_cpu_reset_cbar_property,
&err);
assert_no_error(err);
}
if (!arm_feature(&cpu->env, ARM_FEATURE_M)) {
qdev_property_add_static(DEVICE(obj), &arm_cpu_reset_hivecs_property,
&err);
assert_no_error(err);
}
}
static void arm_cpu_finalizefn(Object *obj)
{
ARMCPU *cpu = ARM_CPU(obj);
@ -249,6 +285,7 @@ static void arm_cpu_realizefn(DeviceState *dev, Error **errp)
set_feature(env, ARM_FEATURE_V7);
set_feature(env, ARM_FEATURE_ARM_DIV);
set_feature(env, ARM_FEATURE_LPAE);
set_feature(env, ARM_FEATURE_V8_AES);
}
if (arm_feature(env, ARM_FEATURE_V7)) {
set_feature(env, ARM_FEATURE_VAPA);
@ -290,6 +327,10 @@ static void arm_cpu_realizefn(DeviceState *dev, Error **errp)
set_feature(env, ARM_FEATURE_PXN);
}
if (cpu->reset_hivecs) {
cpu->reset_sctlr |= (1 << 13);
}
register_cp_regs_for_features(cpu);
arm_cpu_register_gdb_regs_for_features(cpu);
@ -616,6 +657,7 @@ static void cortex_a9_initfn(Object *obj)
* and valid configurations; we don't model A9UP).
*/
set_feature(&cpu->env, ARM_FEATURE_V7MP);
set_feature(&cpu->env, ARM_FEATURE_CBAR);
cpu->midr = 0x410fc090;
cpu->reset_fpsid = 0x41033090;
cpu->mvfr0 = 0x11110222;
@ -638,15 +680,7 @@ static void cortex_a9_initfn(Object *obj)
cpu->clidr = (1 << 27) | (1 << 24) | 3;
cpu->ccsidr[0] = 0xe00fe015; /* 16k L1 dcache. */
cpu->ccsidr[1] = 0x200fe015; /* 16k L1 icache. */
{
ARMCPRegInfo cbar = {
.name = "CBAR", .cp = 15, .crn = 15, .crm = 0, .opc1 = 4,
.opc2 = 0, .access = PL1_R|PL3_W, .resetvalue = cpu->reset_cbar,
.fieldoffset = offsetof(CPUARMState, cp15.c15_config_base_address)
};
define_one_arm_cp_reg(cpu, &cbar);
define_arm_cp_regs(cpu, cortexa9_cp_reginfo);
}
define_arm_cp_regs(cpu, cortexa9_cp_reginfo);
}
#ifndef CONFIG_USER_ONLY
@ -685,6 +719,7 @@ static void cortex_a15_initfn(Object *obj)
set_feature(&cpu->env, ARM_FEATURE_ARM_DIV);
set_feature(&cpu->env, ARM_FEATURE_GENERIC_TIMER);
set_feature(&cpu->env, ARM_FEATURE_DUMMY_C15_REGS);
set_feature(&cpu->env, ARM_FEATURE_CBAR);
set_feature(&cpu->env, ARM_FEATURE_LPAE);
cpu->kvm_target = QEMU_KVM_ARM_TARGET_CORTEX_A15;
cpu->midr = 0x412fc0f1;
@ -999,6 +1034,7 @@ static const TypeInfo arm_cpu_type_info = {
.parent = TYPE_CPU,
.instance_size = sizeof(ARMCPU),
.instance_init = arm_cpu_initfn,
.instance_post_init = arm_cpu_post_init,
.instance_finalize = arm_cpu_finalizefn,
.abstract = true,
.class_size = sizeof(ARMCPUClass),

View File

@ -113,8 +113,15 @@ typedef struct CPUARMState {
/* Regs for A64 mode. */
uint64_t xregs[32];
uint64_t pc;
/* TODO: pstate doesn't correspond to an architectural register;
* it would be better modelled as the underlying fields.
/* PSTATE isn't an architectural register for ARMv8. However, it is
* convenient for us to assemble the underlying state into a 32 bit format
* identical to the architectural format used for the SPSR. (This is also
* what the Linux kernel's 'pstate' field in signal handlers and KVM's
* 'pstate' register are.) Of the PSTATE bits:
* NZCV are kept in the split out env->CF/VF/NF/ZF, (which have the same
* semantics as for AArch32, as described in the comments on each field)
* nRW (also known as M[4]) is kept, inverted, in env->aarch64
* all other bits are stored in their correct places in env->pstate
*/
uint32_t pstate;
uint32_t aarch64; /* 1 if CPU is in aarch64 state; inverse of PSTATE.nRW */
@ -309,15 +316,6 @@ static inline bool is_a64(CPUARMState *env)
return env->aarch64;
}
#define PSTATE_N_SHIFT 3
#define PSTATE_N (1 << PSTATE_N_SHIFT)
#define PSTATE_Z_SHIFT 2
#define PSTATE_Z (1 << PSTATE_Z_SHIFT)
#define PSTATE_C_SHIFT 1
#define PSTATE_C (1 << PSTATE_C_SHIFT)
#define PSTATE_V_SHIFT 0
#define PSTATE_V (1 << PSTATE_V_SHIFT)
/* you can call this signal handler from your SIGBUS and SIGSEGV
signal handlers to inform the virtual CPU of exceptions. non zero
is returned if the signal was handled by the virtual CPU. */
@ -352,6 +350,56 @@ int cpu_arm_handle_mmu_fault (CPUARMState *env, target_ulong address, int rw,
/* Execution state bits. MRS read as zero, MSR writes ignored. */
#define CPSR_EXEC (CPSR_T | CPSR_IT | CPSR_J)
/* Bit definitions for ARMv8 SPSR (PSTATE) format.
* Only these are valid when in AArch64 mode; in
* AArch32 mode SPSRs are basically CPSR-format.
*/
#define PSTATE_M (0xFU)
#define PSTATE_nRW (1U << 4)
#define PSTATE_F (1U << 6)
#define PSTATE_I (1U << 7)
#define PSTATE_A (1U << 8)
#define PSTATE_D (1U << 9)
#define PSTATE_IL (1U << 20)
#define PSTATE_SS (1U << 21)
#define PSTATE_V (1U << 28)
#define PSTATE_C (1U << 29)
#define PSTATE_Z (1U << 30)
#define PSTATE_N (1U << 31)
#define PSTATE_NZCV (PSTATE_N | PSTATE_Z | PSTATE_C | PSTATE_V)
#define CACHED_PSTATE_BITS (PSTATE_NZCV)
/* Mode values for AArch64 */
#define PSTATE_MODE_EL3h 13
#define PSTATE_MODE_EL3t 12
#define PSTATE_MODE_EL2h 9
#define PSTATE_MODE_EL2t 8
#define PSTATE_MODE_EL1h 5
#define PSTATE_MODE_EL1t 4
#define PSTATE_MODE_EL0t 0
/* Return the current PSTATE value. For the moment we don't support 32<->64 bit
* interprocessing, so we don't attempt to sync with the cpsr state used by
* the 32 bit decoder.
*/
static inline uint32_t pstate_read(CPUARMState *env)
{
int ZF;
ZF = (env->ZF == 0);
return (env->NF & 0x80000000) | (ZF << 30)
| (env->CF << 29) | ((env->VF & 0x80000000) >> 3)
| env->pstate;
}
static inline void pstate_write(CPUARMState *env, uint32_t val)
{
env->ZF = (~val) & PSTATE_Z;
env->NF = val;
env->CF = (val >> 29) & 1;
env->VF = (val << 3) & 0x80000000;
env->pstate = val & ~CACHED_PSTATE_BITS;
}
/* Return the current CPSR value. */
uint32_t cpsr_read(CPUARMState *env);
/* Set the CPSR. Note that some bits of mask must be all-set or all-clear. */
@ -399,6 +447,34 @@ static inline void xpsr_write(CPUARMState *env, uint32_t val, uint32_t mask)
uint32_t vfp_get_fpscr(CPUARMState *env);
void vfp_set_fpscr(CPUARMState *env, uint32_t val);
/* For A64 the FPSCR is split into two logically distinct registers,
* FPCR and FPSR. However since they still use non-overlapping bits
* we store the underlying state in fpscr and just mask on read/write.
*/
#define FPSR_MASK 0xf800009f
#define FPCR_MASK 0x07f79f00
static inline uint32_t vfp_get_fpsr(CPUARMState *env)
{
return vfp_get_fpscr(env) & FPSR_MASK;
}
static inline void vfp_set_fpsr(CPUARMState *env, uint32_t val)
{
uint32_t new_fpscr = (vfp_get_fpscr(env) & ~FPSR_MASK) | (val & FPSR_MASK);
vfp_set_fpscr(env, new_fpscr);
}
static inline uint32_t vfp_get_fpcr(CPUARMState *env)
{
return vfp_get_fpscr(env) & FPCR_MASK;
}
static inline void vfp_set_fpcr(CPUARMState *env, uint32_t val)
{
uint32_t new_fpscr = (vfp_get_fpscr(env) & ~FPCR_MASK) | (val & FPCR_MASK);
vfp_set_fpscr(env, new_fpscr);
}
enum arm_cpu_mode {
ARM_CPU_MODE_USR = 0x10,
ARM_CPU_MODE_FIQ = 0x11,
@ -467,6 +543,8 @@ enum arm_features {
ARM_FEATURE_LPAE, /* has Large Physical Address Extension */
ARM_FEATURE_V8,
ARM_FEATURE_AARCH64, /* supports 64 bit mode */
ARM_FEATURE_V8_AES, /* implements AES part of v8 Crypto Extensions */
ARM_FEATURE_CBAR, /* has cp15 CBAR */
};
static inline int arm_feature(CPUARMState *env, int feature)

View File

@ -68,11 +68,22 @@ static void aarch64_cpu_finalizefn(Object *obj)
{
}
static void aarch64_cpu_set_pc(CPUState *cs, vaddr value)
{
ARMCPU *cpu = ARM_CPU(cs);
/*
* TODO: this will need updating for system emulation,
* when the core may be in AArch32 mode.
*/
cpu->env.pc = value;
}
static void aarch64_cpu_class_init(ObjectClass *oc, void *data)
{
CPUClass *cc = CPU_CLASS(oc);
cc->dump_state = aarch64_cpu_dump_state;
cc->set_pc = aarch64_cpu_set_pc;
cc->gdb_read_register = aarch64_cpu_gdb_read_register;
cc->gdb_write_register = aarch64_cpu_gdb_write_register;
cc->gdb_num_core_regs = 34;

281
target-arm/crypto_helper.c Normal file
View File

@ -0,0 +1,281 @@
/*
* crypto_helper.c - emulate v8 Crypto Extensions instructions
*
* Copyright (C) 2013 Linaro Ltd <ard.biesheuvel@linaro.org>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*/
#include <stdlib.h>
#include "cpu.h"
#include "exec/exec-all.h"
#include "helper.h"
union AES_STATE {
uint8_t bytes[16];
uint32_t cols[4];
uint64_t l[2];
};
void HELPER(crypto_aese)(CPUARMState *env, uint32_t rd, uint32_t rm,
uint32_t decrypt)
{
static uint8_t const sbox[][256] = { {
/* S-box for encryption */
0x63, 0x7c, 0x77, 0x7b, 0xf2, 0x6b, 0x6f, 0xc5,
0x30, 0x01, 0x67, 0x2b, 0xfe, 0xd7, 0xab, 0x76,
0xca, 0x82, 0xc9, 0x7d, 0xfa, 0x59, 0x47, 0xf0,
0xad, 0xd4, 0xa2, 0xaf, 0x9c, 0xa4, 0x72, 0xc0,
0xb7, 0xfd, 0x93, 0x26, 0x36, 0x3f, 0xf7, 0xcc,
0x34, 0xa5, 0xe5, 0xf1, 0x71, 0xd8, 0x31, 0x15,
0x04, 0xc7, 0x23, 0xc3, 0x18, 0x96, 0x05, 0x9a,
0x07, 0x12, 0x80, 0xe2, 0xeb, 0x27, 0xb2, 0x75,
0x09, 0x83, 0x2c, 0x1a, 0x1b, 0x6e, 0x5a, 0xa0,
0x52, 0x3b, 0xd6, 0xb3, 0x29, 0xe3, 0x2f, 0x84,
0x53, 0xd1, 0x00, 0xed, 0x20, 0xfc, 0xb1, 0x5b,
0x6a, 0xcb, 0xbe, 0x39, 0x4a, 0x4c, 0x58, 0xcf,
0xd0, 0xef, 0xaa, 0xfb, 0x43, 0x4d, 0x33, 0x85,
0x45, 0xf9, 0x02, 0x7f, 0x50, 0x3c, 0x9f, 0xa8,
0x51, 0xa3, 0x40, 0x8f, 0x92, 0x9d, 0x38, 0xf5,
0xbc, 0xb6, 0xda, 0x21, 0x10, 0xff, 0xf3, 0xd2,
0xcd, 0x0c, 0x13, 0xec, 0x5f, 0x97, 0x44, 0x17,
0xc4, 0xa7, 0x7e, 0x3d, 0x64, 0x5d, 0x19, 0x73,
0x60, 0x81, 0x4f, 0xdc, 0x22, 0x2a, 0x90, 0x88,
0x46, 0xee, 0xb8, 0x14, 0xde, 0x5e, 0x0b, 0xdb,
0xe0, 0x32, 0x3a, 0x0a, 0x49, 0x06, 0x24, 0x5c,
0xc2, 0xd3, 0xac, 0x62, 0x91, 0x95, 0xe4, 0x79,
0xe7, 0xc8, 0x37, 0x6d, 0x8d, 0xd5, 0x4e, 0xa9,
0x6c, 0x56, 0xf4, 0xea, 0x65, 0x7a, 0xae, 0x08,
0xba, 0x78, 0x25, 0x2e, 0x1c, 0xa6, 0xb4, 0xc6,
0xe8, 0xdd, 0x74, 0x1f, 0x4b, 0xbd, 0x8b, 0x8a,
0x70, 0x3e, 0xb5, 0x66, 0x48, 0x03, 0xf6, 0x0e,
0x61, 0x35, 0x57, 0xb9, 0x86, 0xc1, 0x1d, 0x9e,
0xe1, 0xf8, 0x98, 0x11, 0x69, 0xd9, 0x8e, 0x94,
0x9b, 0x1e, 0x87, 0xe9, 0xce, 0x55, 0x28, 0xdf,
0x8c, 0xa1, 0x89, 0x0d, 0xbf, 0xe6, 0x42, 0x68,
0x41, 0x99, 0x2d, 0x0f, 0xb0, 0x54, 0xbb, 0x16
}, {
/* S-box for decryption */
0x52, 0x09, 0x6a, 0xd5, 0x30, 0x36, 0xa5, 0x38,
0xbf, 0x40, 0xa3, 0x9e, 0x81, 0xf3, 0xd7, 0xfb,
0x7c, 0xe3, 0x39, 0x82, 0x9b, 0x2f, 0xff, 0x87,
0x34, 0x8e, 0x43, 0x44, 0xc4, 0xde, 0xe9, 0xcb,
0x54, 0x7b, 0x94, 0x32, 0xa6, 0xc2, 0x23, 0x3d,
0xee, 0x4c, 0x95, 0x0b, 0x42, 0xfa, 0xc3, 0x4e,
0x08, 0x2e, 0xa1, 0x66, 0x28, 0xd9, 0x24, 0xb2,
0x76, 0x5b, 0xa2, 0x49, 0x6d, 0x8b, 0xd1, 0x25,
0x72, 0xf8, 0xf6, 0x64, 0x86, 0x68, 0x98, 0x16,
0xd4, 0xa4, 0x5c, 0xcc, 0x5d, 0x65, 0xb6, 0x92,
0x6c, 0x70, 0x48, 0x50, 0xfd, 0xed, 0xb9, 0xda,
0x5e, 0x15, 0x46, 0x57, 0xa7, 0x8d, 0x9d, 0x84,
0x90, 0xd8, 0xab, 0x00, 0x8c, 0xbc, 0xd3, 0x0a,
0xf7, 0xe4, 0x58, 0x05, 0xb8, 0xb3, 0x45, 0x06,
0xd0, 0x2c, 0x1e, 0x8f, 0xca, 0x3f, 0x0f, 0x02,
0xc1, 0xaf, 0xbd, 0x03, 0x01, 0x13, 0x8a, 0x6b,
0x3a, 0x91, 0x11, 0x41, 0x4f, 0x67, 0xdc, 0xea,
0x97, 0xf2, 0xcf, 0xce, 0xf0, 0xb4, 0xe6, 0x73,
0x96, 0xac, 0x74, 0x22, 0xe7, 0xad, 0x35, 0x85,
0xe2, 0xf9, 0x37, 0xe8, 0x1c, 0x75, 0xdf, 0x6e,
0x47, 0xf1, 0x1a, 0x71, 0x1d, 0x29, 0xc5, 0x89,
0x6f, 0xb7, 0x62, 0x0e, 0xaa, 0x18, 0xbe, 0x1b,
0xfc, 0x56, 0x3e, 0x4b, 0xc6, 0xd2, 0x79, 0x20,
0x9a, 0xdb, 0xc0, 0xfe, 0x78, 0xcd, 0x5a, 0xf4,
0x1f, 0xdd, 0xa8, 0x33, 0x88, 0x07, 0xc7, 0x31,
0xb1, 0x12, 0x10, 0x59, 0x27, 0x80, 0xec, 0x5f,
0x60, 0x51, 0x7f, 0xa9, 0x19, 0xb5, 0x4a, 0x0d,
0x2d, 0xe5, 0x7a, 0x9f, 0x93, 0xc9, 0x9c, 0xef,
0xa0, 0xe0, 0x3b, 0x4d, 0xae, 0x2a, 0xf5, 0xb0,
0xc8, 0xeb, 0xbb, 0x3c, 0x83, 0x53, 0x99, 0x61,
0x17, 0x2b, 0x04, 0x7e, 0xba, 0x77, 0xd6, 0x26,
0xe1, 0x69, 0x14, 0x63, 0x55, 0x21, 0x0c, 0x7d
} };
static uint8_t const shift[][16] = {
/* ShiftRows permutation vector for encryption */
{ 0, 5, 10, 15, 4, 9, 14, 3, 8, 13, 2, 7, 12, 1, 6, 11 },
/* ShiftRows permutation vector for decryption */
{ 0, 13, 10, 7, 4, 1, 14, 11, 8, 5, 2, 15, 12, 9, 6, 3 },
};
union AES_STATE rk = { .l = {
float64_val(env->vfp.regs[rm]),
float64_val(env->vfp.regs[rm + 1])
} };
union AES_STATE st = { .l = {
float64_val(env->vfp.regs[rd]),
float64_val(env->vfp.regs[rd + 1])
} };
int i;
assert(decrypt < 2);
/* xor state vector with round key */
rk.l[0] ^= st.l[0];
rk.l[1] ^= st.l[1];
/* combine ShiftRows operation and sbox substitution */
for (i = 0; i < 16; i++) {
st.bytes[i] = sbox[decrypt][rk.bytes[shift[decrypt][i]]];
}
env->vfp.regs[rd] = make_float64(st.l[0]);
env->vfp.regs[rd + 1] = make_float64(st.l[1]);
}
void HELPER(crypto_aesmc)(CPUARMState *env, uint32_t rd, uint32_t rm,
uint32_t decrypt)
{
static uint32_t const mc[][256] = { {
/* MixColumns lookup table */
0x00000000, 0x03010102, 0x06020204, 0x05030306,
0x0c040408, 0x0f05050a, 0x0a06060c, 0x0907070e,
0x18080810, 0x1b090912, 0x1e0a0a14, 0x1d0b0b16,
0x140c0c18, 0x170d0d1a, 0x120e0e1c, 0x110f0f1e,
0x30101020, 0x33111122, 0x36121224, 0x35131326,
0x3c141428, 0x3f15152a, 0x3a16162c, 0x3917172e,
0x28181830, 0x2b191932, 0x2e1a1a34, 0x2d1b1b36,
0x241c1c38, 0x271d1d3a, 0x221e1e3c, 0x211f1f3e,
0x60202040, 0x63212142, 0x66222244, 0x65232346,
0x6c242448, 0x6f25254a, 0x6a26264c, 0x6927274e,
0x78282850, 0x7b292952, 0x7e2a2a54, 0x7d2b2b56,
0x742c2c58, 0x772d2d5a, 0x722e2e5c, 0x712f2f5e,
0x50303060, 0x53313162, 0x56323264, 0x55333366,
0x5c343468, 0x5f35356a, 0x5a36366c, 0x5937376e,
0x48383870, 0x4b393972, 0x4e3a3a74, 0x4d3b3b76,
0x443c3c78, 0x473d3d7a, 0x423e3e7c, 0x413f3f7e,
0xc0404080, 0xc3414182, 0xc6424284, 0xc5434386,
0xcc444488, 0xcf45458a, 0xca46468c, 0xc947478e,
0xd8484890, 0xdb494992, 0xde4a4a94, 0xdd4b4b96,
0xd44c4c98, 0xd74d4d9a, 0xd24e4e9c, 0xd14f4f9e,
0xf05050a0, 0xf35151a2, 0xf65252a4, 0xf55353a6,
0xfc5454a8, 0xff5555aa, 0xfa5656ac, 0xf95757ae,
0xe85858b0, 0xeb5959b2, 0xee5a5ab4, 0xed5b5bb6,
0xe45c5cb8, 0xe75d5dba, 0xe25e5ebc, 0xe15f5fbe,
0xa06060c0, 0xa36161c2, 0xa66262c4, 0xa56363c6,
0xac6464c8, 0xaf6565ca, 0xaa6666cc, 0xa96767ce,
0xb86868d0, 0xbb6969d2, 0xbe6a6ad4, 0xbd6b6bd6,
0xb46c6cd8, 0xb76d6dda, 0xb26e6edc, 0xb16f6fde,
0x907070e0, 0x937171e2, 0x967272e4, 0x957373e6,
0x9c7474e8, 0x9f7575ea, 0x9a7676ec, 0x997777ee,
0x887878f0, 0x8b7979f2, 0x8e7a7af4, 0x8d7b7bf6,
0x847c7cf8, 0x877d7dfa, 0x827e7efc, 0x817f7ffe,
0x9b80801b, 0x98818119, 0x9d82821f, 0x9e83831d,
0x97848413, 0x94858511, 0x91868617, 0x92878715,
0x8388880b, 0x80898909, 0x858a8a0f, 0x868b8b0d,
0x8f8c8c03, 0x8c8d8d01, 0x898e8e07, 0x8a8f8f05,
0xab90903b, 0xa8919139, 0xad92923f, 0xae93933d,
0xa7949433, 0xa4959531, 0xa1969637, 0xa2979735,
0xb398982b, 0xb0999929, 0xb59a9a2f, 0xb69b9b2d,
0xbf9c9c23, 0xbc9d9d21, 0xb99e9e27, 0xba9f9f25,
0xfba0a05b, 0xf8a1a159, 0xfda2a25f, 0xfea3a35d,
0xf7a4a453, 0xf4a5a551, 0xf1a6a657, 0xf2a7a755,
0xe3a8a84b, 0xe0a9a949, 0xe5aaaa4f, 0xe6abab4d,
0xefacac43, 0xecadad41, 0xe9aeae47, 0xeaafaf45,
0xcbb0b07b, 0xc8b1b179, 0xcdb2b27f, 0xceb3b37d,
0xc7b4b473, 0xc4b5b571, 0xc1b6b677, 0xc2b7b775,
0xd3b8b86b, 0xd0b9b969, 0xd5baba6f, 0xd6bbbb6d,
0xdfbcbc63, 0xdcbdbd61, 0xd9bebe67, 0xdabfbf65,
0x5bc0c09b, 0x58c1c199, 0x5dc2c29f, 0x5ec3c39d,
0x57c4c493, 0x54c5c591, 0x51c6c697, 0x52c7c795,
0x43c8c88b, 0x40c9c989, 0x45caca8f, 0x46cbcb8d,
0x4fcccc83, 0x4ccdcd81, 0x49cece87, 0x4acfcf85,
0x6bd0d0bb, 0x68d1d1b9, 0x6dd2d2bf, 0x6ed3d3bd,
0x67d4d4b3, 0x64d5d5b1, 0x61d6d6b7, 0x62d7d7b5,
0x73d8d8ab, 0x70d9d9a9, 0x75dadaaf, 0x76dbdbad,
0x7fdcdca3, 0x7cdddda1, 0x79dedea7, 0x7adfdfa5,
0x3be0e0db, 0x38e1e1d9, 0x3de2e2df, 0x3ee3e3dd,
0x37e4e4d3, 0x34e5e5d1, 0x31e6e6d7, 0x32e7e7d5,
0x23e8e8cb, 0x20e9e9c9, 0x25eaeacf, 0x26ebebcd,
0x2fececc3, 0x2cededc1, 0x29eeeec7, 0x2aefefc5,
0x0bf0f0fb, 0x08f1f1f9, 0x0df2f2ff, 0x0ef3f3fd,
0x07f4f4f3, 0x04f5f5f1, 0x01f6f6f7, 0x02f7f7f5,
0x13f8f8eb, 0x10f9f9e9, 0x15fafaef, 0x16fbfbed,
0x1ffcfce3, 0x1cfdfde1, 0x19fefee7, 0x1affffe5,
}, {
/* Inverse MixColumns lookup table */
0x00000000, 0x0b0d090e, 0x161a121c, 0x1d171b12,
0x2c342438, 0x27392d36, 0x3a2e3624, 0x31233f2a,
0x58684870, 0x5365417e, 0x4e725a6c, 0x457f5362,
0x745c6c48, 0x7f516546, 0x62467e54, 0x694b775a,
0xb0d090e0, 0xbbdd99ee, 0xa6ca82fc, 0xadc78bf2,
0x9ce4b4d8, 0x97e9bdd6, 0x8afea6c4, 0x81f3afca,
0xe8b8d890, 0xe3b5d19e, 0xfea2ca8c, 0xf5afc382,
0xc48cfca8, 0xcf81f5a6, 0xd296eeb4, 0xd99be7ba,
0x7bbb3bdb, 0x70b632d5, 0x6da129c7, 0x66ac20c9,
0x578f1fe3, 0x5c8216ed, 0x41950dff, 0x4a9804f1,
0x23d373ab, 0x28de7aa5, 0x35c961b7, 0x3ec468b9,
0x0fe75793, 0x04ea5e9d, 0x19fd458f, 0x12f04c81,
0xcb6bab3b, 0xc066a235, 0xdd71b927, 0xd67cb029,
0xe75f8f03, 0xec52860d, 0xf1459d1f, 0xfa489411,
0x9303e34b, 0x980eea45, 0x8519f157, 0x8e14f859,
0xbf37c773, 0xb43ace7d, 0xa92dd56f, 0xa220dc61,
0xf66d76ad, 0xfd607fa3, 0xe07764b1, 0xeb7a6dbf,
0xda595295, 0xd1545b9b, 0xcc434089, 0xc74e4987,
0xae053edd, 0xa50837d3, 0xb81f2cc1, 0xb31225cf,
0x82311ae5, 0x893c13eb, 0x942b08f9, 0x9f2601f7,
0x46bde64d, 0x4db0ef43, 0x50a7f451, 0x5baafd5f,
0x6a89c275, 0x6184cb7b, 0x7c93d069, 0x779ed967,
0x1ed5ae3d, 0x15d8a733, 0x08cfbc21, 0x03c2b52f,
0x32e18a05, 0x39ec830b, 0x24fb9819, 0x2ff69117,
0x8dd64d76, 0x86db4478, 0x9bcc5f6a, 0x90c15664,
0xa1e2694e, 0xaaef6040, 0xb7f87b52, 0xbcf5725c,
0xd5be0506, 0xdeb30c08, 0xc3a4171a, 0xc8a91e14,
0xf98a213e, 0xf2872830, 0xef903322, 0xe49d3a2c,
0x3d06dd96, 0x360bd498, 0x2b1ccf8a, 0x2011c684,
0x1132f9ae, 0x1a3ff0a0, 0x0728ebb2, 0x0c25e2bc,
0x656e95e6, 0x6e639ce8, 0x737487fa, 0x78798ef4,
0x495ab1de, 0x4257b8d0, 0x5f40a3c2, 0x544daacc,
0xf7daec41, 0xfcd7e54f, 0xe1c0fe5d, 0xeacdf753,
0xdbeec879, 0xd0e3c177, 0xcdf4da65, 0xc6f9d36b,
0xafb2a431, 0xa4bfad3f, 0xb9a8b62d, 0xb2a5bf23,
0x83868009, 0x888b8907, 0x959c9215, 0x9e919b1b,
0x470a7ca1, 0x4c0775af, 0x51106ebd, 0x5a1d67b3,
0x6b3e5899, 0x60335197, 0x7d244a85, 0x7629438b,
0x1f6234d1, 0x146f3ddf, 0x097826cd, 0x02752fc3,
0x335610e9, 0x385b19e7, 0x254c02f5, 0x2e410bfb,
0x8c61d79a, 0x876cde94, 0x9a7bc586, 0x9176cc88,
0xa055f3a2, 0xab58faac, 0xb64fe1be, 0xbd42e8b0,
0xd4099fea, 0xdf0496e4, 0xc2138df6, 0xc91e84f8,
0xf83dbbd2, 0xf330b2dc, 0xee27a9ce, 0xe52aa0c0,
0x3cb1477a, 0x37bc4e74, 0x2aab5566, 0x21a65c68,
0x10856342, 0x1b886a4c, 0x069f715e, 0x0d927850,
0x64d90f0a, 0x6fd40604, 0x72c31d16, 0x79ce1418,
0x48ed2b32, 0x43e0223c, 0x5ef7392e, 0x55fa3020,
0x01b79aec, 0x0aba93e2, 0x17ad88f0, 0x1ca081fe,
0x2d83bed4, 0x268eb7da, 0x3b99acc8, 0x3094a5c6,
0x59dfd29c, 0x52d2db92, 0x4fc5c080, 0x44c8c98e,
0x75ebf6a4, 0x7ee6ffaa, 0x63f1e4b8, 0x68fcedb6,
0xb1670a0c, 0xba6a0302, 0xa77d1810, 0xac70111e,
0x9d532e34, 0x965e273a, 0x8b493c28, 0x80443526,
0xe90f427c, 0xe2024b72, 0xff155060, 0xf418596e,
0xc53b6644, 0xce366f4a, 0xd3217458, 0xd82c7d56,
0x7a0ca137, 0x7101a839, 0x6c16b32b, 0x671bba25,
0x5638850f, 0x5d358c01, 0x40229713, 0x4b2f9e1d,
0x2264e947, 0x2969e049, 0x347efb5b, 0x3f73f255,
0x0e50cd7f, 0x055dc471, 0x184adf63, 0x1347d66d,
0xcadc31d7, 0xc1d138d9, 0xdcc623cb, 0xd7cb2ac5,
0xe6e815ef, 0xede51ce1, 0xf0f207f3, 0xfbff0efd,
0x92b479a7, 0x99b970a9, 0x84ae6bbb, 0x8fa362b5,
0xbe805d9f, 0xb58d5491, 0xa89a4f83, 0xa397468d,
} };
union AES_STATE st = { .l = {
float64_val(env->vfp.regs[rm]),
float64_val(env->vfp.regs[rm + 1])
} };
int i;
assert(decrypt < 2);
for (i = 0; i < 16; i += 4) {
st.cols[i >> 2] = cpu_to_le32(
mc[decrypt][st.bytes[i]] ^
rol32(mc[decrypt][st.bytes[i + 1]], 8) ^
rol32(mc[decrypt][st.bytes[i + 2]], 16) ^
rol32(mc[decrypt][st.bytes[i + 3]], 24));
}
env->vfp.regs[rd] = make_float64(st.l[0]);
env->vfp.regs[rd + 1] = make_float64(st.l[1]);
}

View File

@ -37,7 +37,7 @@ int aarch64_cpu_gdb_read_register(CPUState *cs, uint8_t *mem_buf, int n)
return gdb_get_reg64(mem_buf, env->pc);
break;
case 33:
return gdb_get_reg32(mem_buf, env->pstate);
return gdb_get_reg32(mem_buf, pstate_read(env));
}
/* Unknown register. */
return 0;
@ -65,7 +65,7 @@ int aarch64_cpu_gdb_write_register(CPUState *cs, uint8_t *mem_buf, int n)
return 8;
case 33:
/* CPSR */
env->pstate = tmp;
pstate_write(env, tmp);
return 4;
}
/* Unknown register. */

79
target-arm/helper-a64.c Normal file
View File

@ -0,0 +1,79 @@
/*
* AArch64 specific helpers
*
* Copyright (c) 2013 Alexander Graf <agraf@suse.de>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library 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
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, see <http://www.gnu.org/licenses/>.
*/
#include "cpu.h"
#include "exec/gdbstub.h"
#include "helper.h"
#include "qemu/host-utils.h"
#include "sysemu/sysemu.h"
#include "qemu/bitops.h"
/* C2.4.7 Multiply and divide */
/* special cases for 0 and LLONG_MIN are mandated by the standard */
uint64_t HELPER(udiv64)(uint64_t num, uint64_t den)
{
if (den == 0) {
return 0;
}
return num / den;
}
int64_t HELPER(sdiv64)(int64_t num, int64_t den)
{
if (den == 0) {
return 0;
}
if (num == LLONG_MIN && den == -1) {
return LLONG_MIN;
}
return num / den;
}
uint64_t HELPER(clz64)(uint64_t x)
{
return clz64(x);
}
uint64_t HELPER(cls64)(uint64_t x)
{
return clrsb64(x);
}
uint32_t HELPER(cls32)(uint32_t x)
{
return clrsb32(x);
}
uint64_t HELPER(rbit64)(uint64_t x)
{
/* assign the correct byte position */
x = bswap64(x);
/* assign the correct nibble position */
x = ((x & 0xf0f0f0f0f0f0f0f0ULL) >> 4)
| ((x & 0x0f0f0f0f0f0f0f0fULL) << 4);
/* assign the correct bit position */
x = ((x & 0x8888888888888888ULL) >> 3)
| ((x & 0x4444444444444444ULL) >> 1)
| ((x & 0x2222222222222222ULL) << 1)
| ((x & 0x1111111111111111ULL) << 3);
return x;
}

24
target-arm/helper-a64.h Normal file
View File

@ -0,0 +1,24 @@
/*
* AArch64 specific helper definitions
*
* Copyright (c) 2013 Alexander Graf <agraf@suse.de>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library 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
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, see <http://www.gnu.org/licenses/>.
*/
DEF_HELPER_FLAGS_2(udiv64, TCG_CALL_NO_RWG_SE, i64, i64, i64)
DEF_HELPER_FLAGS_2(sdiv64, TCG_CALL_NO_RWG_SE, s64, s64, s64)
DEF_HELPER_FLAGS_1(clz64, TCG_CALL_NO_RWG_SE, i64, i64)
DEF_HELPER_FLAGS_1(cls64, TCG_CALL_NO_RWG_SE, i64, i64)
DEF_HELPER_FLAGS_1(cls32, TCG_CALL_NO_RWG_SE, i32, i32)
DEF_HELPER_FLAGS_1(rbit64, TCG_CALL_NO_RWG_SE, i64, i64)

View File

@ -65,6 +65,48 @@ static int vfp_gdb_set_reg(CPUARMState *env, uint8_t *buf, int reg)
return 0;
}
static int aarch64_fpu_gdb_get_reg(CPUARMState *env, uint8_t *buf, int reg)
{
switch (reg) {
case 0 ... 31:
/* 128 bit FP register */
stfq_le_p(buf, env->vfp.regs[reg * 2]);
stfq_le_p(buf + 8, env->vfp.regs[reg * 2 + 1]);
return 16;
case 32:
/* FPSR */
stl_p(buf, vfp_get_fpsr(env));
return 4;
case 33:
/* FPCR */
stl_p(buf, vfp_get_fpcr(env));
return 4;
default:
return 0;
}
}
static int aarch64_fpu_gdb_set_reg(CPUARMState *env, uint8_t *buf, int reg)
{
switch (reg) {
case 0 ... 31:
/* 128 bit FP register */
env->vfp.regs[reg * 2] = ldfq_le_p(buf);
env->vfp.regs[reg * 2 + 1] = ldfq_le_p(buf + 8);
return 16;
case 32:
/* FPSR */
vfp_set_fpsr(env, ldl_p(buf));
return 4;
case 33:
/* FPCR */
vfp_set_fpcr(env, ldl_p(buf));
return 4;
default:
return 0;
}
}
static int raw_read(CPUARMState *env, const ARMCPRegInfo *ri,
uint64_t *value)
{
@ -1338,7 +1380,8 @@ static const ARMCPRegInfo dummy_c15_cp_reginfo[] = {
*/
{ .name = "C15_IMPDEF", .cp = 15, .crn = 15,
.crm = CP_ANY, .opc1 = CP_ANY, .opc2 = CP_ANY,
.access = PL1_RW, .type = ARM_CP_CONST | ARM_CP_NO_MIGRATE,
.access = PL1_RW,
.type = ARM_CP_CONST | ARM_CP_NO_MIGRATE | ARM_CP_OVERRIDE,
.resetvalue = 0 },
REGINFO_SENTINEL
};
@ -1744,6 +1787,15 @@ void register_cp_regs_for_features(ARMCPU *cpu)
define_one_arm_cp_reg(cpu, &auxcr);
}
if (arm_feature(env, ARM_FEATURE_CBAR)) {
ARMCPRegInfo cbar = {
.name = "CBAR", .cp = 15, .crn = 15, .crm = 0, .opc1 = 4, .opc2 = 0,
.access = PL1_R|PL3_W, .resetvalue = cpu->reset_cbar,
.fieldoffset = offsetof(CPUARMState, cp15.c15_config_base_address)
};
define_one_arm_cp_reg(cpu, &cbar);
}
/* Generic registers whose values depend on the implementation */
{
ARMCPRegInfo sctlr = {
@ -1785,7 +1837,11 @@ void arm_cpu_register_gdb_regs_for_features(ARMCPU *cpu)
CPUState *cs = CPU(cpu);
CPUARMState *env = &cpu->env;
if (arm_feature(env, ARM_FEATURE_NEON)) {
if (arm_feature(env, ARM_FEATURE_AARCH64)) {
gdb_register_coprocessor(cs, aarch64_fpu_gdb_get_reg,
aarch64_fpu_gdb_set_reg,
34, "aarch64-fpu.xml", 0);
} else if (arm_feature(env, ARM_FEATURE_NEON)) {
gdb_register_coprocessor(cs, vfp_gdb_get_reg, vfp_gdb_set_reg,
51, "arm-neon.xml", 0);
} else if (arm_feature(env, ARM_FEATURE_VFP3)) {

View File

@ -463,4 +463,11 @@ DEF_HELPER_3(neon_qzip8, void, env, i32, i32)
DEF_HELPER_3(neon_qzip16, void, env, i32, i32)
DEF_HELPER_3(neon_qzip32, void, env, i32, i32)
DEF_HELPER_4(crypto_aese, void, env, i32, i32, i32)
DEF_HELPER_4(crypto_aesmc, void, env, i32, i32, i32)
#ifdef TARGET_AARCH64
#include "helper-a64.h"
#endif
#include "exec/def-helper.h"

View File

@ -100,120 +100,6 @@ void kvm_arm_destroy_scratch_host_vcpu(int *fdarray)
}
}
static inline void set_feature(uint64_t *features, int feature)
{
*features |= 1ULL << feature;
}
bool kvm_arm_get_host_cpu_features(ARMHostCPUClass *ahcc)
{
/* Identify the feature bits corresponding to the host CPU, and
* fill out the ARMHostCPUClass fields accordingly. To do this
* we have to create a scratch VM, create a single CPU inside it,
* and then query that CPU for the relevant ID registers.
*/
int i, ret, fdarray[3];
uint32_t midr, id_pfr0, id_isar0, mvfr1;
uint64_t features = 0;
/* Old kernels may not know about the PREFERRED_TARGET ioctl: however
* we know these will only support creating one kind of guest CPU,
* which is its preferred CPU type.
*/
static const uint32_t cpus_to_try[] = {
QEMU_KVM_ARM_TARGET_CORTEX_A15,
QEMU_KVM_ARM_TARGET_NONE
};
struct kvm_vcpu_init init;
struct kvm_one_reg idregs[] = {
{
.id = KVM_REG_ARM | KVM_REG_SIZE_U32
| ENCODE_CP_REG(15, 0, 0, 0, 0, 0),
.addr = (uintptr_t)&midr,
},
{
.id = KVM_REG_ARM | KVM_REG_SIZE_U32
| ENCODE_CP_REG(15, 0, 0, 1, 0, 0),
.addr = (uintptr_t)&id_pfr0,
},
{
.id = KVM_REG_ARM | KVM_REG_SIZE_U32
| ENCODE_CP_REG(15, 0, 0, 2, 0, 0),
.addr = (uintptr_t)&id_isar0,
},
{
.id = KVM_REG_ARM | KVM_REG_SIZE_U32
| KVM_REG_ARM_VFP | KVM_REG_ARM_VFP_MVFR1,
.addr = (uintptr_t)&mvfr1,
},
};
if (!kvm_arm_create_scratch_host_vcpu(cpus_to_try, fdarray, &init)) {
return false;
}
ahcc->target = init.target;
/* This is not strictly blessed by the device tree binding docs yet,
* but in practice the kernel does not care about this string so
* there is no point maintaining an KVM_ARM_TARGET_* -> string table.
*/
ahcc->dtb_compatible = "arm,arm-v7";
for (i = 0; i < ARRAY_SIZE(idregs); i++) {
ret = ioctl(fdarray[2], KVM_GET_ONE_REG, &idregs[i]);
if (ret) {
break;
}
}
kvm_arm_destroy_scratch_host_vcpu(fdarray);
if (ret) {
return false;
}
/* Now we've retrieved all the register information we can
* set the feature bits based on the ID register fields.
* We can assume any KVM supporting CPU is at least a v7
* with VFPv3, LPAE and the generic timers; this in turn implies
* most of the other feature bits, but a few must be tested.
*/
set_feature(&features, ARM_FEATURE_V7);
set_feature(&features, ARM_FEATURE_VFP3);
set_feature(&features, ARM_FEATURE_LPAE);
set_feature(&features, ARM_FEATURE_GENERIC_TIMER);
switch (extract32(id_isar0, 24, 4)) {
case 1:
set_feature(&features, ARM_FEATURE_THUMB_DIV);
break;
case 2:
set_feature(&features, ARM_FEATURE_ARM_DIV);
set_feature(&features, ARM_FEATURE_THUMB_DIV);
break;
default:
break;
}
if (extract32(id_pfr0, 12, 4) == 1) {
set_feature(&features, ARM_FEATURE_THUMB2EE);
}
if (extract32(mvfr1, 20, 4) == 1) {
set_feature(&features, ARM_FEATURE_VFP_FP16);
}
if (extract32(mvfr1, 12, 4) == 1) {
set_feature(&features, ARM_FEATURE_NEON);
}
if (extract32(mvfr1, 28, 4) == 1) {
/* FMAC support implies VFPv4 */
set_feature(&features, ARM_FEATURE_VFP4);
}
ahcc->features = features;
return true;
}
static void kvm_arm_host_cpu_class_init(ObjectClass *oc, void *data)
{
ARMHostCPUClass *ahcc = ARM_HOST_CPU_CLASS(oc);
@ -242,7 +128,11 @@ static void kvm_arm_host_cpu_initfn(Object *obj)
static const TypeInfo host_arm_cpu_type_info = {
.name = TYPE_ARM_HOST_CPU,
#ifdef TARGET_AARCH64
.parent = TYPE_AARCH64_CPU,
#else
.parent = TYPE_ARM_CPU,
#endif
.instance_init = kvm_arm_host_cpu_initfn,
.class_init = kvm_arm_host_cpu_class_init,
.class_size = sizeof(ARMHostCPUClass),
@ -265,144 +155,6 @@ unsigned long kvm_arch_vcpu_id(CPUState *cpu)
return cpu->cpu_index;
}
static bool reg_syncs_via_tuple_list(uint64_t regidx)
{
/* Return true if the regidx is a register we should synchronize
* via the cpreg_tuples array (ie is not a core reg we sync by
* hand in kvm_arch_get/put_registers())
*/
switch (regidx & KVM_REG_ARM_COPROC_MASK) {
case KVM_REG_ARM_CORE:
case KVM_REG_ARM_VFP:
return false;
default:
return true;
}
}
static int compare_u64(const void *a, const void *b)
{
if (*(uint64_t *)a > *(uint64_t *)b) {
return 1;
}
if (*(uint64_t *)a < *(uint64_t *)b) {
return -1;
}
return 0;
}
int kvm_arch_init_vcpu(CPUState *cs)
{
struct kvm_vcpu_init init;
int i, ret, arraylen;
uint64_t v;
struct kvm_one_reg r;
struct kvm_reg_list rl;
struct kvm_reg_list *rlp;
ARMCPU *cpu = ARM_CPU(cs);
if (cpu->kvm_target == QEMU_KVM_ARM_TARGET_NONE) {
fprintf(stderr, "KVM is not supported for this guest CPU type\n");
return -EINVAL;
}
init.target = cpu->kvm_target;
memset(init.features, 0, sizeof(init.features));
if (cpu->start_powered_off) {
init.features[0] = 1 << KVM_ARM_VCPU_POWER_OFF;
}
ret = kvm_vcpu_ioctl(cs, KVM_ARM_VCPU_INIT, &init);
if (ret) {
return ret;
}
/* Query the kernel to make sure it supports 32 VFP
* registers: QEMU's "cortex-a15" CPU is always a
* VFP-D32 core. The simplest way to do this is just
* to attempt to read register d31.
*/
r.id = KVM_REG_ARM | KVM_REG_SIZE_U64 | KVM_REG_ARM_VFP | 31;
r.addr = (uintptr_t)(&v);
ret = kvm_vcpu_ioctl(cs, KVM_GET_ONE_REG, &r);
if (ret == -ENOENT) {
return -EINVAL;
}
/* Populate the cpreg list based on the kernel's idea
* of what registers exist (and throw away the TCG-created list).
*/
rl.n = 0;
ret = kvm_vcpu_ioctl(cs, KVM_GET_REG_LIST, &rl);
if (ret != -E2BIG) {
return ret;
}
rlp = g_malloc(sizeof(struct kvm_reg_list) + rl.n * sizeof(uint64_t));
rlp->n = rl.n;
ret = kvm_vcpu_ioctl(cs, KVM_GET_REG_LIST, rlp);
if (ret) {
goto out;
}
/* Sort the list we get back from the kernel, since cpreg_tuples
* must be in strictly ascending order.
*/
qsort(&rlp->reg, rlp->n, sizeof(rlp->reg[0]), compare_u64);
for (i = 0, arraylen = 0; i < rlp->n; i++) {
if (!reg_syncs_via_tuple_list(rlp->reg[i])) {
continue;
}
switch (rlp->reg[i] & KVM_REG_SIZE_MASK) {
case KVM_REG_SIZE_U32:
case KVM_REG_SIZE_U64:
break;
default:
fprintf(stderr, "Can't handle size of register in kernel list\n");
ret = -EINVAL;
goto out;
}
arraylen++;
}
cpu->cpreg_indexes = g_renew(uint64_t, cpu->cpreg_indexes, arraylen);
cpu->cpreg_values = g_renew(uint64_t, cpu->cpreg_values, arraylen);
cpu->cpreg_vmstate_indexes = g_renew(uint64_t, cpu->cpreg_vmstate_indexes,
arraylen);
cpu->cpreg_vmstate_values = g_renew(uint64_t, cpu->cpreg_vmstate_values,
arraylen);
cpu->cpreg_array_len = arraylen;
cpu->cpreg_vmstate_array_len = arraylen;
for (i = 0, arraylen = 0; i < rlp->n; i++) {
uint64_t regidx = rlp->reg[i];
if (!reg_syncs_via_tuple_list(regidx)) {
continue;
}
cpu->cpreg_indexes[arraylen] = regidx;
arraylen++;
}
assert(cpu->cpreg_array_len == arraylen);
if (!write_kvmstate_to_list(cpu)) {
/* Shouldn't happen unless kernel is inconsistent about
* what registers exist.
*/
fprintf(stderr, "Initial read of kernel register state failed\n");
ret = -EINVAL;
goto out;
}
/* Save a copy of the initial register values so that we can
* feed it back to the kernel on VCPU reset.
*/
cpu->cpreg_reset_values = g_memdup(cpu->cpreg_values,
cpu->cpreg_array_len *
sizeof(cpu->cpreg_values[0]));
out:
g_free(rlp);
return ret;
}
/* We track all the KVM devices which need their memory addresses
* passing to the kernel in a list of these structures.
* When board init is complete we run through the list and
@ -563,232 +315,6 @@ bool write_list_to_kvmstate(ARMCPU *cpu)
return ok;
}
typedef struct Reg {
uint64_t id;
int offset;
} Reg;
#define COREREG(KERNELNAME, QEMUFIELD) \
{ \
KVM_REG_ARM | KVM_REG_SIZE_U32 | \
KVM_REG_ARM_CORE | KVM_REG_ARM_CORE_REG(KERNELNAME), \
offsetof(CPUARMState, QEMUFIELD) \
}
#define VFPSYSREG(R) \
{ \
KVM_REG_ARM | KVM_REG_SIZE_U32 | KVM_REG_ARM_VFP | \
KVM_REG_ARM_VFP_##R, \
offsetof(CPUARMState, vfp.xregs[ARM_VFP_##R]) \
}
static const Reg regs[] = {
/* R0_usr .. R14_usr */
COREREG(usr_regs.uregs[0], regs[0]),
COREREG(usr_regs.uregs[1], regs[1]),
COREREG(usr_regs.uregs[2], regs[2]),
COREREG(usr_regs.uregs[3], regs[3]),
COREREG(usr_regs.uregs[4], regs[4]),
COREREG(usr_regs.uregs[5], regs[5]),
COREREG(usr_regs.uregs[6], regs[6]),
COREREG(usr_regs.uregs[7], regs[7]),
COREREG(usr_regs.uregs[8], usr_regs[0]),
COREREG(usr_regs.uregs[9], usr_regs[1]),
COREREG(usr_regs.uregs[10], usr_regs[2]),
COREREG(usr_regs.uregs[11], usr_regs[3]),
COREREG(usr_regs.uregs[12], usr_regs[4]),
COREREG(usr_regs.uregs[13], banked_r13[0]),
COREREG(usr_regs.uregs[14], banked_r14[0]),
/* R13, R14, SPSR for SVC, ABT, UND, IRQ banks */
COREREG(svc_regs[0], banked_r13[1]),
COREREG(svc_regs[1], banked_r14[1]),
COREREG(svc_regs[2], banked_spsr[1]),
COREREG(abt_regs[0], banked_r13[2]),
COREREG(abt_regs[1], banked_r14[2]),
COREREG(abt_regs[2], banked_spsr[2]),
COREREG(und_regs[0], banked_r13[3]),
COREREG(und_regs[1], banked_r14[3]),
COREREG(und_regs[2], banked_spsr[3]),
COREREG(irq_regs[0], banked_r13[4]),
COREREG(irq_regs[1], banked_r14[4]),
COREREG(irq_regs[2], banked_spsr[4]),
/* R8_fiq .. R14_fiq and SPSR_fiq */
COREREG(fiq_regs[0], fiq_regs[0]),
COREREG(fiq_regs[1], fiq_regs[1]),
COREREG(fiq_regs[2], fiq_regs[2]),
COREREG(fiq_regs[3], fiq_regs[3]),
COREREG(fiq_regs[4], fiq_regs[4]),
COREREG(fiq_regs[5], banked_r13[5]),
COREREG(fiq_regs[6], banked_r14[5]),
COREREG(fiq_regs[7], banked_spsr[5]),
/* R15 */
COREREG(usr_regs.uregs[15], regs[15]),
/* VFP system registers */
VFPSYSREG(FPSID),
VFPSYSREG(MVFR1),
VFPSYSREG(MVFR0),
VFPSYSREG(FPEXC),
VFPSYSREG(FPINST),
VFPSYSREG(FPINST2),
};
int kvm_arch_put_registers(CPUState *cs, int level)
{
ARMCPU *cpu = ARM_CPU(cs);
CPUARMState *env = &cpu->env;
struct kvm_one_reg r;
int mode, bn;
int ret, i;
uint32_t cpsr, fpscr;
/* Make sure the banked regs are properly set */
mode = env->uncached_cpsr & CPSR_M;
bn = bank_number(mode);
if (mode == ARM_CPU_MODE_FIQ) {
memcpy(env->fiq_regs, env->regs + 8, 5 * sizeof(uint32_t));
} else {
memcpy(env->usr_regs, env->regs + 8, 5 * sizeof(uint32_t));
}
env->banked_r13[bn] = env->regs[13];
env->banked_r14[bn] = env->regs[14];
env->banked_spsr[bn] = env->spsr;
/* Now we can safely copy stuff down to the kernel */
for (i = 0; i < ARRAY_SIZE(regs); i++) {
r.id = regs[i].id;
r.addr = (uintptr_t)(env) + regs[i].offset;
ret = kvm_vcpu_ioctl(cs, KVM_SET_ONE_REG, &r);
if (ret) {
return ret;
}
}
/* Special cases which aren't a single CPUARMState field */
cpsr = cpsr_read(env);
r.id = KVM_REG_ARM | KVM_REG_SIZE_U32 |
KVM_REG_ARM_CORE | KVM_REG_ARM_CORE_REG(usr_regs.ARM_cpsr);
r.addr = (uintptr_t)(&cpsr);
ret = kvm_vcpu_ioctl(cs, KVM_SET_ONE_REG, &r);
if (ret) {
return ret;
}
/* VFP registers */
r.id = KVM_REG_ARM | KVM_REG_SIZE_U64 | KVM_REG_ARM_VFP;
for (i = 0; i < 32; i++) {
r.addr = (uintptr_t)(&env->vfp.regs[i]);
ret = kvm_vcpu_ioctl(cs, KVM_SET_ONE_REG, &r);
if (ret) {
return ret;
}
r.id++;
}
r.id = KVM_REG_ARM | KVM_REG_SIZE_U32 | KVM_REG_ARM_VFP |
KVM_REG_ARM_VFP_FPSCR;
fpscr = vfp_get_fpscr(env);
r.addr = (uintptr_t)&fpscr;
ret = kvm_vcpu_ioctl(cs, KVM_SET_ONE_REG, &r);
if (ret) {
return ret;
}
/* Note that we do not call write_cpustate_to_list()
* here, so we are only writing the tuple list back to
* KVM. This is safe because nothing can change the
* CPUARMState cp15 fields (in particular gdb accesses cannot)
* and so there are no changes to sync. In fact syncing would
* be wrong at this point: for a constant register where TCG and
* KVM disagree about its value, the preceding write_list_to_cpustate()
* would not have had any effect on the CPUARMState value (since the
* register is read-only), and a write_cpustate_to_list() here would
* then try to write the TCG value back into KVM -- this would either
* fail or incorrectly change the value the guest sees.
*
* If we ever want to allow the user to modify cp15 registers via
* the gdb stub, we would need to be more clever here (for instance
* tracking the set of registers kvm_arch_get_registers() successfully
* managed to update the CPUARMState with, and only allowing those
* to be written back up into the kernel).
*/
if (!write_list_to_kvmstate(cpu)) {
return EINVAL;
}
return ret;
}
int kvm_arch_get_registers(CPUState *cs)
{
ARMCPU *cpu = ARM_CPU(cs);
CPUARMState *env = &cpu->env;
struct kvm_one_reg r;
int mode, bn;
int ret, i;
uint32_t cpsr, fpscr;
for (i = 0; i < ARRAY_SIZE(regs); i++) {
r.id = regs[i].id;
r.addr = (uintptr_t)(env) + regs[i].offset;
ret = kvm_vcpu_ioctl(cs, KVM_GET_ONE_REG, &r);
if (ret) {
return ret;
}
}
/* Special cases which aren't a single CPUARMState field */
r.id = KVM_REG_ARM | KVM_REG_SIZE_U32 |
KVM_REG_ARM_CORE | KVM_REG_ARM_CORE_REG(usr_regs.ARM_cpsr);
r.addr = (uintptr_t)(&cpsr);
ret = kvm_vcpu_ioctl(cs, KVM_GET_ONE_REG, &r);
if (ret) {
return ret;
}
cpsr_write(env, cpsr, 0xffffffff);
/* Make sure the current mode regs are properly set */
mode = env->uncached_cpsr & CPSR_M;
bn = bank_number(mode);
if (mode == ARM_CPU_MODE_FIQ) {
memcpy(env->regs + 8, env->fiq_regs, 5 * sizeof(uint32_t));
} else {
memcpy(env->regs + 8, env->usr_regs, 5 * sizeof(uint32_t));
}
env->regs[13] = env->banked_r13[bn];
env->regs[14] = env->banked_r14[bn];
env->spsr = env->banked_spsr[bn];
/* VFP registers */
r.id = KVM_REG_ARM | KVM_REG_SIZE_U64 | KVM_REG_ARM_VFP;
for (i = 0; i < 32; i++) {
r.addr = (uintptr_t)(&env->vfp.regs[i]);
ret = kvm_vcpu_ioctl(cs, KVM_GET_ONE_REG, &r);
if (ret) {
return ret;
}
r.id++;
}
r.id = KVM_REG_ARM | KVM_REG_SIZE_U32 | KVM_REG_ARM_VFP |
KVM_REG_ARM_VFP_FPSCR;
r.addr = (uintptr_t)&fpscr;
ret = kvm_vcpu_ioctl(cs, KVM_GET_ONE_REG, &r);
if (ret) {
return ret;
}
vfp_set_fpscr(env, fpscr);
if (!write_kvmstate_to_list(cpu)) {
return EINVAL;
}
/* Note that it's OK to have registers which aren't in CPUState,
* so we can ignore a failure return here.
*/
write_list_to_cpustate(cpu);
return 0;
}
void kvm_arch_pre_run(CPUState *cs, struct kvm_run *run)
{
}
@ -802,19 +328,6 @@ int kvm_arch_handle_exit(CPUState *cs, struct kvm_run *run)
return 0;
}
void kvm_arch_reset_vcpu(CPUState *cs)
{
/* Feed the kernel back its initial register state */
ARMCPU *cpu = ARM_CPU(cs);
memmove(cpu->cpreg_values, cpu->cpreg_reset_values,
cpu->cpreg_array_len * sizeof(cpu->cpreg_values[0]));
if (!write_list_to_kvmstate(cpu)) {
abort();
}
}
bool kvm_arch_stop_on_emulation_error(CPUState *cs)
{
return true;

515
target-arm/kvm32.c Normal file
View File

@ -0,0 +1,515 @@
/*
* ARM implementation of KVM hooks, 32 bit specific code.
*
* Copyright Christoffer Dall 2009-2010
*
* This work is licensed under the terms of the GNU GPL, version 2 or later.
* See the COPYING file in the top-level directory.
*
*/
#include <stdio.h>
#include <sys/types.h>
#include <sys/ioctl.h>
#include <sys/mman.h>
#include <linux/kvm.h>
#include "qemu-common.h"
#include "qemu/timer.h"
#include "sysemu/sysemu.h"
#include "sysemu/kvm.h"
#include "kvm_arm.h"
#include "cpu.h"
#include "hw/arm/arm.h"
static inline void set_feature(uint64_t *features, int feature)
{
*features |= 1ULL << feature;
}
bool kvm_arm_get_host_cpu_features(ARMHostCPUClass *ahcc)
{
/* Identify the feature bits corresponding to the host CPU, and
* fill out the ARMHostCPUClass fields accordingly. To do this
* we have to create a scratch VM, create a single CPU inside it,
* and then query that CPU for the relevant ID registers.
*/
int i, ret, fdarray[3];
uint32_t midr, id_pfr0, id_isar0, mvfr1;
uint64_t features = 0;
/* Old kernels may not know about the PREFERRED_TARGET ioctl: however
* we know these will only support creating one kind of guest CPU,
* which is its preferred CPU type.
*/
static const uint32_t cpus_to_try[] = {
QEMU_KVM_ARM_TARGET_CORTEX_A15,
QEMU_KVM_ARM_TARGET_NONE
};
struct kvm_vcpu_init init;
struct kvm_one_reg idregs[] = {
{
.id = KVM_REG_ARM | KVM_REG_SIZE_U32
| ENCODE_CP_REG(15, 0, 0, 0, 0, 0),
.addr = (uintptr_t)&midr,
},
{
.id = KVM_REG_ARM | KVM_REG_SIZE_U32
| ENCODE_CP_REG(15, 0, 0, 1, 0, 0),
.addr = (uintptr_t)&id_pfr0,
},
{
.id = KVM_REG_ARM | KVM_REG_SIZE_U32
| ENCODE_CP_REG(15, 0, 0, 2, 0, 0),
.addr = (uintptr_t)&id_isar0,
},
{
.id = KVM_REG_ARM | KVM_REG_SIZE_U32
| KVM_REG_ARM_VFP | KVM_REG_ARM_VFP_MVFR1,
.addr = (uintptr_t)&mvfr1,
},
};
if (!kvm_arm_create_scratch_host_vcpu(cpus_to_try, fdarray, &init)) {
return false;
}
ahcc->target = init.target;
/* This is not strictly blessed by the device tree binding docs yet,
* but in practice the kernel does not care about this string so
* there is no point maintaining an KVM_ARM_TARGET_* -> string table.
*/
ahcc->dtb_compatible = "arm,arm-v7";
for (i = 0; i < ARRAY_SIZE(idregs); i++) {
ret = ioctl(fdarray[2], KVM_GET_ONE_REG, &idregs[i]);
if (ret) {
break;
}
}
kvm_arm_destroy_scratch_host_vcpu(fdarray);
if (ret) {
return false;
}
/* Now we've retrieved all the register information we can
* set the feature bits based on the ID register fields.
* We can assume any KVM supporting CPU is at least a v7
* with VFPv3, LPAE and the generic timers; this in turn implies
* most of the other feature bits, but a few must be tested.
*/
set_feature(&features, ARM_FEATURE_V7);
set_feature(&features, ARM_FEATURE_VFP3);
set_feature(&features, ARM_FEATURE_LPAE);
set_feature(&features, ARM_FEATURE_GENERIC_TIMER);
switch (extract32(id_isar0, 24, 4)) {
case 1:
set_feature(&features, ARM_FEATURE_THUMB_DIV);
break;
case 2:
set_feature(&features, ARM_FEATURE_ARM_DIV);
set_feature(&features, ARM_FEATURE_THUMB_DIV);
break;
default:
break;
}
if (extract32(id_pfr0, 12, 4) == 1) {
set_feature(&features, ARM_FEATURE_THUMB2EE);
}
if (extract32(mvfr1, 20, 4) == 1) {
set_feature(&features, ARM_FEATURE_VFP_FP16);
}
if (extract32(mvfr1, 12, 4) == 1) {
set_feature(&features, ARM_FEATURE_NEON);
}
if (extract32(mvfr1, 28, 4) == 1) {
/* FMAC support implies VFPv4 */
set_feature(&features, ARM_FEATURE_VFP4);
}
ahcc->features = features;
return true;
}
static bool reg_syncs_via_tuple_list(uint64_t regidx)
{
/* Return true if the regidx is a register we should synchronize
* via the cpreg_tuples array (ie is not a core reg we sync by
* hand in kvm_arch_get/put_registers())
*/
switch (regidx & KVM_REG_ARM_COPROC_MASK) {
case KVM_REG_ARM_CORE:
case KVM_REG_ARM_VFP:
return false;
default:
return true;
}
}
static int compare_u64(const void *a, const void *b)
{
if (*(uint64_t *)a > *(uint64_t *)b) {
return 1;
}
if (*(uint64_t *)a < *(uint64_t *)b) {
return -1;
}
return 0;
}
int kvm_arch_init_vcpu(CPUState *cs)
{
struct kvm_vcpu_init init;
int i, ret, arraylen;
uint64_t v;
struct kvm_one_reg r;
struct kvm_reg_list rl;
struct kvm_reg_list *rlp;
ARMCPU *cpu = ARM_CPU(cs);
if (cpu->kvm_target == QEMU_KVM_ARM_TARGET_NONE) {
fprintf(stderr, "KVM is not supported for this guest CPU type\n");
return -EINVAL;
}
init.target = cpu->kvm_target;
memset(init.features, 0, sizeof(init.features));
if (cpu->start_powered_off) {
init.features[0] = 1 << KVM_ARM_VCPU_POWER_OFF;
}
ret = kvm_vcpu_ioctl(cs, KVM_ARM_VCPU_INIT, &init);
if (ret) {
return ret;
}
/* Query the kernel to make sure it supports 32 VFP
* registers: QEMU's "cortex-a15" CPU is always a
* VFP-D32 core. The simplest way to do this is just
* to attempt to read register d31.
*/
r.id = KVM_REG_ARM | KVM_REG_SIZE_U64 | KVM_REG_ARM_VFP | 31;
r.addr = (uintptr_t)(&v);
ret = kvm_vcpu_ioctl(cs, KVM_GET_ONE_REG, &r);
if (ret == -ENOENT) {
return -EINVAL;
}
/* Populate the cpreg list based on the kernel's idea
* of what registers exist (and throw away the TCG-created list).
*/
rl.n = 0;
ret = kvm_vcpu_ioctl(cs, KVM_GET_REG_LIST, &rl);
if (ret != -E2BIG) {
return ret;
}
rlp = g_malloc(sizeof(struct kvm_reg_list) + rl.n * sizeof(uint64_t));
rlp->n = rl.n;
ret = kvm_vcpu_ioctl(cs, KVM_GET_REG_LIST, rlp);
if (ret) {
goto out;
}
/* Sort the list we get back from the kernel, since cpreg_tuples
* must be in strictly ascending order.
*/
qsort(&rlp->reg, rlp->n, sizeof(rlp->reg[0]), compare_u64);
for (i = 0, arraylen = 0; i < rlp->n; i++) {
if (!reg_syncs_via_tuple_list(rlp->reg[i])) {
continue;
}
switch (rlp->reg[i] & KVM_REG_SIZE_MASK) {
case KVM_REG_SIZE_U32:
case KVM_REG_SIZE_U64:
break;
default:
fprintf(stderr, "Can't handle size of register in kernel list\n");
ret = -EINVAL;
goto out;
}
arraylen++;
}
cpu->cpreg_indexes = g_renew(uint64_t, cpu->cpreg_indexes, arraylen);
cpu->cpreg_values = g_renew(uint64_t, cpu->cpreg_values, arraylen);
cpu->cpreg_vmstate_indexes = g_renew(uint64_t, cpu->cpreg_vmstate_indexes,
arraylen);
cpu->cpreg_vmstate_values = g_renew(uint64_t, cpu->cpreg_vmstate_values,
arraylen);
cpu->cpreg_array_len = arraylen;
cpu->cpreg_vmstate_array_len = arraylen;
for (i = 0, arraylen = 0; i < rlp->n; i++) {
uint64_t regidx = rlp->reg[i];
if (!reg_syncs_via_tuple_list(regidx)) {
continue;
}
cpu->cpreg_indexes[arraylen] = regidx;
arraylen++;
}
assert(cpu->cpreg_array_len == arraylen);
if (!write_kvmstate_to_list(cpu)) {
/* Shouldn't happen unless kernel is inconsistent about
* what registers exist.
*/
fprintf(stderr, "Initial read of kernel register state failed\n");
ret = -EINVAL;
goto out;
}
/* Save a copy of the initial register values so that we can
* feed it back to the kernel on VCPU reset.
*/
cpu->cpreg_reset_values = g_memdup(cpu->cpreg_values,
cpu->cpreg_array_len *
sizeof(cpu->cpreg_values[0]));
out:
g_free(rlp);
return ret;
}
typedef struct Reg {
uint64_t id;
int offset;
} Reg;
#define COREREG(KERNELNAME, QEMUFIELD) \
{ \
KVM_REG_ARM | KVM_REG_SIZE_U32 | \
KVM_REG_ARM_CORE | KVM_REG_ARM_CORE_REG(KERNELNAME), \
offsetof(CPUARMState, QEMUFIELD) \
}
#define VFPSYSREG(R) \
{ \
KVM_REG_ARM | KVM_REG_SIZE_U32 | KVM_REG_ARM_VFP | \
KVM_REG_ARM_VFP_##R, \
offsetof(CPUARMState, vfp.xregs[ARM_VFP_##R]) \
}
static const Reg regs[] = {
/* R0_usr .. R14_usr */
COREREG(usr_regs.uregs[0], regs[0]),
COREREG(usr_regs.uregs[1], regs[1]),
COREREG(usr_regs.uregs[2], regs[2]),
COREREG(usr_regs.uregs[3], regs[3]),
COREREG(usr_regs.uregs[4], regs[4]),
COREREG(usr_regs.uregs[5], regs[5]),
COREREG(usr_regs.uregs[6], regs[6]),
COREREG(usr_regs.uregs[7], regs[7]),
COREREG(usr_regs.uregs[8], usr_regs[0]),
COREREG(usr_regs.uregs[9], usr_regs[1]),
COREREG(usr_regs.uregs[10], usr_regs[2]),
COREREG(usr_regs.uregs[11], usr_regs[3]),
COREREG(usr_regs.uregs[12], usr_regs[4]),
COREREG(usr_regs.uregs[13], banked_r13[0]),
COREREG(usr_regs.uregs[14], banked_r14[0]),
/* R13, R14, SPSR for SVC, ABT, UND, IRQ banks */
COREREG(svc_regs[0], banked_r13[1]),
COREREG(svc_regs[1], banked_r14[1]),
COREREG(svc_regs[2], banked_spsr[1]),
COREREG(abt_regs[0], banked_r13[2]),
COREREG(abt_regs[1], banked_r14[2]),
COREREG(abt_regs[2], banked_spsr[2]),
COREREG(und_regs[0], banked_r13[3]),
COREREG(und_regs[1], banked_r14[3]),
COREREG(und_regs[2], banked_spsr[3]),
COREREG(irq_regs[0], banked_r13[4]),
COREREG(irq_regs[1], banked_r14[4]),
COREREG(irq_regs[2], banked_spsr[4]),
/* R8_fiq .. R14_fiq and SPSR_fiq */
COREREG(fiq_regs[0], fiq_regs[0]),
COREREG(fiq_regs[1], fiq_regs[1]),
COREREG(fiq_regs[2], fiq_regs[2]),
COREREG(fiq_regs[3], fiq_regs[3]),
COREREG(fiq_regs[4], fiq_regs[4]),
COREREG(fiq_regs[5], banked_r13[5]),
COREREG(fiq_regs[6], banked_r14[5]),
COREREG(fiq_regs[7], banked_spsr[5]),
/* R15 */
COREREG(usr_regs.uregs[15], regs[15]),
/* VFP system registers */
VFPSYSREG(FPSID),
VFPSYSREG(MVFR1),
VFPSYSREG(MVFR0),
VFPSYSREG(FPEXC),
VFPSYSREG(FPINST),
VFPSYSREG(FPINST2),
};
int kvm_arch_put_registers(CPUState *cs, int level)
{
ARMCPU *cpu = ARM_CPU(cs);
CPUARMState *env = &cpu->env;
struct kvm_one_reg r;
int mode, bn;
int ret, i;
uint32_t cpsr, fpscr;
/* Make sure the banked regs are properly set */
mode = env->uncached_cpsr & CPSR_M;
bn = bank_number(mode);
if (mode == ARM_CPU_MODE_FIQ) {
memcpy(env->fiq_regs, env->regs + 8, 5 * sizeof(uint32_t));
} else {
memcpy(env->usr_regs, env->regs + 8, 5 * sizeof(uint32_t));
}
env->banked_r13[bn] = env->regs[13];
env->banked_r14[bn] = env->regs[14];
env->banked_spsr[bn] = env->spsr;
/* Now we can safely copy stuff down to the kernel */
for (i = 0; i < ARRAY_SIZE(regs); i++) {
r.id = regs[i].id;
r.addr = (uintptr_t)(env) + regs[i].offset;
ret = kvm_vcpu_ioctl(cs, KVM_SET_ONE_REG, &r);
if (ret) {
return ret;
}
}
/* Special cases which aren't a single CPUARMState field */
cpsr = cpsr_read(env);
r.id = KVM_REG_ARM | KVM_REG_SIZE_U32 |
KVM_REG_ARM_CORE | KVM_REG_ARM_CORE_REG(usr_regs.ARM_cpsr);
r.addr = (uintptr_t)(&cpsr);
ret = kvm_vcpu_ioctl(cs, KVM_SET_ONE_REG, &r);
if (ret) {
return ret;
}
/* VFP registers */
r.id = KVM_REG_ARM | KVM_REG_SIZE_U64 | KVM_REG_ARM_VFP;
for (i = 0; i < 32; i++) {
r.addr = (uintptr_t)(&env->vfp.regs[i]);
ret = kvm_vcpu_ioctl(cs, KVM_SET_ONE_REG, &r);
if (ret) {
return ret;
}
r.id++;
}
r.id = KVM_REG_ARM | KVM_REG_SIZE_U32 | KVM_REG_ARM_VFP |
KVM_REG_ARM_VFP_FPSCR;
fpscr = vfp_get_fpscr(env);
r.addr = (uintptr_t)&fpscr;
ret = kvm_vcpu_ioctl(cs, KVM_SET_ONE_REG, &r);
if (ret) {
return ret;
}
/* Note that we do not call write_cpustate_to_list()
* here, so we are only writing the tuple list back to
* KVM. This is safe because nothing can change the
* CPUARMState cp15 fields (in particular gdb accesses cannot)
* and so there are no changes to sync. In fact syncing would
* be wrong at this point: for a constant register where TCG and
* KVM disagree about its value, the preceding write_list_to_cpustate()
* would not have had any effect on the CPUARMState value (since the
* register is read-only), and a write_cpustate_to_list() here would
* then try to write the TCG value back into KVM -- this would either
* fail or incorrectly change the value the guest sees.
*
* If we ever want to allow the user to modify cp15 registers via
* the gdb stub, we would need to be more clever here (for instance
* tracking the set of registers kvm_arch_get_registers() successfully
* managed to update the CPUARMState with, and only allowing those
* to be written back up into the kernel).
*/
if (!write_list_to_kvmstate(cpu)) {
return EINVAL;
}
return ret;
}
int kvm_arch_get_registers(CPUState *cs)
{
ARMCPU *cpu = ARM_CPU(cs);
CPUARMState *env = &cpu->env;
struct kvm_one_reg r;
int mode, bn;
int ret, i;
uint32_t cpsr, fpscr;
for (i = 0; i < ARRAY_SIZE(regs); i++) {
r.id = regs[i].id;
r.addr = (uintptr_t)(env) + regs[i].offset;
ret = kvm_vcpu_ioctl(cs, KVM_GET_ONE_REG, &r);
if (ret) {
return ret;
}
}
/* Special cases which aren't a single CPUARMState field */
r.id = KVM_REG_ARM | KVM_REG_SIZE_U32 |
KVM_REG_ARM_CORE | KVM_REG_ARM_CORE_REG(usr_regs.ARM_cpsr);
r.addr = (uintptr_t)(&cpsr);
ret = kvm_vcpu_ioctl(cs, KVM_GET_ONE_REG, &r);
if (ret) {
return ret;
}
cpsr_write(env, cpsr, 0xffffffff);
/* Make sure the current mode regs are properly set */
mode = env->uncached_cpsr & CPSR_M;
bn = bank_number(mode);
if (mode == ARM_CPU_MODE_FIQ) {
memcpy(env->regs + 8, env->fiq_regs, 5 * sizeof(uint32_t));
} else {
memcpy(env->regs + 8, env->usr_regs, 5 * sizeof(uint32_t));
}
env->regs[13] = env->banked_r13[bn];
env->regs[14] = env->banked_r14[bn];
env->spsr = env->banked_spsr[bn];
/* VFP registers */
r.id = KVM_REG_ARM | KVM_REG_SIZE_U64 | KVM_REG_ARM_VFP;
for (i = 0; i < 32; i++) {
r.addr = (uintptr_t)(&env->vfp.regs[i]);
ret = kvm_vcpu_ioctl(cs, KVM_GET_ONE_REG, &r);
if (ret) {
return ret;
}
r.id++;
}
r.id = KVM_REG_ARM | KVM_REG_SIZE_U32 | KVM_REG_ARM_VFP |
KVM_REG_ARM_VFP_FPSCR;
r.addr = (uintptr_t)&fpscr;
ret = kvm_vcpu_ioctl(cs, KVM_GET_ONE_REG, &r);
if (ret) {
return ret;
}
vfp_set_fpscr(env, fpscr);
if (!write_kvmstate_to_list(cpu)) {
return EINVAL;
}
/* Note that it's OK to have registers which aren't in CPUState,
* so we can ignore a failure return here.
*/
write_list_to_cpustate(cpu);
return 0;
}
void kvm_arch_reset_vcpu(CPUState *cs)
{
/* Feed the kernel back its initial register state */
ARMCPU *cpu = ARM_CPU(cs);
memmove(cpu->cpreg_values, cpu->cpreg_reset_values,
cpu->cpreg_array_len * sizeof(cpu->cpreg_values[0]));
if (!write_list_to_kvmstate(cpu)) {
abort();
}
}

204
target-arm/kvm64.c Normal file
View File

@ -0,0 +1,204 @@
/*
* ARM implementation of KVM hooks, 64 bit specific code
*
* Copyright Mian-M. Hamayun 2013, Virtual Open Systems
*
* This work is licensed under the terms of the GNU GPL, version 2 or later.
* See the COPYING file in the top-level directory.
*
*/
#include <stdio.h>
#include <sys/types.h>
#include <sys/ioctl.h>
#include <sys/mman.h>
#include <linux/kvm.h>
#include "qemu-common.h"
#include "qemu/timer.h"
#include "sysemu/sysemu.h"
#include "sysemu/kvm.h"
#include "kvm_arm.h"
#include "cpu.h"
#include "hw/arm/arm.h"
static inline void set_feature(uint64_t *features, int feature)
{
*features |= 1ULL << feature;
}
bool kvm_arm_get_host_cpu_features(ARMHostCPUClass *ahcc)
{
/* Identify the feature bits corresponding to the host CPU, and
* fill out the ARMHostCPUClass fields accordingly. To do this
* we have to create a scratch VM, create a single CPU inside it,
* and then query that CPU for the relevant ID registers.
* For AArch64 we currently don't care about ID registers at
* all; we just want to know the CPU type.
*/
int fdarray[3];
uint64_t features = 0;
/* Old kernels may not know about the PREFERRED_TARGET ioctl: however
* we know these will only support creating one kind of guest CPU,
* which is its preferred CPU type. Fortunately these old kernels
* support only a very limited number of CPUs.
*/
static const uint32_t cpus_to_try[] = {
KVM_ARM_TARGET_AEM_V8,
KVM_ARM_TARGET_FOUNDATION_V8,
KVM_ARM_TARGET_CORTEX_A57,
QEMU_KVM_ARM_TARGET_NONE
};
struct kvm_vcpu_init init;
if (!kvm_arm_create_scratch_host_vcpu(cpus_to_try, fdarray, &init)) {
return false;
}
ahcc->target = init.target;
ahcc->dtb_compatible = "arm,arm-v8";
kvm_arm_destroy_scratch_host_vcpu(fdarray);
/* We can assume any KVM supporting CPU is at least a v8
* with VFPv4+Neon; this in turn implies most of the other
* feature bits.
*/
set_feature(&features, ARM_FEATURE_V8);
set_feature(&features, ARM_FEATURE_VFP4);
set_feature(&features, ARM_FEATURE_NEON);
set_feature(&features, ARM_FEATURE_AARCH64);
ahcc->features = features;
return true;
}
int kvm_arch_init_vcpu(CPUState *cs)
{
ARMCPU *cpu = ARM_CPU(cs);
struct kvm_vcpu_init init;
int ret;
if (cpu->kvm_target == QEMU_KVM_ARM_TARGET_NONE ||
!arm_feature(&cpu->env, ARM_FEATURE_AARCH64)) {
fprintf(stderr, "KVM is not supported for this guest CPU type\n");
return -EINVAL;
}
init.target = cpu->kvm_target;
memset(init.features, 0, sizeof(init.features));
if (cpu->start_powered_off) {
init.features[0] = 1 << KVM_ARM_VCPU_POWER_OFF;
}
ret = kvm_vcpu_ioctl(cs, KVM_ARM_VCPU_INIT, &init);
/* TODO : support for save/restore/reset of system regs via tuple list */
return ret;
}
#define AARCH64_CORE_REG(x) (KVM_REG_ARM64 | KVM_REG_SIZE_U64 | \
KVM_REG_ARM_CORE | KVM_REG_ARM_CORE_REG(x))
int kvm_arch_put_registers(CPUState *cs, int level)
{
struct kvm_one_reg reg;
uint64_t val;
int i;
int ret;
ARMCPU *cpu = ARM_CPU(cs);
CPUARMState *env = &cpu->env;
for (i = 0; i < 31; i++) {
reg.id = AARCH64_CORE_REG(regs.regs[i]);
reg.addr = (uintptr_t) &env->xregs[i];
ret = kvm_vcpu_ioctl(cs, KVM_SET_ONE_REG, &reg);
if (ret) {
return ret;
}
}
reg.id = AARCH64_CORE_REG(regs.sp);
reg.addr = (uintptr_t) &env->xregs[31];
ret = kvm_vcpu_ioctl(cs, KVM_SET_ONE_REG, &reg);
if (ret) {
return ret;
}
/* Note that KVM thinks pstate is 64 bit but we use a uint32_t */
val = pstate_read(env);
reg.id = AARCH64_CORE_REG(regs.pstate);
reg.addr = (uintptr_t) &val;
ret = kvm_vcpu_ioctl(cs, KVM_SET_ONE_REG, &reg);
if (ret) {
return ret;
}
reg.id = AARCH64_CORE_REG(regs.pc);
reg.addr = (uintptr_t) &env->pc;
ret = kvm_vcpu_ioctl(cs, KVM_SET_ONE_REG, &reg);
if (ret) {
return ret;
}
/* TODO:
* SP_EL1
* ELR_EL1
* SPSR[]
* FP state
* system registers
*/
return ret;
}
int kvm_arch_get_registers(CPUState *cs)
{
struct kvm_one_reg reg;
uint64_t val;
int i;
int ret;
ARMCPU *cpu = ARM_CPU(cs);
CPUARMState *env = &cpu->env;
for (i = 0; i < 31; i++) {
reg.id = AARCH64_CORE_REG(regs.regs[i]);
reg.addr = (uintptr_t) &env->xregs[i];
ret = kvm_vcpu_ioctl(cs, KVM_GET_ONE_REG, &reg);
if (ret) {
return ret;
}
}
reg.id = AARCH64_CORE_REG(regs.sp);
reg.addr = (uintptr_t) &env->xregs[31];
ret = kvm_vcpu_ioctl(cs, KVM_GET_ONE_REG, &reg);
if (ret) {
return ret;
}
reg.id = AARCH64_CORE_REG(regs.pstate);
reg.addr = (uintptr_t) &val;
ret = kvm_vcpu_ioctl(cs, KVM_GET_ONE_REG, &reg);
if (ret) {
return ret;
}
pstate_write(env, val);
reg.id = AARCH64_CORE_REG(regs.pc);
reg.addr = (uintptr_t) &env->pc;
ret = kvm_vcpu_ioctl(cs, KVM_GET_ONE_REG, &reg);
if (ret) {
return ret;
}
/* TODO: other registers */
return ret;
}
void kvm_arch_reset_vcpu(CPUState *cs)
{
}

File diff suppressed because it is too large Load Diff

View File

@ -56,11 +56,6 @@ static uint32_t gen_opc_condexec_bits[OPC_BUF_SIZE];
#define IS_USER(s) (s->user)
#endif
/* These instructions trap after executing, so defer them until after the
conditional execution state has been updated. */
#define DISAS_WFI 4
#define DISAS_SWI 5
TCGv_ptr cpu_env;
/* We reuse the same 64-bit temporaries for efficiency. */
static TCGv_i64 cpu_V0, cpu_V1, cpu_M0;
@ -676,7 +671,11 @@ static void gen_thumb2_parallel_addsub(int op1, int op2, TCGv_i32 a, TCGv_i32 b)
}
#undef PAS_OP
static void gen_test_cc(int cc, int label)
/*
* generate a conditional branch based on ARM condition code cc.
* This is common between ARM and Aarch64 targets.
*/
void arm_gen_test_cc(int cc, int label)
{
TCGv_i32 tmp;
int inv;
@ -900,11 +899,7 @@ DO_GEN_ST(32, MO_TEUL)
static inline void gen_set_pc_im(DisasContext *s, target_ulong val)
{
if (s->aarch64) {
gen_a64_set_pc_im(val);
} else {
tcg_gen_movi_i32(cpu_R[15], val);
}
tcg_gen_movi_i32(cpu_R[15], val);
}
/* Force a TB lookup after an instruction that changes the CPU state. */
@ -4592,6 +4587,8 @@ static const uint8_t neon_3r_sizes[] = {
#define NEON_2RM_VREV16 2
#define NEON_2RM_VPADDL 4
#define NEON_2RM_VPADDL_U 5
#define NEON_2RM_AESE 6 /* Includes AESD */
#define NEON_2RM_AESMC 7 /* Includes AESIMC */
#define NEON_2RM_VCLS 8
#define NEON_2RM_VCLZ 9
#define NEON_2RM_VCNT 10
@ -4649,6 +4646,8 @@ static const uint8_t neon_2rm_sizes[] = {
[NEON_2RM_VREV16] = 0x1,
[NEON_2RM_VPADDL] = 0x7,
[NEON_2RM_VPADDL_U] = 0x7,
[NEON_2RM_AESE] = 0x1,
[NEON_2RM_AESMC] = 0x1,
[NEON_2RM_VCLS] = 0x7,
[NEON_2RM_VCLZ] = 0x7,
[NEON_2RM_VCNT] = 0x1,
@ -6184,6 +6183,28 @@ static int disas_neon_data_insn(CPUARMState * env, DisasContext *s, uint32_t ins
tcg_temp_free_i32(tmp2);
tcg_temp_free_i32(tmp3);
break;
case NEON_2RM_AESE: case NEON_2RM_AESMC:
if (!arm_feature(env, ARM_FEATURE_V8_AES)
|| ((rm | rd) & 1)) {
return 1;
}
tmp = tcg_const_i32(rd);
tmp2 = tcg_const_i32(rm);
/* Bit 6 is the lowest opcode bit; it distinguishes between
* encryption (AESE/AESMC) and decryption (AESD/AESIMC)
*/
tmp3 = tcg_const_i32(extract32(insn, 6, 1));
if (op == NEON_2RM_AESE) {
gen_helper_crypto_aese(cpu_env, tmp, tmp2, tmp3);
} else {
gen_helper_crypto_aesmc(cpu_env, tmp, tmp2, tmp3);
}
tcg_temp_free_i32(tmp);
tcg_temp_free_i32(tmp2);
tcg_temp_free_i32(tmp3);
break;
default:
elementwise:
for (pass = 0; pass < (q ? 4 : 2); pass++) {
@ -7114,7 +7135,7 @@ static void disas_arm_insn(CPUARMState * env, DisasContext *s)
/* if not always execute, we generate a conditional jump to
next instruction */
s->condlabel = gen_new_label();
gen_test_cc(cond ^ 1, s->condlabel);
arm_gen_test_cc(cond ^ 1, s->condlabel);
s->condjmp = 1;
}
if ((insn & 0x0f900000) == 0x03000000) {
@ -9131,7 +9152,7 @@ static int disas_thumb2_insn(CPUARMState *env, DisasContext *s, uint16_t insn_hw
op = (insn >> 22) & 0xf;
/* Generate a conditional jump to next instruction. */
s->condlabel = gen_new_label();
gen_test_cc(op ^ 1, s->condlabel);
arm_gen_test_cc(op ^ 1, s->condlabel);
s->condjmp = 1;
/* offset[11:1] = insn[10:0] */
@ -9488,7 +9509,7 @@ static void disas_thumb_insn(CPUARMState *env, DisasContext *s)
cond = s->condexec_cond;
if (cond != 0x0e) { /* Skip conditional when condition is AL. */
s->condlabel = gen_new_label();
gen_test_cc(cond ^ 1, s->condlabel);
arm_gen_test_cc(cond ^ 1, s->condlabel);
s->condjmp = 1;
}
}
@ -10161,7 +10182,7 @@ static void disas_thumb_insn(CPUARMState *env, DisasContext *s)
}
/* generate a conditional jump to next instruction */
s->condlabel = gen_new_label();
gen_test_cc(cond ^ 1, s->condlabel);
arm_gen_test_cc(cond ^ 1, s->condlabel);
s->condjmp = 1;
/* jump to the offset */
@ -10217,6 +10238,15 @@ static inline void gen_intermediate_code_internal(ARMCPU *cpu,
int max_insns;
/* generate intermediate code */
/* The A64 decoder has its own top level loop, because it doesn't need
* the A32/T32 complexity to do with conditional execution/IT blocks/etc.
*/
if (ARM_TBFLAG_AARCH64_STATE(tb->flags)) {
gen_intermediate_code_internal_a64(cpu, tb, search_pc);
return;
}
pc_start = tb->pc;
dc->tb = tb;
@ -10228,31 +10258,18 @@ static inline void gen_intermediate_code_internal(ARMCPU *cpu,
dc->singlestep_enabled = cs->singlestep_enabled;
dc->condjmp = 0;
if (ARM_TBFLAG_AARCH64_STATE(tb->flags)) {
dc->aarch64 = 1;
dc->thumb = 0;
dc->bswap_code = 0;
dc->condexec_mask = 0;
dc->condexec_cond = 0;
dc->aarch64 = 0;
dc->thumb = ARM_TBFLAG_THUMB(tb->flags);
dc->bswap_code = ARM_TBFLAG_BSWAP_CODE(tb->flags);
dc->condexec_mask = (ARM_TBFLAG_CONDEXEC(tb->flags) & 0xf) << 1;
dc->condexec_cond = ARM_TBFLAG_CONDEXEC(tb->flags) >> 4;
#if !defined(CONFIG_USER_ONLY)
dc->user = 0;
dc->user = (ARM_TBFLAG_PRIV(tb->flags) == 0);
#endif
dc->vfp_enabled = 0;
dc->vec_len = 0;
dc->vec_stride = 0;
} else {
dc->aarch64 = 0;
dc->thumb = ARM_TBFLAG_THUMB(tb->flags);
dc->bswap_code = ARM_TBFLAG_BSWAP_CODE(tb->flags);
dc->condexec_mask = (ARM_TBFLAG_CONDEXEC(tb->flags) & 0xf) << 1;
dc->condexec_cond = ARM_TBFLAG_CONDEXEC(tb->flags) >> 4;
#if !defined(CONFIG_USER_ONLY)
dc->user = (ARM_TBFLAG_PRIV(tb->flags) == 0);
#endif
dc->vfp_enabled = ARM_TBFLAG_VFPEN(tb->flags);
dc->vec_len = ARM_TBFLAG_VECLEN(tb->flags);
dc->vec_stride = ARM_TBFLAG_VECSTRIDE(tb->flags);
}
dc->vfp_enabled = ARM_TBFLAG_VFPEN(tb->flags);
dc->vec_len = ARM_TBFLAG_VECLEN(tb->flags);
dc->vec_stride = ARM_TBFLAG_VECSTRIDE(tb->flags);
cpu_F0s = tcg_temp_new_i32();
cpu_F1s = tcg_temp_new_i32();
cpu_F0d = tcg_temp_new_i64();
@ -10314,7 +10331,7 @@ static inline void gen_intermediate_code_internal(ARMCPU *cpu,
do {
#ifdef CONFIG_USER_ONLY
/* Intercept jump to the magic kernel page. */
if (!dc->aarch64 && dc->pc >= 0xffff0000) {
if (dc->pc >= 0xffff0000) {
/* We always get here via a jump, so know we are not in a
conditional execution block. */
gen_exception(EXCP_KERNEL_TRAP);
@ -10362,9 +10379,7 @@ static inline void gen_intermediate_code_internal(ARMCPU *cpu,
tcg_gen_debug_insn_start(dc->pc);
}
if (dc->aarch64) {
disas_a64_insn(env, dc);
} else if (dc->thumb) {
if (dc->thumb) {
disas_thumb_insn(env, dc);
if (dc->condexec_mask) {
dc->condexec_cond = (dc->condexec_cond & 0xe)
@ -10559,8 +10574,9 @@ void restore_state_to_opc(CPUARMState *env, TranslationBlock *tb, int pc_pos)
{
if (is_a64(env)) {
env->pc = tcg_ctx.gen_opc_pc[pc_pos];
env->condexec_bits = 0;
} else {
env->regs[15] = tcg_ctx.gen_opc_pc[pc_pos];
env->condexec_bits = gen_opc_condexec_bits[pc_pos];
}
env->condexec_bits = gen_opc_condexec_bits[pc_pos];
}

View File

@ -24,20 +24,39 @@ typedef struct DisasContext {
int vec_len;
int vec_stride;
int aarch64;
#define TMP_A64_MAX 16
int tmp_a64_count;
TCGv_i64 tmp_a64[TMP_A64_MAX];
} DisasContext;
extern TCGv_ptr cpu_env;
/* target-specific extra values for is_jmp */
/* These instructions trap after executing, so the A32/T32 decoder must
* defer them until after the conditional execution state has been updated.
* WFI also needs special handling when single-stepping.
*/
#define DISAS_WFI 4
#define DISAS_SWI 5
/* For instructions which unconditionally cause an exception we can skip
* emitting unreachable code at the end of the TB in the A64 decoder
*/
#define DISAS_EXC 6
#ifdef TARGET_AARCH64
void a64_translate_init(void);
void disas_a64_insn(CPUARMState *env, DisasContext *s);
void gen_intermediate_code_internal_a64(ARMCPU *cpu,
TranslationBlock *tb,
bool search_pc);
void gen_a64_set_pc_im(uint64_t val);
#else
static inline void a64_translate_init(void)
{
}
static inline void disas_a64_insn(CPUARMState *env, DisasContext *s)
static inline void gen_intermediate_code_internal_a64(ARMCPU *cpu,
TranslationBlock *tb,
bool search_pc)
{
}
@ -46,4 +65,6 @@ static inline void gen_a64_set_pc_im(uint64_t val)
}
#endif
void arm_gen_test_cc(int cc, int label);
#endif /* TARGET_ARM_TRANSLATE_H */

View File

@ -70,6 +70,8 @@ static const char *arm_machines[] = {
"xilinx-zynq-a9",
"highbank",
"midway",
"canon-a1100",
"cubieboard",
};
static const char *cris_machines[] = {