diff options
84 files changed, 2492 insertions, 241 deletions
diff --git a/.travis.yml b/.travis.yml index 6ca0260941..0428aed0ec 100644 --- a/.travis.yml +++ b/.travis.yml @@ -78,7 +78,7 @@ matrix: compiler: gcc # All the trace backends (apart from dtrace) - env: TARGETS=i386-softmmu,x86_64-softmmu - EXTRA_CONFIG="--enable-trace-backends=stderr" + EXTRA_CONFIG="--enable-trace-backends=log" compiler: gcc - env: TARGETS=i386-softmmu,x86_64-softmmu EXTRA_CONFIG="--enable-trace-backends=simple" diff --git a/Makefile.objs b/Makefile.objs index 06b95c72d0..fbcaa7471f 100644 --- a/Makefile.objs +++ b/Makefile.objs @@ -89,7 +89,6 @@ endif ####################################################################### # Target-independent parts used in system and user emulation -common-obj-y += qemu-log.o common-obj-y += tcg-runtime.o common-obj-y += hw/ common-obj-y += qom/ diff --git a/bsd-user/main.c b/bsd-user/main.c index 1ecaeb50e5..fefcfc1ccf 100644 --- a/bsd-user/main.c +++ b/bsd-user/main.c @@ -33,6 +33,7 @@ #include "tcg.h" #include "qemu/timer.h" #include "qemu/envlist.h" +#include "exec/log.h" int singlestep; unsigned long mmap_min_addr; diff --git a/configure b/configure index 297bfc7997..d4411a1314 100755 --- a/configure +++ b/configure @@ -303,7 +303,7 @@ pkgversion="" pie="" zero_malloc="" qom_cast_debug="yes" -trace_backends="nop" +trace_backends="log" trace_file="trace" spice="" rbd="" @@ -5445,8 +5445,8 @@ if have_backend "simple"; then # Set the appropriate trace file. trace_file="\"$trace_file-\" FMT_pid" fi -if have_backend "stderr"; then - echo "CONFIG_TRACE_STDERR=y" >> $config_host_mak +if have_backend "log"; then + echo "CONFIG_TRACE_LOG=y" >> $config_host_mak fi if have_backend "ust"; then echo "CONFIG_TRACE_UST=y" >> $config_host_mak diff --git a/cpu-exec.c b/cpu-exec.c index c4593646ff..fd92452f16 100644 --- a/cpu-exec.c +++ b/cpu-exec.c @@ -27,6 +27,7 @@ #include "exec/address-spaces.h" #include "qemu/rcu.h" #include "exec/tb-hash.h" +#include "exec/log.h" #if defined(TARGET_I386) && !defined(CONFIG_USER_ONLY) #include "hw/i386/apic.h" #endif diff --git a/default-configs/arm-softmmu.mak b/default-configs/arm-softmmu.mak index d9b90a50d6..a9f82a1032 100644 --- a/default-configs/arm-softmmu.mak +++ b/default-configs/arm-softmmu.mak @@ -79,6 +79,7 @@ CONFIG_TUSB6010=y CONFIG_IMX=y CONFIG_MAINSTONE=y CONFIG_NSERIES=y +CONFIG_RASPI=y CONFIG_REALVIEW=y CONFIG_ZAURUS=y CONFIG_ZYNQ=y diff --git a/disas/libvixl/vixl/a64/disasm-a64.cc b/disas/libvixl/vixl/a64/disasm-a64.cc index 20caba4317..7a58a5c087 100644 --- a/disas/libvixl/vixl/a64/disasm-a64.cc +++ b/disas/libvixl/vixl/a64/disasm-a64.cc @@ -2688,8 +2688,12 @@ void Disassembler::AppendRegisterNameToOutput(const Instruction* instr, void Disassembler::AppendPCRelativeOffsetToOutput(const Instruction* instr, int64_t offset) { USE(instr); + uint64_t abs_offset = offset; char sign = (offset < 0) ? '-' : '+'; - AppendToOutput("#%c0x%" PRIx64, sign, std::abs(offset)); + if (offset < 0) { + abs_offset = -abs_offset; + } + AppendToOutput("#%c0x%" PRIx64, sign, abs_offset); } diff --git a/exec.c b/exec.c index 9e076bc323..ab373604d7 100644 --- a/exec.c +++ b/exec.c @@ -52,6 +52,7 @@ #include "exec/memory-internal.h" #include "exec/ram_addr.h" +#include "exec/log.h" #include "qemu/range.h" #ifndef _WIN32 diff --git a/hw/acpi/cpu_hotplug.c b/hw/acpi/cpu_hotplug.c index 2f99ec5455..5a410a5287 100644 --- a/hw/acpi/cpu_hotplug.c +++ b/hw/acpi/cpu_hotplug.c @@ -12,6 +12,7 @@ #include "qemu/osdep.h" #include "hw/hw.h" #include "hw/acpi/cpu_hotplug.h" +#include "qom/cpu.h" static uint64_t cpu_status_read(void *opaque, hwaddr addr, unsigned int size) { diff --git a/hw/arm/Makefile.objs b/hw/arm/Makefile.objs index 2195b60fac..a711e4df61 100644 --- a/hw/arm/Makefile.objs +++ b/hw/arm/Makefile.objs @@ -11,6 +11,7 @@ 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 +obj-$(CONFIG_RASPI) += bcm2835_peripherals.o bcm2836.o raspi.o obj-$(CONFIG_STM32F205_SOC) += stm32f205_soc.o obj-$(CONFIG_XLNX_ZYNQMP) += xlnx-zynqmp.o xlnx-ep108.o obj-$(CONFIG_FSL_IMX25) += fsl-imx25.o imx25_pdk.o diff --git a/hw/arm/bcm2835_peripherals.c b/hw/arm/bcm2835_peripherals.c new file mode 100644 index 0000000000..18b72ecb69 --- /dev/null +++ b/hw/arm/bcm2835_peripherals.c @@ -0,0 +1,204 @@ +/* + * Raspberry Pi emulation (c) 2012 Gregory Estrade + * Upstreaming code cleanup [including bcm2835_*] (c) 2013 Jan Petrous + * + * Rasperry Pi 2 emulation and refactoring Copyright (c) 2015, Microsoft + * Written by Andrew Baumann + * + * This code is licensed under the GNU GPLv2 and later. + */ + +#include "hw/arm/bcm2835_peripherals.h" +#include "hw/misc/bcm2835_mbox_defs.h" +#include "hw/arm/raspi_platform.h" + +/* Peripheral base address on the VC (GPU) system bus */ +#define BCM2835_VC_PERI_BASE 0x7e000000 + +/* Capabilities for SD controller: no DMA, high-speed, default clocks etc. */ +#define BCM2835_SDHC_CAPAREG 0x52034b4 + +static void bcm2835_peripherals_init(Object *obj) +{ + BCM2835PeripheralState *s = BCM2835_PERIPHERALS(obj); + + /* Memory region for peripheral devices, which we export to our parent */ + memory_region_init(&s->peri_mr, obj,"bcm2835-peripherals", 0x1000000); + object_property_add_child(obj, "peripheral-io", OBJECT(&s->peri_mr), NULL); + sysbus_init_mmio(SYS_BUS_DEVICE(s), &s->peri_mr); + + /* Internal memory region for peripheral bus addresses (not exported) */ + memory_region_init(&s->gpu_bus_mr, obj, "bcm2835-gpu", (uint64_t)1 << 32); + object_property_add_child(obj, "gpu-bus", OBJECT(&s->gpu_bus_mr), NULL); + + /* Internal memory region for request/response communication with + * mailbox-addressable peripherals (not exported) + */ + memory_region_init(&s->mbox_mr, obj, "bcm2835-mbox", + MBOX_CHAN_COUNT << MBOX_AS_CHAN_SHIFT); + + /* Interrupt Controller */ + object_initialize(&s->ic, sizeof(s->ic), TYPE_BCM2835_IC); + object_property_add_child(obj, "ic", OBJECT(&s->ic), NULL); + qdev_set_parent_bus(DEVICE(&s->ic), sysbus_get_default()); + + /* UART0 */ + s->uart0 = SYS_BUS_DEVICE(object_new("pl011")); + object_property_add_child(obj, "uart0", OBJECT(s->uart0), NULL); + qdev_set_parent_bus(DEVICE(s->uart0), sysbus_get_default()); + + /* Mailboxes */ + object_initialize(&s->mboxes, sizeof(s->mboxes), TYPE_BCM2835_MBOX); + object_property_add_child(obj, "mbox", OBJECT(&s->mboxes), NULL); + qdev_set_parent_bus(DEVICE(&s->mboxes), sysbus_get_default()); + + object_property_add_const_link(OBJECT(&s->mboxes), "mbox-mr", + OBJECT(&s->mbox_mr), &error_abort); + + /* Property channel */ + object_initialize(&s->property, sizeof(s->property), TYPE_BCM2835_PROPERTY); + object_property_add_child(obj, "property", OBJECT(&s->property), NULL); + qdev_set_parent_bus(DEVICE(&s->property), sysbus_get_default()); + + object_property_add_const_link(OBJECT(&s->property), "dma-mr", + OBJECT(&s->gpu_bus_mr), &error_abort); + + /* Extended Mass Media Controller */ + object_initialize(&s->sdhci, sizeof(s->sdhci), TYPE_SYSBUS_SDHCI); + object_property_add_child(obj, "sdhci", OBJECT(&s->sdhci), NULL); + qdev_set_parent_bus(DEVICE(&s->sdhci), sysbus_get_default()); +} + +static void bcm2835_peripherals_realize(DeviceState *dev, Error **errp) +{ + BCM2835PeripheralState *s = BCM2835_PERIPHERALS(dev); + Object *obj; + MemoryRegion *ram; + Error *err = NULL; + uint32_t ram_size; + int n; + + obj = object_property_get_link(OBJECT(dev), "ram", &err); + if (obj == NULL) { + error_setg(errp, "%s: required ram link not found: %s", + __func__, error_get_pretty(err)); + return; + } + + ram = MEMORY_REGION(obj); + ram_size = memory_region_size(ram); + + /* Map peripherals and RAM into the GPU address space. */ + memory_region_init_alias(&s->peri_mr_alias, OBJECT(s), + "bcm2835-peripherals", &s->peri_mr, 0, + memory_region_size(&s->peri_mr)); + + memory_region_add_subregion_overlap(&s->gpu_bus_mr, BCM2835_VC_PERI_BASE, + &s->peri_mr_alias, 1); + + /* RAM is aliased four times (different cache configurations) on the GPU */ + for (n = 0; n < 4; n++) { + memory_region_init_alias(&s->ram_alias[n], OBJECT(s), + "bcm2835-gpu-ram-alias[*]", ram, 0, ram_size); + memory_region_add_subregion_overlap(&s->gpu_bus_mr, (hwaddr)n << 30, + &s->ram_alias[n], 0); + } + + /* Interrupt Controller */ + object_property_set_bool(OBJECT(&s->ic), true, "realized", &err); + if (err) { + error_propagate(errp, err); + return; + } + + memory_region_add_subregion(&s->peri_mr, ARMCTRL_IC_OFFSET, + sysbus_mmio_get_region(SYS_BUS_DEVICE(&s->ic), 0)); + sysbus_pass_irq(SYS_BUS_DEVICE(s), SYS_BUS_DEVICE(&s->ic)); + + /* UART0 */ + object_property_set_bool(OBJECT(s->uart0), true, "realized", &err); + if (err) { + error_propagate(errp, err); + return; + } + + memory_region_add_subregion(&s->peri_mr, UART0_OFFSET, + sysbus_mmio_get_region(s->uart0, 0)); + sysbus_connect_irq(s->uart0, 0, + qdev_get_gpio_in_named(DEVICE(&s->ic), BCM2835_IC_GPU_IRQ, + INTERRUPT_UART)); + + /* Mailboxes */ + object_property_set_bool(OBJECT(&s->mboxes), true, "realized", &err); + if (err) { + error_propagate(errp, err); + return; + } + + memory_region_add_subregion(&s->peri_mr, ARMCTRL_0_SBM_OFFSET, + sysbus_mmio_get_region(SYS_BUS_DEVICE(&s->mboxes), 0)); + sysbus_connect_irq(SYS_BUS_DEVICE(&s->mboxes), 0, + qdev_get_gpio_in_named(DEVICE(&s->ic), BCM2835_IC_ARM_IRQ, + INTERRUPT_ARM_MAILBOX)); + + /* Property channel */ + object_property_set_int(OBJECT(&s->property), ram_size, "ram-size", &err); + if (err) { + error_propagate(errp, err); + return; + } + + object_property_set_bool(OBJECT(&s->property), true, "realized", &err); + if (err) { + error_propagate(errp, err); + return; + } + + memory_region_add_subregion(&s->mbox_mr, + MBOX_CHAN_PROPERTY << MBOX_AS_CHAN_SHIFT, + sysbus_mmio_get_region(SYS_BUS_DEVICE(&s->property), 0)); + sysbus_connect_irq(SYS_BUS_DEVICE(&s->property), 0, + qdev_get_gpio_in(DEVICE(&s->mboxes), MBOX_CHAN_PROPERTY)); + + /* Extended Mass Media Controller */ + object_property_set_int(OBJECT(&s->sdhci), BCM2835_SDHC_CAPAREG, "capareg", + &err); + if (err) { + error_propagate(errp, err); + return; + } + + object_property_set_bool(OBJECT(&s->sdhci), true, "realized", &err); + if (err) { + error_propagate(errp, err); + return; + } + + memory_region_add_subregion(&s->peri_mr, EMMC_OFFSET, + sysbus_mmio_get_region(SYS_BUS_DEVICE(&s->sdhci), 0)); + sysbus_connect_irq(SYS_BUS_DEVICE(&s->sdhci), 0, + qdev_get_gpio_in_named(DEVICE(&s->ic), BCM2835_IC_GPU_IRQ, + INTERRUPT_ARASANSDIO)); +} + +static void bcm2835_peripherals_class_init(ObjectClass *oc, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(oc); + + dc->realize = bcm2835_peripherals_realize; +} + +static const TypeInfo bcm2835_peripherals_type_info = { + .name = TYPE_BCM2835_PERIPHERALS, + .parent = TYPE_SYS_BUS_DEVICE, + .instance_size = sizeof(BCM2835PeripheralState), + .instance_init = bcm2835_peripherals_init, + .class_init = bcm2835_peripherals_class_init, +}; + +static void bcm2835_peripherals_register_types(void) +{ + type_register_static(&bcm2835_peripherals_type_info); +} + +type_init(bcm2835_peripherals_register_types) diff --git a/hw/arm/bcm2836.c b/hw/arm/bcm2836.c new file mode 100644 index 0000000000..69c7438317 --- /dev/null +++ b/hw/arm/bcm2836.c @@ -0,0 +1,165 @@ +/* + * Raspberry Pi emulation (c) 2012 Gregory Estrade + * Upstreaming code cleanup [including bcm2835_*] (c) 2013 Jan Petrous + * + * Rasperry Pi 2 emulation and refactoring Copyright (c) 2015, Microsoft + * Written by Andrew Baumann + * + * This code is licensed under the GNU GPLv2 and later. + */ + +#include "hw/arm/bcm2836.h" +#include "hw/arm/raspi_platform.h" +#include "hw/sysbus.h" +#include "exec/address-spaces.h" + +/* Peripheral base address seen by the CPU */ +#define BCM2836_PERI_BASE 0x3F000000 + +/* "QA7" (Pi2) interrupt controller and mailboxes etc. */ +#define BCM2836_CONTROL_BASE 0x40000000 + +static void bcm2836_init(Object *obj) +{ + BCM2836State *s = BCM2836(obj); + int n; + + for (n = 0; n < BCM2836_NCPUS; n++) { + object_initialize(&s->cpus[n], sizeof(s->cpus[n]), + "cortex-a15-" TYPE_ARM_CPU); + object_property_add_child(obj, "cpu[*]", OBJECT(&s->cpus[n]), + &error_abort); + } + + object_initialize(&s->control, sizeof(s->control), TYPE_BCM2836_CONTROL); + object_property_add_child(obj, "control", OBJECT(&s->control), NULL); + qdev_set_parent_bus(DEVICE(&s->control), sysbus_get_default()); + + object_initialize(&s->peripherals, sizeof(s->peripherals), + TYPE_BCM2835_PERIPHERALS); + object_property_add_child(obj, "peripherals", OBJECT(&s->peripherals), + &error_abort); + qdev_set_parent_bus(DEVICE(&s->peripherals), sysbus_get_default()); +} + +static void bcm2836_realize(DeviceState *dev, Error **errp) +{ + BCM2836State *s = BCM2836(dev); + Object *obj; + Error *err = NULL; + int n; + + /* common peripherals from bcm2835 */ + + obj = object_property_get_link(OBJECT(dev), "ram", &err); + if (obj == NULL) { + error_setg(errp, "%s: required ram link not found: %s", + __func__, error_get_pretty(err)); + return; + } + + object_property_add_const_link(OBJECT(&s->peripherals), "ram", obj, &err); + if (err) { + error_propagate(errp, err); + return; + } + + object_property_set_bool(OBJECT(&s->peripherals), true, "realized", &err); + if (err) { + error_propagate(errp, err); + return; + } + + sysbus_mmio_map_overlap(SYS_BUS_DEVICE(&s->peripherals), 0, + BCM2836_PERI_BASE, 1); + + /* bcm2836 interrupt controller (and mailboxes, etc.) */ + object_property_set_bool(OBJECT(&s->control), true, "realized", &err); + if (err) { + error_propagate(errp, err); + return; + } + + sysbus_mmio_map(SYS_BUS_DEVICE(&s->control), 0, BCM2836_CONTROL_BASE); + + sysbus_connect_irq(SYS_BUS_DEVICE(&s->peripherals), 0, + qdev_get_gpio_in_named(DEVICE(&s->control), "gpu-irq", 0)); + sysbus_connect_irq(SYS_BUS_DEVICE(&s->peripherals), 1, + qdev_get_gpio_in_named(DEVICE(&s->control), "gpu-fiq", 0)); + + for (n = 0; n < BCM2836_NCPUS; n++) { + /* Mirror bcm2836, which has clusterid set to 0xf + * TODO: this should be converted to a property of ARM_CPU + */ + s->cpus[n].mp_affinity = 0xF00 | n; + + /* set periphbase/CBAR value for CPU-local registers */ + object_property_set_int(OBJECT(&s->cpus[n]), + BCM2836_PERI_BASE + MCORE_OFFSET, + "reset-cbar", &err); + if (err) { + error_propagate(errp, err); + return; + } + + /* start powered off if not enabled */ + object_property_set_bool(OBJECT(&s->cpus[n]), n >= s->enabled_cpus, + "start-powered-off", &err); + if (err) { + error_propagate(errp, err); + return; + } + + object_property_set_bool(OBJECT(&s->cpus[n]), true, "realized", &err); + if (err) { + error_propagate(errp, err); + return; + } + + /* Connect irq/fiq outputs from the interrupt controller. */ + qdev_connect_gpio_out_named(DEVICE(&s->control), "irq", n, + qdev_get_gpio_in(DEVICE(&s->cpus[n]), ARM_CPU_IRQ)); + qdev_connect_gpio_out_named(DEVICE(&s->control), "fiq", n, + qdev_get_gpio_in(DEVICE(&s->cpus[n]), ARM_CPU_FIQ)); + + /* Connect timers from the CPU to the interrupt controller */ + qdev_connect_gpio_out(DEVICE(&s->cpus[n]), GTIMER_PHYS, + qdev_get_gpio_in_named(DEVICE(&s->control), "cntpsirq", n)); + qdev_connect_gpio_out(DEVICE(&s->cpus[n]), GTIMER_VIRT, + qdev_get_gpio_in_named(DEVICE(&s->control), "cntvirq", n)); + } +} + +static Property bcm2836_props[] = { + DEFINE_PROP_UINT32("enabled-cpus", BCM2836State, enabled_cpus, BCM2836_NCPUS), + DEFINE_PROP_END_OF_LIST() +}; + +static void bcm2836_class_init(ObjectClass *oc, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(oc); + + dc->props = bcm2836_props; + dc->realize = bcm2836_realize; + + /* + * Reason: creates an ARM CPU, thus use after free(), see + * arm_cpu_class_init() + */ + dc->cannot_destroy_with_object_finalize_yet = true; +} + +static const TypeInfo bcm2836_type_info = { + .name = TYPE_BCM2836, + .parent = TYPE_SYS_BUS_DEVICE, + .instance_size = sizeof(BCM2836State), + .instance_init = bcm2836_init, + .class_init = bcm2836_class_init, +}; + +static void bcm2836_register_types(void) +{ + type_register_static(&bcm2836_type_info); +} + +type_init(bcm2836_register_types) diff --git a/hw/arm/boot.c b/hw/arm/boot.c index 7742dd3cb6..cce8c7cd1c 100644 --- a/hw/arm/boot.c +++ b/hw/arm/boot.c @@ -178,6 +178,57 @@ static void default_write_secondary(ARMCPU *cpu, smpboot, fixupcontext); } +void arm_write_secure_board_setup_dummy_smc(ARMCPU *cpu, + const struct arm_boot_info *info, + hwaddr mvbar_addr) +{ + int n; + uint32_t mvbar_blob[] = { + /* mvbar_addr: secure monitor vectors + * Default unimplemented and unused vectors to spin. Makes it + * easier to debug (as opposed to the CPU running away). + */ + 0xeafffffe, /* (spin) */ + 0xeafffffe, /* (spin) */ + 0xe1b0f00e, /* movs pc, lr ;SMC exception return */ + 0xeafffffe, /* (spin) */ + 0xeafffffe, /* (spin) */ + 0xeafffffe, /* (spin) */ + 0xeafffffe, /* (spin) */ + 0xeafffffe, /* (spin) */ + }; + uint32_t board_setup_blob[] = { + /* board setup addr */ + 0xe3a00e00 + (mvbar_addr >> 4), /* mov r0, #mvbar_addr */ + 0xee0c0f30, /* mcr p15, 0, r0, c12, c0, 1 ;set MVBAR */ + 0xee110f11, /* mrc p15, 0, r0, c1 , c1, 0 ;read SCR */ + 0xe3800031, /* orr r0, #0x31 ;enable AW, FW, NS */ + 0xee010f11, /* mcr p15, 0, r0, c1, c1, 0 ;write SCR */ + 0xe1a0100e, /* mov r1, lr ;save LR across SMC */ + 0xe1600070, /* smc #0 ;call monitor to flush SCR */ + 0xe1a0f001, /* mov pc, r1 ;return */ + }; + + /* check that mvbar_addr is correctly aligned and relocatable (using MOV) */ + assert((mvbar_addr & 0x1f) == 0 && (mvbar_addr >> 4) < 0x100); + + /* check that these blobs don't overlap */ + assert((mvbar_addr + sizeof(mvbar_blob) <= info->board_setup_addr) + || (info->board_setup_addr + sizeof(board_setup_blob) <= mvbar_addr)); + + for (n = 0; n < ARRAY_SIZE(mvbar_blob); n++) { + mvbar_blob[n] = tswap32(mvbar_blob[n]); + } + rom_add_blob_fixed("board-setup-mvbar", mvbar_blob, sizeof(mvbar_blob), + mvbar_addr); + + for (n = 0; n < ARRAY_SIZE(board_setup_blob); n++) { + board_setup_blob[n] = tswap32(board_setup_blob[n]); + } + rom_add_blob_fixed("board-setup", board_setup_blob, + sizeof(board_setup_blob), info->board_setup_addr); +} + static void default_reset_secondary(ARMCPU *cpu, const struct arm_boot_info *info) { @@ -488,7 +539,9 @@ static void do_cpu_reset(void *opaque) * adjust. */ if (env->aarch64) { + env->cp15.scr_el3 |= SCR_RW; if (arm_feature(env, ARM_FEATURE_EL2)) { + env->cp15.hcr_el2 |= HCR_RW; env->pstate = PSTATE_MODE_EL2h; } else { env->pstate = PSTATE_MODE_EL1h; diff --git a/hw/arm/highbank.c b/hw/arm/highbank.c index 620b52631a..e25cf5e3f3 100644 --- a/hw/arm/highbank.c +++ b/hw/arm/highbank.c @@ -35,49 +35,16 @@ #define MPCORE_PERIPHBASE 0xfff10000 #define MVBAR_ADDR 0x200 +#define BOARD_SETUP_ADDR (MVBAR_ADDR + 8 * sizeof(uint32_t)) #define NIRQ_GIC 160 /* Board init. */ -/* MVBAR_ADDR is limited by precision of movw */ - -QEMU_BUILD_BUG_ON(MVBAR_ADDR >= (1 << 16)); - -#define ARMV7_IMM16(x) (extract32((x), 0, 12) | \ - extract32((x), 12, 4) << 16) - static void hb_write_board_setup(ARMCPU *cpu, const struct arm_boot_info *info) { - int n; - uint32_t board_setup_blob[] = { - /* MVBAR_ADDR */ - /* Default unimplemented and unused vectors to spin. Makes it - * easier to debug (as opposed to the CPU running away). - */ - 0xeafffffe, /* notused1: b notused */ - 0xeafffffe, /* notused2: b notused */ - 0xe1b0f00e, /* smc: movs pc, lr - exception return */ - 0xeafffffe, /* prefetch_abort: b prefetch_abort */ - 0xeafffffe, /* data_abort: b data_abort */ - 0xeafffffe, /* notused3: b notused3 */ - 0xeafffffe, /* irq: b irq */ - 0xeafffffe, /* fiq: b fiq */ -#define BOARD_SETUP_ADDR (MVBAR_ADDR + 8 * sizeof(uint32_t)) - 0xe3000000 + ARMV7_IMM16(MVBAR_ADDR), /* movw r0, MVBAR_ADDR */ - 0xee0c0f30, /* mcr p15, 0, r0, c12, c0, 1 - set MVBAR */ - 0xee110f11, /* mrc p15, 0, r0, c1 , c1, 0 - get SCR */ - 0xe3810001, /* orr r0, #1 - set NS */ - 0xee010f11, /* mcr p15, 0, r0, c1 , c1, 0 - set SCR */ - 0xe1600070, /* smc - go to monitor mode to flush NS change */ - 0xe12fff1e, /* bx lr - return to caller */ - }; - for (n = 0; n < ARRAY_SIZE(board_setup_blob); n++) { - board_setup_blob[n] = tswap32(board_setup_blob[n]); - } - rom_add_blob_fixed("board-setup", board_setup_blob, - sizeof(board_setup_blob), MVBAR_ADDR); + arm_write_secure_board_setup_dummy_smc(cpu, info, MVBAR_ADDR); } static void hb_write_secondary(ARMCPU *cpu, const struct arm_boot_info *info) diff --git a/hw/arm/raspi.c b/hw/arm/raspi.c new file mode 100644 index 0000000000..0c9427c40e --- /dev/null +++ b/hw/arm/raspi.c @@ -0,0 +1,152 @@ +/* + * Raspberry Pi emulation (c) 2012 Gregory Estrade + * Upstreaming code cleanup [including bcm2835_*] (c) 2013 Jan Petrous + * + * Rasperry Pi 2 emulation Copyright (c) 2015, Microsoft + * Written by Andrew Baumann + * + * This code is licensed under the GNU GPLv2 and later. + */ + +#include "hw/arm/bcm2836.h" +#include "qemu/error-report.h" +#include "hw/boards.h" +#include "hw/loader.h" +#include "hw/arm/arm.h" +#include "sysemu/sysemu.h" + +#define SMPBOOT_ADDR 0x300 /* this should leave enough space for ATAGS */ +#define MVBAR_ADDR 0x400 /* secure vectors */ +#define BOARDSETUP_ADDR (MVBAR_ADDR + 0x20) /* board setup code */ +#define FIRMWARE_ADDR 0x8000 /* Pi loads kernel.img here by default */ + +/* Table of Linux board IDs for different Pi versions */ +static const int raspi_boardid[] = {[1] = 0xc42, [2] = 0xc43}; + +typedef struct RasPiState { + BCM2836State soc; + MemoryRegion ram; +} RasPiState; + +static void write_smpboot(ARMCPU *cpu, const struct arm_boot_info *info) +{ + static const uint32_t smpboot[] = { + 0xe1a0e00f, /* mov lr, pc */ + 0xe3a0fe00 + (BOARDSETUP_ADDR >> 4), /* mov pc, BOARDSETUP_ADDR */ + 0xee100fb0, /* mrc p15, 0, r0, c0, c0, 5;get core ID */ + 0xe7e10050, /* ubfx r0, r0, #0, #2 ;extract LSB */ + 0xe59f5014, /* ldr r5, =0x400000CC ;load mbox base */ + 0xe320f001, /* 1: yield */ + 0xe7953200, /* ldr r3, [r5, r0, lsl #4] ;read mbox for our core*/ + 0xe3530000, /* cmp r3, #0 ;spin while zero */ + 0x0afffffb, /* beq 1b */ + 0xe7853200, /* str r3, [r5, r0, lsl #4] ;clear mbox */ + 0xe12fff13, /* bx r3 ;jump to target */ + 0x400000cc, /* (constant: mailbox 3 read/clear base) */ + }; + + /* check that we don't overrun board setup vectors */ + QEMU_BUILD_BUG_ON(SMPBOOT_ADDR + sizeof(smpboot) > MVBAR_ADDR); + /* check that board setup address is correctly relocated */ + QEMU_BUILD_BUG_ON((BOARDSETUP_ADDR & 0xf) != 0 + || (BOARDSETUP_ADDR >> 4) >= 0x100); + + rom_add_blob_fixed("raspi_smpboot", smpboot, sizeof(smpboot), + info->smp_loader_start); +} + +static void write_board_setup(ARMCPU *cpu, const struct arm_boot_info *info) +{ + arm_write_secure_board_setup_dummy_smc(cpu, info, MVBAR_ADDR); +} + +static void reset_secondary(ARMCPU *cpu, const struct arm_boot_info *info) +{ + CPUState *cs = CPU(cpu); + cpu_set_pc(cs, info->smp_loader_start); +} + +static void setup_boot(MachineState *machine, int version, size_t ram_size) +{ + static struct arm_boot_info binfo; + int r; + + binfo.board_id = raspi_boardid[version]; + binfo.ram_size = ram_size; + binfo.nb_cpus = smp_cpus; + binfo.board_setup_addr = BOARDSETUP_ADDR; + binfo.write_board_setup = write_board_setup; + binfo.secure_board_setup = true; + binfo.secure_boot = true; + + /* Pi2 requires SMP setup */ + if (version == 2) { + binfo.smp_loader_start = SMPBOOT_ADDR; + binfo.write_secondary_boot = write_smpboot; + binfo.secondary_cpu_reset_hook = reset_secondary; + } + + /* If the user specified a "firmware" image (e.g. UEFI), we bypass + * the normal Linux boot process + */ + if (machine->firmware) { + /* load the firmware image (typically kernel.img) */ + r = load_image_targphys(machine->firmware, FIRMWARE_ADDR, + ram_size - FIRMWARE_ADDR); + if (r < 0) { + error_report("Failed to load firmware from %s", machine->firmware); + exit(1); + } + + binfo.entry = FIRMWARE_ADDR; + binfo.firmware_loaded = true; + } else { + binfo.kernel_filename = machine->kernel_filename; + binfo.kernel_cmdline = machine->kernel_cmdline; + binfo.initrd_filename = machine->initrd_filename; + } + + arm_load_kernel(ARM_CPU(first_cpu), &binfo); +} + +static void raspi2_init(MachineState *machine) +{ + RasPiState *s = g_new0(RasPiState, 1); + + object_initialize(&s->soc, sizeof(s->soc), TYPE_BCM2836); + object_property_add_child(OBJECT(machine), "soc", OBJECT(&s->soc), + &error_abort); + + /* Allocate and map RAM */ + memory_region_allocate_system_memory(&s->ram, OBJECT(machine), "ram", + machine->ram_size); + /* FIXME: Remove when we have custom CPU address space support */ + memory_region_add_subregion_overlap(get_system_memory(), 0, &s->ram, 0); + + /* Setup the SOC */ + object_property_add_const_link(OBJECT(&s->soc), "ram", OBJECT(&s->ram), + &error_abort); + object_property_set_int(OBJECT(&s->soc), smp_cpus, "enabled-cpus", + &error_abort); + object_property_set_bool(OBJECT(&s->soc), true, "realized", &error_abort); + + setup_boot(machine, 2, machine->ram_size); +} + +static void raspi2_machine_init(MachineClass *mc) +{ + mc->desc = "Raspberry Pi 2"; + mc->init = raspi2_init; + mc->block_default_type = IF_SD; + mc->no_parallel = 1; + mc->no_floppy = 1; + mc->no_cdrom = 1; + mc->max_cpus = BCM2836_NCPUS; + + /* XXX: Temporary restriction in RAM size from the full 1GB. Since + * we do not yet support the framebuffer / GPU, we need to limit + * RAM usable by the OS to sit below the peripherals. + */ + mc->default_ram_size = 0x3F000000; /* BCM2836_PERI_BASE */ +}; +DEFINE_MACHINE("raspi2", raspi2_machine_init) diff --git a/hw/arm/virt-acpi-build.c b/hw/arm/virt-acpi-build.c index 87fbe7c97d..26146919cd 100644 --- a/hw/arm/virt-acpi-build.c +++ b/hw/arm/virt-acpi-build.c @@ -46,20 +46,6 @@ #define ARM_SPI_BASE 32 #define ACPI_POWER_BUTTON_DEVICE "PWRB" -typedef struct VirtAcpiCpuInfo { - DECLARE_BITMAP(found_cpus, VIRT_ACPI_CPU_ID_LIMIT); -} VirtAcpiCpuInfo; - -static void virt_acpi_get_cpu_info(VirtAcpiCpuInfo *cpuinfo) -{ - CPUState *cpu; - - memset(cpuinfo->found_cpus, 0, sizeof cpuinfo->found_cpus); - CPU_FOREACH(cpu) { - set_bit(cpu->cpu_index, cpuinfo->found_cpus); - } -} - static void acpi_dsdt_add_cpus(Aml *scope, int smp_cpus) { uint16_t i; @@ -443,7 +429,7 @@ build_gtdt(GArray *table_data, GArray *linker) gtdt->secure_el1_flags = ACPI_EDGE_SENSITIVE; gtdt->non_secure_el1_interrupt = ARCH_TIMER_NS_EL1_IRQ + 16; - gtdt->non_secure_el1_flags = ACPI_EDGE_SENSITIVE; + gtdt->non_secure_el1_flags = ACPI_EDGE_SENSITIVE | ACPI_GTDT_ALWAYS_ON; gtdt->virtual_timer_interrupt = ARCH_TIMER_VIRT_IRQ + 16; gtdt->virtual_timer_flags = ACPI_EDGE_SENSITIVE; @@ -458,8 +444,7 @@ build_gtdt(GArray *table_data, GArray *linker) /* MADT */ static void -build_madt(GArray *table_data, GArray *linker, VirtGuestInfo *guest_info, - VirtAcpiCpuInfo *cpuinfo) +build_madt(GArray *table_data, GArray *linker, VirtGuestInfo *guest_info) { int madt_start = table_data->len; const MemMapEntry *memmap = guest_info->memmap; @@ -489,9 +474,7 @@ build_madt(GArray *table_data, GArray *linker, VirtGuestInfo *guest_info, gicc->cpu_interface_number = i; gicc->arm_mpidr = armcpu->mp_affinity; gicc->uid = i; - if (test_bit(i, cpuinfo->found_cpus)) { - gicc->flags = cpu_to_le32(ACPI_GICC_ENABLED); - } + gicc->flags = cpu_to_le32(ACPI_GICC_ENABLED); } if (guest_info->gic_version == 3) { @@ -599,11 +582,8 @@ void virt_acpi_build(VirtGuestInfo *guest_info, AcpiBuildTables *tables) { GArray *table_offsets; unsigned dsdt, rsdt; - VirtAcpiCpuInfo cpuinfo; GArray *tables_blob = tables->table_data; - virt_acpi_get_cpu_info(&cpuinfo); - table_offsets = g_array_new(false, true /* clear */, sizeof(uint32_t)); @@ -630,7 +610,7 @@ void virt_acpi_build(VirtGuestInfo *guest_info, AcpiBuildTables *tables) build_fadt(tables_blob, tables->linker, dsdt); acpi_add_table(table_offsets, tables_blob); - build_madt(tables_blob, tables->linker, guest_info, &cpuinfo); + build_madt(tables_blob, tables->linker, guest_info); acpi_add_table(table_offsets, tables_blob); build_gtdt(tables_blob, tables->linker); diff --git a/hw/intc/Makefile.objs b/hw/intc/Makefile.objs index 004b0c25e4..6a13a39519 100644 --- a/hw/intc/Makefile.objs +++ b/hw/intc/Makefile.objs @@ -24,6 +24,7 @@ obj-$(CONFIG_GRLIB) += grlib_irqmp.o obj-$(CONFIG_IOAPIC) += ioapic.o obj-$(CONFIG_OMAP) += omap_intc.o obj-$(CONFIG_OPENPIC_KVM) += openpic_kvm.o +obj-$(CONFIG_RASPI) += bcm2835_ic.o bcm2836_control.o obj-$(CONFIG_SH4) += sh_intc.o obj-$(CONFIG_XICS) += xics.o obj-$(CONFIG_XICS_KVM) += xics_kvm.o diff --git a/hw/intc/bcm2835_ic.c b/hw/intc/bcm2835_ic.c new file mode 100644 index 0000000000..005a72b1e2 --- /dev/null +++ b/hw/intc/bcm2835_ic.c @@ -0,0 +1,236 @@ +/* + * Raspberry Pi emulation (c) 2012 Gregory Estrade + * Refactoring for Pi2 Copyright (c) 2015, Microsoft. Written by Andrew Baumann. + * This code is licensed under the GNU GPLv2 and later. + * Heavily based on pl190.c, copyright terms below: + * + * Arm PrimeCell PL190 Vector Interrupt Controller + * + * Copyright (c) 2006 CodeSourcery. + * Written by Paul Brook + * + * This code is licensed under the GPL. + */ + +#include "hw/intc/bcm2835_ic.h" + +#define GPU_IRQS 64 +#define ARM_IRQS 8 + +#define IRQ_PENDING_BASIC 0x00 /* IRQ basic pending */ +#define IRQ_PENDING_1 0x04 /* IRQ pending 1 */ +#define IRQ_PENDING_2 0x08 /* IRQ pending 2 */ +#define FIQ_CONTROL 0x0C /* FIQ register */ +#define IRQ_ENABLE_1 0x10 /* Interrupt enable register 1 */ +#define IRQ_ENABLE_2 0x14 /* Interrupt enable register 2 */ +#define IRQ_ENABLE_BASIC 0x18 /* Base interrupt enable register */ +#define IRQ_DISABLE_1 0x1C /* Interrupt disable register 1 */ +#define IRQ_DISABLE_2 0x20 /* Interrupt disable register 2 */ +#define IRQ_DISABLE_BASIC 0x24 /* Base interrupt disable register */ + +/* Update interrupts. */ +static void bcm2835_ic_update(BCM2835ICState *s) +{ + bool set = false; + + if (s->fiq_enable) { + if (s->fiq_select >= GPU_IRQS) { + /* ARM IRQ */ + set = extract32(s->arm_irq_level, s->fiq_select - GPU_IRQS, 1); + } else { + set = extract64(s->gpu_irq_level, s->fiq_select, 1); + } + } + qemu_set_irq(s->fiq, set); + + set = (s->gpu_irq_level & s->gpu_irq_enable) + || (s->arm_irq_level & s->arm_irq_enable); + qemu_set_irq(s->irq, set); + +} + +static void bcm2835_ic_set_gpu_irq(void *opaque, int irq, int level) +{ + BCM2835ICState *s = opaque; + + assert(irq >= 0 && irq < 64); + s->gpu_irq_level = deposit64(s->gpu_irq_level, irq, 1, level != 0); + bcm2835_ic_update(s); +} + +static void bcm2835_ic_set_arm_irq(void *opaque, int irq, int level) +{ + BCM2835ICState *s = opaque; + + assert(irq >= 0 && irq < 8); + s->arm_irq_level = deposit32(s->arm_irq_level, irq, 1, level != 0); + bcm2835_ic_update(s); +} + +static const int irq_dups[] = { 7, 9, 10, 18, 19, 53, 54, 55, 56, 57, 62 }; + +static uint64_t bcm2835_ic_read(void *opaque, hwaddr offset, unsigned size) +{ + BCM2835ICState *s = opaque; + uint32_t res = 0; + uint64_t gpu_pending = s->gpu_irq_level & s->gpu_irq_enable; + int i; + + switch (offset) { + case IRQ_PENDING_BASIC: + /* bits 0-7: ARM irqs */ + res = s->arm_irq_level & s->arm_irq_enable; + + /* bits 8 & 9: pending registers 1 & 2 */ + res |= (((uint32_t)gpu_pending) != 0) << 8; + res |= ((gpu_pending >> 32) != 0) << 9; + + /* bits 10-20: selected GPU IRQs */ + for (i = 0; i < ARRAY_SIZE(irq_dups); i++) { + res |= extract64(gpu_pending, irq_dups[i], 1) << (i + 10); + } + break; + case IRQ_PENDING_1: + res = gpu_pending; + break; + case IRQ_PENDING_2: + res = gpu_pending >> 32; + break; + case FIQ_CONTROL: + res = (s->fiq_enable << 7) | s->fiq_select; + break; + case IRQ_ENABLE_1: + res = s->gpu_irq_enable; + break; + case IRQ_ENABLE_2: + res = s->gpu_irq_enable >> 32; + break; + case IRQ_ENABLE_BASIC: + res = s->arm_irq_enable; + break; + case IRQ_DISABLE_1: + res = ~s->gpu_irq_enable; + break; + case IRQ_DISABLE_2: + res = ~s->gpu_irq_enable >> 32; + break; + case IRQ_DISABLE_BASIC: + res = ~s->arm_irq_enable; + break; + default: + qemu_log_mask(LOG_GUEST_ERROR, "%s: Bad offset %"HWADDR_PRIx"\n", + __func__, offset); + return 0; + } + + return res; +} + +static void bcm2835_ic_write(void *opaque, hwaddr offset, uint64_t val, + unsigned size) +{ + BCM2835ICState *s = opaque; + + switch (offset) { + case FIQ_CONTROL: + s->fiq_select = extract32(val, 0, 7); + s->fiq_enable = extract32(val, 7, 1); + break; + case IRQ_ENABLE_1: + s->gpu_irq_enable |= val; + break; + case IRQ_ENABLE_2: + s->gpu_irq_enable |= val << 32; + break; + case IRQ_ENABLE_BASIC: + s->arm_irq_enable |= val & 0xff; + break; + case IRQ_DISABLE_1: + s->gpu_irq_enable &= ~val; + break; + case IRQ_DISABLE_2: + s->gpu_irq_enable &= ~(val << 32); + break; + case IRQ_DISABLE_BASIC: + s->arm_irq_enable &= ~val & 0xff; + break; + default: + qemu_log_mask(LOG_GUEST_ERROR, "%s: Bad offset %"HWADDR_PRIx"\n", + __func__, offset); + return; + } + bcm2835_ic_update(s); +} + +static const MemoryRegionOps bcm2835_ic_ops = { + .read = bcm2835_ic_read, + .write = bcm2835_ic_write, + .endianness = DEVICE_NATIVE_ENDIAN, + .valid.min_access_size = 4, + .valid.max_access_size = 4, +}; + +static void bcm2835_ic_reset(DeviceState *d) +{ + BCM2835ICState *s = BCM2835_IC(d); + + s->gpu_irq_enable = 0; + s->arm_irq_enable = 0; + s->fiq_enable = false; + s->fiq_select = 0; +} + +static void bcm2835_ic_init(Object *obj) +{ + BCM2835ICState *s = BCM2835_IC(obj); + + memory_region_init_io(&s->iomem, obj, &bcm2835_ic_ops, s, TYPE_BCM2835_IC, + 0x200); + sysbus_init_mmio(SYS_BUS_DEVICE(s), &s->iomem); + + qdev_init_gpio_in_named(DEVICE(s), bcm2835_ic_set_gpu_irq, + BCM2835_IC_GPU_IRQ, GPU_IRQS); + qdev_init_gpio_in_named(DEVICE(s), bcm2835_ic_set_arm_irq, + BCM2835_IC_ARM_IRQ, ARM_IRQS); + + sysbus_init_irq(SYS_BUS_DEVICE(s), &s->irq); + sysbus_init_irq(SYS_BUS_DEVICE(s), &s->fiq); +} + +static const VMStateDescription vmstate_bcm2835_ic = { + .name = TYPE_BCM2835_IC, + .version_id = 1, + .minimum_version_id = 1, + .fields = (VMStateField[]) { + VMSTATE_UINT64(gpu_irq_level, BCM2835ICState), + VMSTATE_UINT64(gpu_irq_enable, BCM2835ICState), + VMSTATE_UINT8(arm_irq_level, BCM2835ICState), + VMSTATE_UINT8(arm_irq_enable, BCM2835ICState), + VMSTATE_BOOL(fiq_enable, BCM2835ICState), + VMSTATE_UINT8(fiq_select, BCM2835ICState), + VMSTATE_END_OF_LIST() + } +}; + +static void bcm2835_ic_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + + dc->reset = bcm2835_ic_reset; + dc->vmsd = &vmstate_bcm2835_ic; +} + +static TypeInfo bcm2835_ic_info = { + .name = TYPE_BCM2835_IC, + .parent = TYPE_SYS_BUS_DEVICE, + .instance_size = sizeof(BCM2835ICState), + .class_init = bcm2835_ic_class_init, + .instance_init = bcm2835_ic_init, +}; + +static void bcm2835_ic_register_types(void) +{ + type_register_static(&bcm2835_ic_info); +} + +type_init(bcm2835_ic_register_types) diff --git a/hw/intc/bcm2836_control.c b/hw/intc/bcm2836_control.c new file mode 100644 index 0000000000..ad622aa99f --- /dev/null +++ b/hw/intc/bcm2836_control.c @@ -0,0 +1,303 @@ +/* + * Rasperry Pi 2 emulation ARM control logic module. + * Copyright (c) 2015, Microsoft + * Written by Andrew Baumann + * + * Based on bcm2835_ic.c (Raspberry Pi emulation) (c) 2012 Gregory Estrade + * This code is licensed under the GNU GPLv2 and later. + * + * At present, only implements interrupt routing, and mailboxes (i.e., + * not local timer, PMU interrupt, or AXI counters). + * + * Ref: + * https://www.raspberrypi.org/documentation/hardware/raspberrypi/bcm2836/QA7_rev3.4.pdf + */ + +#include "hw/intc/bcm2836_control.h" + +#define REG_GPU_ROUTE 0x0c +#define REG_TIMERCONTROL 0x40 +#define REG_MBOXCONTROL 0x50 +#define REG_IRQSRC 0x60 +#define REG_FIQSRC 0x70 +#define REG_MBOX0_WR 0x80 +#define REG_MBOX0_RDCLR 0xc0 +#define REG_LIMIT 0x100 + +#define IRQ_BIT(cntrl, num) (((cntrl) & (1 << (num))) != 0) +#define FIQ_BIT(cntrl, num) (((cntrl) & (1 << ((num) + 4))) != 0) + +#define IRQ_CNTPSIRQ 0 +#define IRQ_CNTPNSIRQ 1 +#define IRQ_CNTHPIRQ 2 +#define IRQ_CNTVIRQ 3 +#define IRQ_MAILBOX0 4 +#define IRQ_MAILBOX1 5 +#define IRQ_MAILBOX2 6 +#define IRQ_MAILBOX3 7 +#define IRQ_GPU 8 +#define IRQ_PMU 9 +#define IRQ_AXI 10 +#define IRQ_TIMER 11 +#define IRQ_MAX IRQ_TIMER + +static void deliver_local(BCM2836ControlState *s, uint8_t core, uint8_t irq, + uint32_t controlreg, uint8_t controlidx) +{ + if (FIQ_BIT(controlreg, controlidx)) { + /* deliver a FIQ */ + s->fiqsrc[core] |= (uint32_t)1 << irq; + } else if (IRQ_BIT(controlreg, controlidx)) { + /* deliver an IRQ */ + s->irqsrc[core] |= (uint32_t)1 << irq; + } else { + /* the interrupt is masked */ + } +} + +/* Update interrupts. */ +static void bcm2836_control_update(BCM2836ControlState *s) +{ + int i, j; + + /* reset pending IRQs/FIQs */ + for (i = 0; i < BCM2836_NCORES; i++) { + s->irqsrc[i] = s->fiqsrc[i] = 0; + } + + /* apply routing logic, update status regs */ + if (s->gpu_irq) { + assert(s->route_gpu_irq < BCM2836_NCORES); + s->irqsrc[s->route_gpu_irq] |= (uint32_t)1 << IRQ_GPU; + } + + if (s->gpu_fiq) { + assert(s->route_gpu_fiq < BCM2836_NCORES); + s->fiqsrc[s->route_gpu_fiq] |= (uint32_t)1 << IRQ_GPU; + } + + for (i = 0; i < BCM2836_NCORES; i++) { + /* handle local timer interrupts for this core */ + if (s->timerirqs[i]) { + assert(s->timerirqs[i] < (1 << (IRQ_CNTVIRQ + 1))); /* sane mask? */ + for (j = 0; j <= IRQ_CNTVIRQ; j++) { + if ((s->timerirqs[i] & (1 << j)) != 0) { + /* local interrupt j is set */ + deliver_local(s, i, j, s->timercontrol[i], j); + } + } + } + + /* handle mailboxes for this core */ + for (j = 0; j < BCM2836_MBPERCORE; j++) { + if (s->mailboxes[i * BCM2836_MBPERCORE + j] != 0) { + /* mailbox j is set */ + deliver_local(s, i, j + IRQ_MAILBOX0, s->mailboxcontrol[i], j); + } + } + } + + /* call set_irq appropriately for each output */ + for (i = 0; i < BCM2836_NCORES; i++) { + qemu_set_irq(s->irq[i], s->irqsrc[i] != 0); + qemu_set_irq(s->fiq[i], s->fiqsrc[i] != 0); + } +} + +static void bcm2836_control_set_local_irq(void *opaque, int core, int local_irq, + int level) +{ + BCM2836ControlState *s = opaque; + + assert(core >= 0 && core < BCM2836_NCORES); + assert(local_irq >= 0 && local_irq <= IRQ_CNTVIRQ); + + s->timerirqs[core] = deposit32(s->timerirqs[core], local_irq, 1, !!level); + + bcm2836_control_update(s); +} + +/* XXX: the following wrapper functions are a kludgy workaround, + * needed because I can't seem to pass useful information in the "irq" + * parameter when using named interrupts. Feel free to clean this up! + */ + +static void bcm2836_control_set_local_irq0(void *opaque, int core, int level) +{ + bcm2836_control_set_local_irq(opaque, core, 0, level); +} + +static void bcm2836_control_set_local_irq1(void *opaque, int core, int level) +{ + bcm2836_control_set_local_irq(opaque, core, 1, level); +} + +static void bcm2836_control_set_local_irq2(void *opaque, int core, int level) +{ + bcm2836_control_set_local_irq(opaque, core, 2, level); +} + +static void bcm2836_control_set_local_irq3(void *opaque, int core, int level) +{ + bcm2836_control_set_local_irq(opaque, core, 3, level); +} + +static void bcm2836_control_set_gpu_irq(void *opaque, int irq, int level) +{ + BCM2836ControlState *s = opaque; + + s->gpu_irq = level; + + bcm2836_control_update(s); +} + +static void bcm2836_control_set_gpu_fiq(void *opaque, int irq, int level) +{ + BCM2836ControlState *s = opaque; + + s->gpu_fiq = level; + + bcm2836_control_update(s); +} + +static uint64_t bcm2836_control_read(void *opaque, hwaddr offset, unsigned size) +{ + BCM2836ControlState *s = opaque; + + if (offset == REG_GPU_ROUTE) { + assert(s->route_gpu_fiq < BCM2836_NCORES + && s->route_gpu_irq < BCM2836_NCORES); + return ((uint32_t)s->route_gpu_fiq << 2) | s->route_gpu_irq; + } else if (offset >= REG_TIMERCONTROL && offset < REG_MBOXCONTROL) { + return s->timercontrol[(offset - REG_TIMERCONTROL) >> 2]; + } else if (offset >= REG_MBOXCONTROL && offset < REG_IRQSRC) { + return s->mailboxcontrol[(offset - REG_MBOXCONTROL) >> 2]; + } else if (offset >= REG_IRQSRC && offset < REG_FIQSRC) { + return s->irqsrc[(offset - REG_IRQSRC) >> 2]; + } else if (offset >= REG_FIQSRC && offset < REG_MBOX0_WR) { + return s->fiqsrc[(offset - REG_FIQSRC) >> 2]; + } else if (offset >= REG_MBOX0_RDCLR && offset < REG_LIMIT) { + return s->mailboxes[(offset - REG_MBOX0_RDCLR) >> 2]; + } else { + qemu_log_mask(LOG_GUEST_ERROR, "%s: Bad offset %"HWADDR_PRIx"\n", + __func__, offset); + return 0; + } +} + +static void bcm2836_control_write(void *opaque, hwaddr offset, + uint64_t val, unsigned size) +{ + BCM2836ControlState *s = opaque; + + if (offset == REG_GPU_ROUTE) { + s->route_gpu_irq = val & 0x3; + s->route_gpu_fiq = (val >> 2) & 0x3; + } else if (offset >= REG_TIMERCONTROL && offset < REG_MBOXCONTROL) { + s->timercontrol[(offset - REG_TIMERCONTROL) >> 2] = val & 0xff; + } else if (offset >= REG_MBOXCONTROL && offset < REG_IRQSRC) { + s->mailboxcontrol[(offset - REG_MBOXCONTROL) >> 2] = val & 0xff; + } else if (offset >= REG_MBOX0_WR && offset < REG_MBOX0_RDCLR) { + s->mailboxes[(offset - REG_MBOX0_WR) >> 2] |= val; + } else if (offset >= REG_MBOX0_RDCLR && offset < REG_LIMIT) { + s->mailboxes[(offset - REG_MBOX0_RDCLR) >> 2] &= ~val; + } else { + qemu_log_mask(LOG_GUEST_ERROR, "%s: Bad offset %"HWADDR_PRIx"\n", + __func__, offset); + return; + } + + bcm2836_control_update(s); +} + +static const MemoryRegionOps bcm2836_control_ops = { + .read = bcm2836_control_read, + .write = bcm2836_control_write, + .endianness = DEVICE_NATIVE_ENDIAN, + .valid.min_access_size = 4, + .valid.max_access_size = 4, +}; + +static void bcm2836_control_reset(DeviceState *d) +{ + BCM2836ControlState *s = BCM2836_CONTROL(d); + int i; + + s->route_gpu_irq = s->route_gpu_fiq = 0; + + for (i = 0; i < BCM2836_NCORES; i++) { + s->timercontrol[i] = 0; + s->mailboxcontrol[i] = 0; + } + + for (i = 0; i < BCM2836_NCORES * BCM2836_MBPERCORE; i++) { + s->mailboxes[i] = 0; + } +} + +static void bcm2836_control_init(Object *obj) +{ + BCM2836ControlState *s = BCM2836_CONTROL(obj); + DeviceState *dev = DEVICE(obj); + + memory_region_init_io(&s->iomem, obj, &bcm2836_control_ops, s, + TYPE_BCM2836_CONTROL, REG_LIMIT); + sysbus_init_mmio(SYS_BUS_DEVICE(s), &s->iomem); + + /* inputs from each CPU core */ + qdev_init_gpio_in_named(dev, bcm2836_control_set_local_irq0, "cntpsirq", + BCM2836_NCORES); + qdev_init_gpio_in_named(dev, bcm2836_control_set_local_irq1, "cntpnsirq", + BCM2836_NCORES); + qdev_init_gpio_in_named(dev, bcm2836_control_set_local_irq2, "cnthpirq", + BCM2836_NCORES); + qdev_init_gpio_in_named(dev, bcm2836_control_set_local_irq3, "cntvirq", + BCM2836_NCORES); + + /* IRQ and FIQ inputs from upstream bcm2835 controller */ + qdev_init_gpio_in_named(dev, bcm2836_control_set_gpu_irq, "gpu-irq", 1); + qdev_init_gpio_in_named(dev, bcm2836_control_set_gpu_fiq, "gpu-fiq", 1); + + /* outputs to CPU cores */ + qdev_init_gpio_out_named(dev, s->irq, "irq", BCM2836_NCORES); + qdev_init_gpio_out_named(dev, s->fiq, "fiq", BCM2836_NCORES); +} + +static const VMStateDescription vmstate_bcm2836_control = { + .name = TYPE_BCM2836_CONTROL, + .version_id = 1, + .minimum_version_id = 1, + .fields = (VMStateField[]) { + VMSTATE_UINT32_ARRAY(mailboxes, BCM2836ControlState, + BCM2836_NCORES * BCM2836_MBPERCORE), + VMSTATE_UINT8(route_gpu_irq, BCM2836ControlState), + VMSTATE_UINT8(route_gpu_fiq, BCM2836ControlState), + VMSTATE_UINT32_ARRAY(timercontrol, BCM2836ControlState, BCM2836_NCORES), + VMSTATE_UINT32_ARRAY(mailboxcontrol, BCM2836ControlState, + BCM2836_NCORES), + VMSTATE_END_OF_LIST() + } +}; + +static void bcm2836_control_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + + dc->reset = bcm2836_control_reset; + dc->vmsd = &vmstate_bcm2836_control; +} + +static TypeInfo bcm2836_control_info = { + .name = TYPE_BCM2836_CONTROL, + .parent = TYPE_SYS_BUS_DEVICE, + .instance_size = sizeof(BCM2836ControlState), + .class_init = bcm2836_control_class_init, + .instance_init = bcm2836_control_init, +}; + +static void bcm2836_control_register_types(void) +{ + type_register_static(&bcm2836_control_info); +} + +type_init(bcm2836_control_register_types) diff --git a/hw/misc/Makefile.objs b/hw/misc/Makefile.objs index d4765c2516..ea6cd3c9ff 100644 --- a/hw/misc/Makefile.objs +++ b/hw/misc/Makefile.objs @@ -36,6 +36,8 @@ obj-$(CONFIG_OMAP) += omap_gpmc.o obj-$(CONFIG_OMAP) += omap_l4.o obj-$(CONFIG_OMAP) += omap_sdrc.o obj-$(CONFIG_OMAP) += omap_tap.o +obj-$(CONFIG_RASPI) += bcm2835_mbox.o +obj-$(CONFIG_RASPI) += bcm2835_property.o obj-$(CONFIG_SLAVIO) += slavio_misc.o obj-$(CONFIG_ZYNQ) += zynq_slcr.o obj-$(CONFIG_ZYNQ) += zynq-xadc.o diff --git a/hw/misc/bcm2835_mbox.c b/hw/misc/bcm2835_mbox.c new file mode 100644 index 0000000000..df1d6e6ad6 --- /dev/null +++ b/hw/misc/bcm2835_mbox.c @@ -0,0 +1,333 @@ +/* + * Raspberry Pi emulation (c) 2012 Gregory Estrade + * This code is licensed under the GNU GPLv2 and later. + * + * This file models the system mailboxes, which are used for + * communication with low-bandwidth GPU peripherals. Refs: + * https://github.com/raspberrypi/firmware/wiki/Mailboxes + * https://github.com/raspberrypi/firmware/wiki/Accessing-mailboxes + */ + +#include "hw/misc/bcm2835_mbox.h" + +#define MAIL0_PEEK 0x90 +#define MAIL0_SENDER 0x94 +#define MAIL1_STATUS 0xb8 + +/* Mailbox status register */ +#define MAIL0_STATUS 0x98 +#define ARM_MS_FULL 0x80000000 +#define ARM_MS_EMPTY 0x40000000 +#define ARM_MS_LEVEL 0x400000FF /* Max. value depends on mailbox depth */ + +/* MAILBOX config/status register */ +#define MAIL0_CONFIG 0x9c +/* ANY write to this register clears the error bits! */ +#define ARM_MC_IHAVEDATAIRQEN 0x00000001 /* mbox irq enable: has data */ +#define ARM_MC_IHAVESPACEIRQEN 0x00000002 /* mbox irq enable: has space */ +#define ARM_MC_OPPISEMPTYIRQEN 0x00000004 /* mbox irq enable: Opp is empty */ +#define ARM_MC_MAIL_CLEAR 0x00000008 /* mbox clear write 1, then 0 */ +#define ARM_MC_IHAVEDATAIRQPEND 0x00000010 /* mbox irq pending: has space */ +#define ARM_MC_IHAVESPACEIRQPEND 0x00000020 /* mbox irq pending: Opp is empty */ +#define ARM_MC_OPPISEMPTYIRQPEND 0x00000040 /* mbox irq pending */ +/* Bit 7 is unused */ +#define ARM_MC_ERRNOOWN 0x00000100 /* error : none owner read from mailbox */ +#define ARM_MC_ERROVERFLW 0x00000200 /* error : write to fill mailbox */ +#define ARM_MC_ERRUNDRFLW 0x00000400 /* error : read from empty mailbox */ + +static void mbox_update_status(BCM2835Mbox *mb) +{ + mb->status &= ~(ARM_MS_EMPTY | ARM_MS_FULL); + if (mb->count == 0) { + mb->status |= ARM_MS_EMPTY; + } else if (mb->count == MBOX_SIZE) { + mb->status |= ARM_MS_FULL; + } +} + +static void mbox_reset(BCM2835Mbox *mb) +{ + int n; + + mb->count = 0; + mb->config = 0; + for (n = 0; n < MBOX_SIZE; n++) { + mb->reg[n] = MBOX_INVALID_DATA; + } + mbox_update_status(mb); +} + +static uint32_t mbox_pull(BCM2835Mbox *mb, int index) +{ + int n; + uint32_t val; + + assert(mb->count > 0); + assert(index < mb->count); + + val = mb->reg[index]; + for (n = index + 1; n < mb->count; n++) { + mb->reg[n - 1] = mb->reg[n]; + } + mb->count--; + mb->reg[mb->count] = MBOX_INVALID_DATA; + + mbox_update_status(mb); + + return val; +} + +static void mbox_push(BCM2835Mbox *mb, uint32_t val) +{ + assert(mb->count < MBOX_SIZE); + mb->reg[mb->count++] = val; + mbox_update_status(mb); +} + +static void bcm2835_mbox_update(BCM2835MboxState *s) +{ + uint32_t value; + bool set; + int n; + + s->mbox_irq_disabled = true; + + /* Get pending responses and put them in the vc->arm mbox, + * as long as it's not full + */ + for (n = 0; n < MBOX_CHAN_COUNT; n++) { + while (s->available[n] && !(s->mbox[0].status & ARM_MS_FULL)) { + value = ldl_phys(&s->mbox_as, n << MBOX_AS_CHAN_SHIFT); + assert(value != MBOX_INVALID_DATA); /* Pending interrupt but no data */ + mbox_push(&s->mbox[0], value); + } + } + + /* TODO (?): Try to push pending requests from the arm->vc mbox */ + + /* Re-enable calls from the IRQ routine */ + s->mbox_irq_disabled = false; + + /* Update ARM IRQ status */ + set = false; + s->mbox[0].config &= ~ARM_MC_IHAVEDATAIRQPEND; + if (!(s->mbox[0].status & ARM_MS_EMPTY)) { + s->mbox[0].config |= ARM_MC_IHAVEDATAIRQPEND; + if (s->mbox[0].config & ARM_MC_IHAVEDATAIRQEN) { + set = true; + } + } + qemu_set_irq(s->arm_irq, set); +} + +static void bcm2835_mbox_set_irq(void *opaque, int irq, int level) +{ + BCM2835MboxState *s = opaque; + + s->available[irq] = level; + + /* avoid recursively calling bcm2835_mbox_update when the interrupt + * status changes due to the ldl_phys call within that function + */ + if (!s->mbox_irq_disabled) { + bcm2835_mbox_update(s); + } +} + +static uint64_t bcm2835_mbox_read(void *opaque, hwaddr offset, unsigned size) +{ + BCM2835MboxState *s = opaque; + uint32_t res = 0; + + offset &= 0xff; + + switch (offset) { + case 0x80 ... 0x8c: /* MAIL0_READ */ + if (s->mbox[0].status & ARM_MS_EMPTY) { + res = MBOX_INVALID_DATA; + } else { + res = mbox_pull(&s->mbox[0], 0); + } + break; + + case MAIL0_PEEK: + res = s->mbox[0].reg[0]; + break; + + case MAIL0_SENDER: + break; + + case MAIL0_STATUS: + res = s->mbox[0].status; + break; + + case MAIL0_CONFIG: + res = s->mbox[0].config; + break; + + case MAIL1_STATUS: + res = s->mbox[1].status; + break; + + default: + qemu_log_mask(LOG_GUEST_ERROR, "%s: Bad offset %"HWADDR_PRIx"\n", + __func__, offset); + return 0; + } + + bcm2835_mbox_update(s); + + return res; +} + +static void bcm2835_mbox_write(void *opaque, hwaddr offset, + uint64_t value, unsigned size) +{ + BCM2835MboxState *s = opaque; + hwaddr childaddr; + uint8_t ch; + + offset &= 0xff; + + switch (offset) { + case MAIL0_SENDER: + break; + + case MAIL0_CONFIG: + s->mbox[0].config &= ~ARM_MC_IHAVEDATAIRQEN; + s->mbox[0].config |= value & ARM_MC_IHAVEDATAIRQEN; + break; + + case 0xa0 ... 0xac: /* MAIL1_WRITE */ + if (s->mbox[1].status & ARM_MS_FULL) { + /* Mailbox full */ + qemu_log_mask(LOG_GUEST_ERROR, "%s: mailbox full\n", __func__); + } else { + ch = value & 0xf; + if (ch < MBOX_CHAN_COUNT) { + childaddr = ch << MBOX_AS_CHAN_SHIFT; + if (ldl_phys(&s->mbox_as, childaddr + MBOX_AS_PENDING)) { + /* Child busy, push delayed. Push it in the arm->vc mbox */ + mbox_push(&s->mbox[1], value); + } else { + /* Push it directly to the child device */ + stl_phys(&s->mbox_as, childaddr, value); + } + } else { + /* Invalid channel number */ + qemu_log_mask(LOG_GUEST_ERROR, "%s: invalid channel %u\n", + __func__, ch); + } + } + break; + + default: + qemu_log_mask(LOG_GUEST_ERROR, "%s: Bad offset %"HWADDR_PRIx"\n", + __func__, offset); + return; + } + + bcm2835_mbox_update(s); +} + +static const MemoryRegionOps bcm2835_mbox_ops = { + .read = bcm2835_mbox_read, + .write = bcm2835_mbox_write, + .endianness = DEVICE_NATIVE_ENDIAN, + .valid.min_access_size = 4, + .valid.max_access_size = 4, +}; + +/* vmstate of a single mailbox */ +static const VMStateDescription vmstate_bcm2835_mbox_box = { + .name = TYPE_BCM2835_MBOX "_box", + .version_id = 1, + .minimum_version_id = 1, + .fields = (VMStateField[]) { + VMSTATE_UINT32_ARRAY(reg, BCM2835Mbox, MBOX_SIZE), + VMSTATE_UINT32(count, BCM2835Mbox), + VMSTATE_UINT32(status, BCM2835Mbox), + VMSTATE_UINT32(config, BCM2835Mbox), + VMSTATE_END_OF_LIST() + } +}; + +/* vmstate of the entire device */ +static const VMStateDescription vmstate_bcm2835_mbox = { + .name = TYPE_BCM2835_MBOX, + .version_id = 1, + .minimum_version_id = 1, + .minimum_version_id_old = 1, + .fields = (VMStateField[]) { + VMSTATE_BOOL_ARRAY(available, BCM2835MboxState, MBOX_CHAN_COUNT), + VMSTATE_STRUCT_ARRAY(mbox, BCM2835MboxState, 2, 1, + vmstate_bcm2835_mbox_box, BCM2835Mbox), + VMSTATE_END_OF_LIST() + } +}; + +static void bcm2835_mbox_init(Object *obj) +{ + BCM2835MboxState *s = BCM2835_MBOX(obj); + + memory_region_init_io(&s->iomem, obj, &bcm2835_mbox_ops, s, + TYPE_BCM2835_MBOX, 0x400); + sysbus_init_mmio(SYS_BUS_DEVICE(s), &s->iomem); + sysbus_init_irq(SYS_BUS_DEVICE(s), &s->arm_irq); + qdev_init_gpio_in(DEVICE(s), bcm2835_mbox_set_irq, MBOX_CHAN_COUNT); +} + +static void bcm2835_mbox_reset(DeviceState *dev) +{ + BCM2835MboxState *s = BCM2835_MBOX(dev); + int n; + + mbox_reset(&s->mbox[0]); + mbox_reset(&s->mbox[1]); + s->mbox_irq_disabled = false; + for (n = 0; n < MBOX_CHAN_COUNT; n++) { + s->available[n] = false; + } +} + +static void bcm2835_mbox_realize(DeviceState *dev, Error **errp) +{ + BCM2835MboxState *s = BCM2835_MBOX(dev); + Object *obj; + Error *err = NULL; + + obj = object_property_get_link(OBJECT(dev), "mbox-mr", &err); + if (obj == NULL) { + error_setg(errp, "%s: required mbox-mr link not found: %s", + __func__, error_get_pretty(err)); + return; + } + + s->mbox_mr = MEMORY_REGION(obj); + address_space_init(&s->mbox_as, s->mbox_mr, NULL); + bcm2835_mbox_reset(dev); +} + +static void bcm2835_mbox_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + + dc->realize = bcm2835_mbox_realize; + dc->reset = bcm2835_mbox_reset; + dc->vmsd = &vmstate_bcm2835_mbox; +} + +static TypeInfo bcm2835_mbox_info = { + .name = TYPE_BCM2835_MBOX, + .parent = TYPE_SYS_BUS_DEVICE, + .instance_size = sizeof(BCM2835MboxState), + .class_init = bcm2835_mbox_class_init, + .instance_init = bcm2835_mbox_init, +}; + +static void bcm2835_mbox_register_types(void) +{ + type_register_static(&bcm2835_mbox_info); +} + +type_init(bcm2835_mbox_register_types) diff --git a/hw/misc/bcm2835_property.c b/hw/misc/bcm2835_property.c new file mode 100644 index 0000000000..e42b43e72d --- /dev/null +++ b/hw/misc/bcm2835_property.c @@ -0,0 +1,287 @@ +/* + * Raspberry Pi emulation (c) 2012 Gregory Estrade + * This code is licensed under the GNU GPLv2 and later. + */ + +#include "hw/misc/bcm2835_property.h" +#include "hw/misc/bcm2835_mbox_defs.h" +#include "sysemu/dma.h" + +/* https://github.com/raspberrypi/firmware/wiki/Mailbox-property-interface */ + +static void bcm2835_property_mbox_push(BCM2835PropertyState *s, uint32_t value) +{ + uint32_t tag; + uint32_t bufsize; + uint32_t tot_len; + size_t resplen; + uint32_t tmp; + + value &= ~0xf; + + s->addr = value; + + tot_len = ldl_phys(&s->dma_as, value); + + /* @(addr + 4) : Buffer response code */ + value = s->addr + 8; + while (value + 8 <= s->addr + tot_len) { + tag = ldl_phys(&s->dma_as, value); + bufsize = ldl_phys(&s->dma_as, value + 4); + /* @(value + 8) : Request/response indicator */ + resplen = 0; + switch (tag) { + case 0x00000000: /* End tag */ + break; + case 0x00000001: /* Get firmware revision */ + stl_phys(&s->dma_as, value + 12, 346337); + resplen = 4; + break; + case 0x00010001: /* Get board model */ + qemu_log_mask(LOG_UNIMP, + "bcm2835_property: %x get board model NYI\n", tag); + resplen = 4; + break; + case 0x00010002: /* Get board revision */ + qemu_log_mask(LOG_UNIMP, + "bcm2835_property: %x get board revision NYI\n", tag); + resplen = 4; + break; + case 0x00010003: /* Get board MAC address */ + resplen = sizeof(s->macaddr.a); + dma_memory_write(&s->dma_as, value + 12, s->macaddr.a, resplen); + break; + case 0x00010004: /* Get board serial */ + qemu_log_mask(LOG_UNIMP, + "bcm2835_property: %x get board serial NYI\n", tag); + resplen = 8; + break; + case 0x00010005: /* Get ARM memory */ + /* base */ + stl_phys(&s->dma_as, value + 12, 0); + /* size */ + stl_phys(&s->dma_as, value + 16, s->ram_size); + resplen = 8; + break; + case 0x00028001: /* Set power state */ + /* Assume that whatever device they asked for exists, + * and we'll just claim we set it to the desired state + */ + tmp = ldl_phys(&s->dma_as, value + 16); + stl_phys(&s->dma_as, value + 16, (tmp & 1)); + resplen = 8; + break; + + /* Clocks */ + + case 0x00030001: /* Get clock state */ + stl_phys(&s->dma_as, value + 16, 0x1); + resplen = 8; + break; + + case 0x00038001: /* Set clock state */ + qemu_log_mask(LOG_UNIMP, + "bcm2835_property: %x set clock state NYI\n", tag); + resplen = 8; + break; + + case 0x00030002: /* Get clock rate */ + case 0x00030004: /* Get max clock rate */ + case 0x00030007: /* Get min clock rate */ + switch (ldl_phys(&s->dma_as, value + 12)) { + case 1: /* EMMC */ + stl_phys(&s->dma_as, value + 16, 50000000); + break; + case 2: /* UART */ + stl_phys(&s->dma_as, value + 16, 3000000); + break; + default: + stl_phys(&s->dma_as, value + 16, 700000000); + break; + } + resplen = 8; + break; + + case 0x00038002: /* Set clock rate */ + case 0x00038004: /* Set max clock rate */ + case 0x00038007: /* Set min clock rate */ + qemu_log_mask(LOG_UNIMP, + "bcm2835_property: %x set clock rates NYI\n", tag); + resplen = 8; + break; + + /* Temperature */ + + case 0x00030006: /* Get temperature */ + stl_phys(&s->dma_as, value + 16, 25000); + resplen = 8; + break; + + case 0x0003000A: /* Get max temperature */ + stl_phys(&s->dma_as, value + 16, 99000); + resplen = 8; + break; + + + case 0x00060001: /* Get DMA channels */ + /* channels 2-5 */ + stl_phys(&s->dma_as, value + 12, 0x003C); + resplen = 4; + break; + + case 0x00050001: /* Get command line */ + resplen = 0; + break; + + default: + qemu_log_mask(LOG_GUEST_ERROR, + "bcm2835_property: unhandled tag %08x\n", tag); + break; + } + + if (tag == 0) { + break; + } + + stl_phys(&s->dma_as, value + 8, (1 << 31) | resplen); + value += bufsize + 12; + } + + /* Buffer response code */ + stl_phys(&s->dma_as, s->addr + 4, (1 << 31)); +} + +static uint64_t bcm2835_property_read(void *opaque, hwaddr offset, + unsigned size) +{ + BCM2835PropertyState *s = opaque; + uint32_t res = 0; + + switch (offset) { + case MBOX_AS_DATA: + res = MBOX_CHAN_PROPERTY | s->addr; + s->pending = false; + qemu_set_irq(s->mbox_irq, 0); + break; + + case MBOX_AS_PENDING: + res = s->pending; + break; + + default: + qemu_log_mask(LOG_GUEST_ERROR, "%s: Bad offset %"HWADDR_PRIx"\n", + __func__, offset); + return 0; + } + + return res; +} + +static void bcm2835_property_write(void *opaque, hwaddr offset, + uint64_t value, unsigned size) +{ + BCM2835PropertyState *s = opaque; + + switch (offset) { + case MBOX_AS_DATA: + /* bcm2835_mbox should check our pending status before pushing */ + assert(!s->pending); + s->pending = true; + bcm2835_property_mbox_push(s, value); + qemu_set_irq(s->mbox_irq, 1); + break; + + default: + qemu_log_mask(LOG_GUEST_ERROR, "%s: Bad offset %"HWADDR_PRIx"\n", + __func__, offset); + return; + } +} + +static const MemoryRegionOps bcm2835_property_ops = { + .read = bcm2835_property_read, + .write = bcm2835_property_write, + .endianness = DEVICE_NATIVE_ENDIAN, + .valid.min_access_size = 4, + .valid.max_access_size = 4, +}; + +static const VMStateDescription vmstate_bcm2835_property = { + .name = TYPE_BCM2835_PROPERTY, + .version_id = 1, + .minimum_version_id = 1, + .fields = (VMStateField[]) { + VMSTATE_MACADDR(macaddr, BCM2835PropertyState), + VMSTATE_UINT32(addr, BCM2835PropertyState), + VMSTATE_BOOL(pending, BCM2835PropertyState), + VMSTATE_END_OF_LIST() + } +}; + +static void bcm2835_property_init(Object *obj) +{ + BCM2835PropertyState *s = BCM2835_PROPERTY(obj); + + memory_region_init_io(&s->iomem, OBJECT(s), &bcm2835_property_ops, s, + TYPE_BCM2835_PROPERTY, 0x10); + sysbus_init_mmio(SYS_BUS_DEVICE(s), &s->iomem); + sysbus_init_irq(SYS_BUS_DEVICE(s), &s->mbox_irq); +} + +static void bcm2835_property_reset(DeviceState *dev) +{ + BCM2835PropertyState *s = BCM2835_PROPERTY(dev); + + s->pending = false; +} + +static void bcm2835_property_realize(DeviceState *dev, Error **errp) +{ + BCM2835PropertyState *s = BCM2835_PROPERTY(dev); + Object *obj; + Error *err = NULL; + + obj = object_property_get_link(OBJECT(dev), "dma-mr", &err); + if (obj == NULL) { + error_setg(errp, "%s: required dma-mr link not found: %s", + __func__, error_get_pretty(err)); + return; + } + + s->dma_mr = MEMORY_REGION(obj); + address_space_init(&s->dma_as, s->dma_mr, NULL); + + /* TODO: connect to MAC address of USB NIC device, once we emulate it */ + qemu_macaddr_default_if_unset(&s->macaddr); + + bcm2835_property_reset(dev); +} + +static Property bcm2835_property_props[] = { + DEFINE_PROP_UINT32("ram-size", BCM2835PropertyState, ram_size, 0), + DEFINE_PROP_END_OF_LIST() +}; + +static void bcm2835_property_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + + dc->props = bcm2835_property_props; + dc->realize = bcm2835_property_realize; + dc->vmsd = &vmstate_bcm2835_property; +} + +static TypeInfo bcm2835_property_info = { + .name = TYPE_BCM2835_PROPERTY, + .parent = TYPE_SYS_BUS_DEVICE, + .instance_size = sizeof(BCM2835PropertyState), + .class_init = bcm2835_property_class_init, + .instance_init = bcm2835_property_init, +}; + +static void bcm2835_property_register_types(void) +{ + type_register_static(&bcm2835_property_info); +} + +type_init(bcm2835_property_register_types) diff --git a/hw/timer/a9gtimer.c b/hw/timer/a9gtimer.c index c11a7bc75b..fa4602ca04 100644 --- a/hw/timer/a9gtimer.c +++ b/hw/timer/a9gtimer.c @@ -25,6 +25,7 @@ #include "qemu/timer.h" #include "qemu/bitops.h" #include "qemu/log.h" +#include "qom/cpu.h" #ifndef A9_GTIMER_ERR_DEBUG #define A9_GTIMER_ERR_DEBUG 0 diff --git a/include/exec/log.h b/include/exec/log.h new file mode 100644 index 0000000000..ba1c9b5682 --- /dev/null +++ b/include/exec/log.h @@ -0,0 +1,60 @@ +#ifndef QEMU_EXEC_LOG_H +#define QEMU_EXEC_LOG_H + +#include "qemu/log.h" +#include "qom/cpu.h" +#include "disas/disas.h" + +/* cpu_dump_state() logging functions: */ +/** + * log_cpu_state: + * @cpu: The CPU whose state is to be logged. + * @flags: Flags what to log. + * + * Logs the output of cpu_dump_state(). + */ +static inline void log_cpu_state(CPUState *cpu, int flags) +{ + if (qemu_log_enabled()) { + cpu_dump_state(cpu, qemu_logfile, fprintf, flags); + } +} + +/** + * log_cpu_state_mask: + * @mask: Mask when to log. + * @cpu: The CPU whose state is to be logged. + * @flags: Flags what to log. + * + * Logs the output of cpu_dump_state() if loglevel includes @mask. + */ +static inline void log_cpu_state_mask(int mask, CPUState *cpu, int flags) +{ + if (qemu_loglevel & mask) { + log_cpu_state(cpu, flags); + } +} + +#ifdef NEED_CPU_H +/* disas() and target_disas() to qemu_logfile: */ +static inline void log_target_disas(CPUState *cpu, target_ulong start, + target_ulong len, int flags) +{ + target_disas(qemu_logfile, cpu, start, len, flags); +} + +static inline void log_disas(void *code, unsigned long size) +{ + disas(qemu_logfile, code, size); +} + +#if defined(CONFIG_USER_ONLY) +/* page_dump() output to the log file: */ +static inline void log_page_dump(void) +{ + page_dump(qemu_logfile); +} +#endif +#endif + +#endif diff --git a/include/hw/arm/arm.h b/include/hw/arm/arm.h index c26b0e357f..52ecf4aa8f 100644 --- a/include/hw/arm/arm.h +++ b/include/hw/arm/arm.h @@ -122,6 +122,11 @@ struct arm_boot_info { */ void arm_load_kernel(ARMCPU *cpu, struct arm_boot_info *info); +/* Write a secure board setup routine with a dummy handler for SMCs */ +void arm_write_secure_board_setup_dummy_smc(ARMCPU *cpu, + const struct arm_boot_info *info, + hwaddr mvbar_addr); + /* Multiplication factor to convert from system clock ticks to qemu timer ticks. */ extern int system_clock_scale; diff --git a/include/hw/arm/bcm2835_peripherals.h b/include/hw/arm/bcm2835_peripherals.h new file mode 100644 index 0000000000..5d888dca53 --- /dev/null +++ b/include/hw/arm/bcm2835_peripherals.h @@ -0,0 +1,42 @@ +/* + * Raspberry Pi emulation (c) 2012 Gregory Estrade + * Upstreaming code cleanup [including bcm2835_*] (c) 2013 Jan Petrous + * + * Rasperry Pi 2 emulation and refactoring Copyright (c) 2015, Microsoft + * Written by Andrew Baumann + * + * This code is licensed under the GNU GPLv2 and later. + */ + +#ifndef BCM2835_PERIPHERALS_H +#define BCM2835_PERIPHERALS_H + +#include "qemu-common.h" +#include "exec/address-spaces.h" +#include "hw/sysbus.h" +#include "hw/intc/bcm2835_ic.h" +#include "hw/misc/bcm2835_property.h" +#include "hw/misc/bcm2835_mbox.h" +#include "hw/sd/sdhci.h" + +#define TYPE_BCM2835_PERIPHERALS "bcm2835-peripherals" +#define BCM2835_PERIPHERALS(obj) \ + OBJECT_CHECK(BCM2835PeripheralState, (obj), TYPE_BCM2835_PERIPHERALS) + +typedef struct BCM2835PeripheralState { + /*< private >*/ + SysBusDevice parent_obj; + /*< public >*/ + + MemoryRegion peri_mr, peri_mr_alias, gpu_bus_mr, mbox_mr; + MemoryRegion ram_alias[4]; + qemu_irq irq, fiq; + + SysBusDevice *uart0; + BCM2835ICState ic; + BCM2835PropertyState property; + BCM2835MboxState mboxes; + SDHCIState sdhci; +} BCM2835PeripheralState; + +#endif /* BCM2835_PERIPHERALS_H */ diff --git a/include/hw/arm/bcm2836.h b/include/hw/arm/bcm2836.h new file mode 100644 index 0000000000..76de1996af --- /dev/null +++ b/include/hw/arm/bcm2836.h @@ -0,0 +1,35 @@ +/* + * Raspberry Pi emulation (c) 2012 Gregory Estrade + * Upstreaming code cleanup [including bcm2835_*] (c) 2013 Jan Petrous + * + * Rasperry Pi 2 emulation and refactoring Copyright (c) 2015, Microsoft + * Written by Andrew Baumann + * + * This code is licensed under the GNU GPLv2 and later. + */ + +#ifndef BCM2836_H +#define BCM2836_H + +#include "hw/arm/arm.h" +#include "hw/arm/bcm2835_peripherals.h" +#include "hw/intc/bcm2836_control.h" + +#define TYPE_BCM2836 "bcm2836" +#define BCM2836(obj) OBJECT_CHECK(BCM2836State, (obj), TYPE_BCM2836) + +#define BCM2836_NCPUS 4 + +typedef struct BCM2836State { + /*< private >*/ + DeviceState parent_obj; + /*< public >*/ + + uint32_t enabled_cpus; + + ARMCPU cpus[BCM2836_NCPUS]; + BCM2836ControlState control; + BCM2835PeripheralState peripherals; +} BCM2836State; + +#endif /* BCM2836_H */ diff --git a/include/hw/arm/raspi_platform.h b/include/hw/arm/raspi_platform.h new file mode 100644 index 0000000000..6467e88ae6 --- /dev/null +++ b/include/hw/arm/raspi_platform.h @@ -0,0 +1,128 @@ +/* + * bcm2708 aka bcm2835/2836 aka Raspberry Pi/Pi2 SoC platform defines + * + * These definitions are derived from those in Raspbian Linux at + * arch/arm/mach-{bcm2708,bcm2709}/include/mach/platform.h + * where they carry the following notice: + * + * Copyright (C) 2010 Broadcom + * + * 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. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#define MCORE_OFFSET 0x0000 /* Fake frame buffer device + * (the multicore sync block) */ +#define IC0_OFFSET 0x2000 +#define ST_OFFSET 0x3000 /* System Timer */ +#define MPHI_OFFSET 0x6000 /* Message-based Parallel Host Intf. */ +#define DMA_OFFSET 0x7000 /* DMA controller, channels 0-14 */ +#define ARM_OFFSET 0xB000 /* BCM2708 ARM control block */ +#define ARMCTRL_OFFSET (ARM_OFFSET + 0x000) +#define ARMCTRL_IC_OFFSET (ARM_OFFSET + 0x200) /* Interrupt controller */ +#define ARMCTRL_TIMER0_1_OFFSET (ARM_OFFSET + 0x400) /* Timer 0 and 1 */ +#define ARMCTRL_0_SBM_OFFSET (ARM_OFFSET + 0x800) /* User 0 (ARM) Semaphores + * Doorbells & Mailboxes */ +#define PM_OFFSET 0x100000 /* Power Management, Reset controller + * and Watchdog registers */ +#define PCM_CLOCK_OFFSET 0x101098 +#define RNG_OFFSET 0x104000 +#define GPIO_OFFSET 0x200000 +#define UART0_OFFSET 0x201000 +#define MMCI0_OFFSET 0x202000 +#define I2S_OFFSET 0x203000 +#define SPI0_OFFSET 0x204000 +#define BSC0_OFFSET 0x205000 /* BSC0 I2C/TWI */ +#define UART1_OFFSET 0x215000 +#define EMMC_OFFSET 0x300000 +#define SMI_OFFSET 0x600000 +#define BSC1_OFFSET 0x804000 /* BSC1 I2C/TWI */ +#define USB_OFFSET 0x980000 /* DTC_OTG USB controller */ +#define DMA15_OFFSET 0xE05000 /* DMA controller, channel 15 */ + +/* GPU interrupts */ +#define INTERRUPT_TIMER0 0 +#define INTERRUPT_TIMER1 1 +#define INTERRUPT_TIMER2 2 +#define INTERRUPT_TIMER3 3 +#define INTERRUPT_CODEC0 4 +#define INTERRUPT_CODEC1 5 +#define INTERRUPT_CODEC2 6 +#define INTERRUPT_JPEG 7 +#define INTERRUPT_ISP 8 +#define INTERRUPT_USB 9 +#define INTERRUPT_3D 10 +#define INTERRUPT_TRANSPOSER 11 +#define INTERRUPT_MULTICORESYNC0 12 +#define INTERRUPT_MULTICORESYNC1 13 +#define INTERRUPT_MULTICORESYNC2 14 +#define INTERRUPT_MULTICORESYNC3 15 +#define INTERRUPT_DMA0 16 +#define INTERRUPT_DMA1 17 +#define INTERRUPT_DMA2 18 +#define INTERRUPT_DMA3 19 +#define INTERRUPT_DMA4 20 +#define INTERRUPT_DMA5 21 +#define INTERRUPT_DMA6 22 +#define INTERRUPT_DMA7 23 +#define INTERRUPT_DMA8 24 +#define INTERRUPT_DMA9 25 +#define INTERRUPT_DMA10 26 +#define INTERRUPT_DMA11 27 +#define INTERRUPT_DMA12 28 +#define INTERRUPT_AUX 29 +#define INTERRUPT_ARM 30 +#define INTERRUPT_VPUDMA 31 +#define INTERRUPT_HOSTPORT 32 +#define INTERRUPT_VIDEOSCALER 33 +#define INTERRUPT_CCP2TX 34 +#define INTERRUPT_SDC 35 +#define INTERRUPT_DSI0 36 +#define INTERRUPT_AVE 37 +#define INTERRUPT_CAM0 38 +#define INTERRUPT_CAM1 39 +#define INTERRUPT_HDMI0 40 +#define INTERRUPT_HDMI1 41 +#define INTERRUPT_PIXELVALVE1 42 +#define INTERRUPT_I2CSPISLV 43 +#define INTERRUPT_DSI1 44 +#define INTERRUPT_PWA0 45 +#define INTERRUPT_PWA1 46 +#define INTERRUPT_CPR 47 +#define INTERRUPT_SMI 48 +#define INTERRUPT_GPIO0 49 +#define INTERRUPT_GPIO1 50 +#define INTERRUPT_GPIO2 51 +#define INTERRUPT_GPIO3 52 +#define INTERRUPT_I2C 53 +#define INTERRUPT_SPI 54 +#define INTERRUPT_I2SPCM 55 +#define INTERRUPT_SDIO 56 +#define INTERRUPT_UART 57 +#define INTERRUPT_SLIMBUS 58 +#define INTERRUPT_VEC 59 +#define INTERRUPT_CPG 60 +#define INTERRUPT_RNG 61 +#define INTERRUPT_ARASANSDIO 62 +#define INTERRUPT_AVSPMON 63 + +/* ARM CPU IRQs use a private number space */ +#define INTERRUPT_ARM_TIMER 0 +#define INTERRUPT_ARM_MAILBOX 1 +#define INTERRUPT_ARM_DOORBELL_0 2 +#define INTERRUPT_ARM_DOORBELL_1 3 +#define INTERRUPT_VPU0_HALTED 4 +#define INTERRUPT_VPU1_HALTED 5 +#define INTERRUPT_ILLEGAL_TYPE0 6 +#define INTERRUPT_ILLEGAL_TYPE1 7 diff --git a/include/hw/arm/virt-acpi-build.h b/include/hw/arm/virt-acpi-build.h index 744b666385..7d3700ebf6 100644 --- a/include/hw/arm/virt-acpi-build.h +++ b/include/hw/arm/virt-acpi-build.h @@ -23,7 +23,6 @@ #include "qemu-common.h" #include "hw/arm/virt.h" -#define VIRT_ACPI_CPU_ID_LIMIT 8 #define ACPI_GICC_ENABLED 1 typedef struct VirtGuestInfo { diff --git a/include/hw/intc/bcm2835_ic.h b/include/hw/intc/bcm2835_ic.h new file mode 100644 index 0000000000..fb75fa0064 --- /dev/null +++ b/include/hw/intc/bcm2835_ic.h @@ -0,0 +1,33 @@ +/* + * Raspberry Pi emulation (c) 2012 Gregory Estrade + * This code is licensed under the GNU GPLv2 and later. + */ + +#ifndef BCM2835_IC_H +#define BCM2835_IC_H + +#include "hw/sysbus.h" + +#define TYPE_BCM2835_IC "bcm2835-ic" +#define BCM2835_IC(obj) OBJECT_CHECK(BCM2835ICState, (obj), TYPE_BCM2835_IC) + +#define BCM2835_IC_GPU_IRQ "gpu-irq" +#define BCM2835_IC_ARM_IRQ "arm-irq" + +typedef struct BCM2835ICState { + /*< private >*/ + SysBusDevice busdev; + /*< public >*/ + + MemoryRegion iomem; + qemu_irq irq; + qemu_irq fiq; + + /* 64 GPU IRQs + 8 ARM IRQs = 72 total (GPU first) */ + uint64_t gpu_irq_level, gpu_irq_enable; + uint8_t arm_irq_level, arm_irq_enable; + bool fiq_enable; + uint8_t fiq_select; +} BCM2835ICState; + +#endif diff --git a/include/hw/intc/bcm2836_control.h b/include/hw/intc/bcm2836_control.h new file mode 100644 index 0000000000..613f3c4186 --- /dev/null +++ b/include/hw/intc/bcm2836_control.h @@ -0,0 +1,51 @@ +/* + * Raspberry Pi emulation (c) 2012 Gregory Estrade + * Upstreaming code cleanup [including bcm2835_*] (c) 2013 Jan Petrous + * + * Rasperry Pi 2 emulation and refactoring Copyright (c) 2015, Microsoft + * Written by Andrew Baumann + * + * This code is licensed under the GNU GPLv2 and later. + */ + +#ifndef BCM2836_CONTROL_H +#define BCM2836_CONTROL_H + +#include "hw/sysbus.h" + +/* 4 mailboxes per core, for 16 total */ +#define BCM2836_NCORES 4 +#define BCM2836_MBPERCORE 4 + +#define TYPE_BCM2836_CONTROL "bcm2836-control" +#define BCM2836_CONTROL(obj) \ + OBJECT_CHECK(BCM2836ControlState, (obj), TYPE_BCM2836_CONTROL) + +typedef struct BCM2836ControlState { + /*< private >*/ + SysBusDevice busdev; + /*< public >*/ + MemoryRegion iomem; + + /* mailbox state */ + uint32_t mailboxes[BCM2836_NCORES * BCM2836_MBPERCORE]; + + /* interrupt routing/control registers */ + uint8_t route_gpu_irq, route_gpu_fiq; + uint32_t timercontrol[BCM2836_NCORES]; + uint32_t mailboxcontrol[BCM2836_NCORES]; + + /* interrupt status regs (derived from input pins; not visible to user) */ + bool gpu_irq, gpu_fiq; + uint8_t timerirqs[BCM2836_NCORES]; + + /* interrupt source registers, post-routing (also input-derived; visible) */ + uint32_t irqsrc[BCM2836_NCORES]; + uint32_t fiqsrc[BCM2836_NCORES]; + + /* outputs to CPU cores */ + qemu_irq irq[BCM2836_NCORES]; + qemu_irq fiq[BCM2836_NCORES]; +} BCM2836ControlState; + +#endif diff --git a/include/hw/misc/bcm2835_mbox.h b/include/hw/misc/bcm2835_mbox.h new file mode 100644 index 0000000000..f4e9ff9ef6 --- /dev/null +++ b/include/hw/misc/bcm2835_mbox.h @@ -0,0 +1,38 @@ +/* + * Raspberry Pi emulation (c) 2012 Gregory Estrade + * This code is licensed under the GNU GPLv2 and later. + */ + +#ifndef BCM2835_MBOX_H +#define BCM2835_MBOX_H + +#include "bcm2835_mbox_defs.h" +#include "hw/sysbus.h" +#include "exec/address-spaces.h" + +#define TYPE_BCM2835_MBOX "bcm2835-mbox" +#define BCM2835_MBOX(obj) \ + OBJECT_CHECK(BCM2835MboxState, (obj), TYPE_BCM2835_MBOX) + +typedef struct { + uint32_t reg[MBOX_SIZE]; + uint32_t count; + uint32_t status; + uint32_t config; +} BCM2835Mbox; + +typedef struct { + /*< private >*/ + SysBusDevice busdev; + /*< public >*/ + MemoryRegion *mbox_mr; + AddressSpace mbox_as; + MemoryRegion iomem; + qemu_irq arm_irq; + + bool mbox_irq_disabled; + bool available[MBOX_CHAN_COUNT]; + BCM2835Mbox mbox[2]; +} BCM2835MboxState; + +#endif diff --git a/include/hw/misc/bcm2835_mbox_defs.h b/include/hw/misc/bcm2835_mbox_defs.h new file mode 100644 index 0000000000..a18e520b22 --- /dev/null +++ b/include/hw/misc/bcm2835_mbox_defs.h @@ -0,0 +1,27 @@ +/* + * Raspberry Pi emulation (c) 2012 Gregory Estrade + * This code is licensed under the GNU GPLv2 and later. + */ + +#ifndef BCM2835_MBOX_DEFS_H +#define BCM2835_MBOX_DEFS_H + +/* Constants shared with the ARM identifying separate mailbox channels */ +#define MBOX_CHAN_POWER 0 /* for use by the power management interface */ +#define MBOX_CHAN_FB 1 /* for use by the frame buffer */ +#define MBOX_CHAN_VCHIQ 3 /* for use by the VCHIQ interface */ +#define MBOX_CHAN_PROPERTY 8 /* for use by the property channel */ +#define MBOX_CHAN_COUNT 9 + +#define MBOX_SIZE 32 +#define MBOX_INVALID_DATA 0x0f + +/* Layout of the private address space used for communication between + * the mbox device emulation, and child devices: each channel occupies + * 16 bytes of address space, but only two registers are presently defined. + */ +#define MBOX_AS_CHAN_SHIFT 4 +#define MBOX_AS_DATA 0 /* request / response data (RW at offset 0) */ +#define MBOX_AS_PENDING 4 /* pending response status (RO at offset 4) */ + +#endif /* BCM2835_MBOX_DEFS_H */ diff --git a/include/hw/misc/bcm2835_property.h b/include/hw/misc/bcm2835_property.h new file mode 100644 index 0000000000..fcf5f3deca --- /dev/null +++ b/include/hw/misc/bcm2835_property.h @@ -0,0 +1,31 @@ +/* + * Raspberry Pi emulation (c) 2012 Gregory Estrade + * This code is licensed under the GNU GPLv2 and later. + */ + +#ifndef BCM2835_PROPERTY_H +#define BCM2835_PROPERTY_H + +#include "hw/sysbus.h" +#include "exec/address-spaces.h" +#include "net/net.h" + +#define TYPE_BCM2835_PROPERTY "bcm2835-property" +#define BCM2835_PROPERTY(obj) \ + OBJECT_CHECK(BCM2835PropertyState, (obj), TYPE_BCM2835_PROPERTY) + +typedef struct { + /*< private >*/ + SysBusDevice busdev; + /*< public >*/ + MemoryRegion *dma_mr; + AddressSpace dma_as; + MemoryRegion iomem; + qemu_irq mbox_irq; + MACAddr macaddr; + uint32_t ram_size; + uint32_t addr; + bool pending; +} BCM2835PropertyState; + +#endif diff --git a/include/qemu/log.h b/include/qemu/log.h index d837d90e77..30817f7b42 100644 --- a/include/qemu/log.h +++ b/include/qemu/log.h @@ -5,10 +5,6 @@ #include <stdbool.h> #include <stdio.h> #include "qemu/compiler.h" -#include "qom/cpu.h" -#ifdef NEED_CPU_H -#include "disas/disas.h" -#endif /* Private global variables, don't use */ extern FILE *qemu_logfile; @@ -49,6 +45,7 @@ static inline bool qemu_log_separate(void) #define CPU_LOG_MMU (1 << 12) #define CPU_LOG_TB_NOCHAIN (1 << 13) #define CPU_LOG_PAGE (1 << 14) +#define LOG_TRACE (1 << 15) /* Returns true if a bit is set in the current loglevel mask */ @@ -78,61 +75,6 @@ qemu_log_vprintf(const char *fmt, va_list va) void GCC_FMT_ATTR(2, 3) qemu_log_mask(int mask, const char *fmt, ...); -/* Special cases: */ - -/* cpu_dump_state() logging functions: */ -/** - * log_cpu_state: - * @cpu: The CPU whose state is to be logged. - * @flags: Flags what to log. - * - * Logs the output of cpu_dump_state(). - */ -static inline void log_cpu_state(CPUState *cpu, int flags) -{ - if (qemu_log_enabled()) { - cpu_dump_state(cpu, qemu_logfile, fprintf, flags); - } -} - -/** - * log_cpu_state_mask: - * @mask: Mask when to log. - * @cpu: The CPU whose state is to be logged. - * @flags: Flags what to log. - * - * Logs the output of cpu_dump_state() if loglevel includes @mask. - */ -static inline void log_cpu_state_mask(int mask, CPUState *cpu, int flags) -{ - if (qemu_loglevel & mask) { - log_cpu_state(cpu, flags); - } -} - -#ifdef NEED_CPU_H -/* disas() and target_disas() to qemu_logfile: */ -static inline void log_target_disas(CPUState *cpu, target_ulong start, - target_ulong len, int flags) -{ - target_disas(qemu_logfile, cpu, start, len, flags); -} - -static inline void log_disas(void *code, unsigned long size) -{ - disas(qemu_logfile, code, size); -} - -#if defined(CONFIG_USER_ONLY) -/* page_dump() output to the log file: */ -static inline void log_page_dump(void) -{ - page_dump(qemu_logfile); -} -#endif -#endif - - /* Maintenance: */ /* fflush() the log file */ diff --git a/linux-user/main.c b/linux-user/main.c index f8d45778c8..e719a2da02 100644 --- a/linux-user/main.c +++ b/linux-user/main.c @@ -28,6 +28,7 @@ #include "qemu/timer.h" #include "qemu/envlist.h" #include "elf.h" +#include "exec/log.h" char *exec_path; diff --git a/qemu-io.c b/qemu-io.c index d593f19642..83c48f4149 100644 --- a/qemu-io.c +++ b/qemu-io.c @@ -435,7 +435,7 @@ int main(int argc, char **argv) } break; case 'T': - if (!trace_init_backends(optarg, NULL)) { + if (!trace_init_backends()) { exit(1); /* error message will have been printed */ } break; diff --git a/qemu-options.hx b/qemu-options.hx index f31a240bed..733a1949e9 100644 --- a/qemu-options.hx +++ b/qemu-options.hx @@ -3544,7 +3544,7 @@ config files on @var{sysconfdir}, but won't make it skip the QEMU-provided confi files from @var{datadir}. ETEXI DEF("trace", HAS_ARG, QEMU_OPTION_trace, - "-trace [events=<file>][,file=<file>]\n" + "-trace [[enable=]<pattern>][,events=<file>][,file=<file>]\n" " specify tracing options\n", QEMU_ARCH_ALL) STEXI @@ -3556,15 +3556,25 @@ HXCOMM HX does not support conditional compilation of text. Specify tracing options. @table @option +@item [enable=]@var{pattern} +Immediately enable events matching @var{pattern}. +The file must contain one event name (as listed in the @file{trace-events} file) +per line; globbing patterns are accepted too. This option is only +available if QEMU has been compiled with the @var{simple}, @var{stderr} +or @var{ftrace} tracing backend. To specify multiple events or patterns, +specify the @option{-trace} option multiple times. + +Use @code{-trace help} to print a list of names of trace points. + @item events=@var{file} Immediately enable events listed in @var{file}. -The file must contain one event name (as listed in the @var{trace-events} file) -per line. -This option is only available if QEMU has been compiled with -either @var{simple} or @var{stderr} tracing backend. +The file must contain one event name (as listed in the @file{trace-events} file) +per line; globbing patterns are accepted too. This option is only +available if QEMU has been compiled with the @var{simple}, @var{stderr} or +@var{ftrace} tracing backend. + @item file=@var{file} Log output traces to @var{file}. - This option is only available if QEMU has been compiled with the @var{simple} tracing backend. @end table diff --git a/qom/cpu.c b/qom/cpu.c index 8f537a470f..38dc713ce2 100644 --- a/qom/cpu.c +++ b/qom/cpu.c @@ -23,6 +23,7 @@ #include "sysemu/kvm.h" #include "qemu/notify.h" #include "qemu/log.h" +#include "exec/log.h" #include "qemu/error-report.h" #include "sysemu/sysemu.h" diff --git a/scripts/tracetool/backend/stderr.py b/scripts/tracetool/backend/log.py index ca58054621..a62c310705 100644 --- a/scripts/tracetool/backend/stderr.py +++ b/scripts/tracetool/backend/log.py @@ -25,6 +25,7 @@ def generate_h_begin(events): '#include <sys/types.h>', '#include <unistd.h>', '#include "trace/control.h"', + '#include "qemu/log.h"', '') @@ -36,10 +37,10 @@ def generate_h(event): out(' if (trace_event_get_state(%(event_id)s)) {', ' struct timeval _now;', ' gettimeofday(&_now, NULL);', - ' fprintf(stderr, "%%d@%%zd.%%06zd:%(name)s " %(fmt)s "\\n",', - ' getpid(),', - ' (size_t)_now.tv_sec, (size_t)_now.tv_usec', - ' %(argnames)s);', + ' qemu_log_mask(LOG_TRACE, "%%d@%%zd.%%06zd:%(name)s " %(fmt)s "\\n",', + ' getpid(),', + ' (size_t)_now.tv_sec, (size_t)_now.tv_usec', + ' %(argnames)s);', ' }', event_id="TRACE_" + event.name.upper(), name=event.name, diff --git a/scripts/tracetool/format/events_c.py b/scripts/tracetool/format/events_c.py index 2d97fa310a..2717ea3a0b 100644 --- a/scripts/tracetool/format/events_c.py +++ b/scripts/tracetool/format/events_c.py @@ -27,7 +27,7 @@ def generate(events, backend): out('TraceEvent trace_events[TRACE_EVENT_COUNT] = {') for e in events: - out(' { .id = %(id)s, .name = \"%(name)s\", .sstate = %(sstate)s, .dstate = 0 },', + out(' { .id = %(id)s, .name = \"%(name)s\", .sstate = %(sstate)s },', id = "TRACE_" + e.name.upper(), name = e.name, sstate = "TRACE_%s_ENABLED" % e.name.upper()) diff --git a/target-alpha/translate.c b/target-alpha/translate.c index 7d419d9972..c96adbb6c7 100644 --- a/target-alpha/translate.c +++ b/target-alpha/translate.c @@ -28,6 +28,7 @@ #include "exec/helper-gen.h" #include "trace-tcg.h" +#include "exec/log.h" #undef ALPHA_DEBUG_DISAS diff --git a/target-arm/cpu.c b/target-arm/cpu.c index 0e582c4410..7ddbf3d19b 100644 --- a/target-arm/cpu.c +++ b/target-arm/cpu.c @@ -650,6 +650,15 @@ static void arm_cpu_realizefn(DeviceState *dev, Error **errp) cpu->id_aa64pfr0 &= ~0xf000; } + if (!arm_feature(env, ARM_FEATURE_EL2)) { + /* Disable the hypervisor feature bits in the processor feature + * registers if we don't have EL2. These are id_pfr1[15:12] and + * id_aa64pfr0_el1[11:8]. + */ + cpu->id_aa64pfr0 &= ~0xf00; + cpu->id_pfr1 &= ~0xf000; + } + if (!cpu->has_mpu) { unset_feature(env, ARM_FEATURE_MPU); } diff --git a/target-arm/helper.c b/target-arm/helper.c index ae024869d0..5ea507f5bc 100644 --- a/target-arm/helper.c +++ b/target-arm/helper.c @@ -3167,6 +3167,35 @@ static const ARMCPRegInfo v8_cp_reginfo[] = { .type = ARM_CP_ALIAS, .fieldoffset = offsetof(CPUARMState, vfp.xregs[ARM_VFP_FPEXC]), .access = PL2_RW, .accessfn = fpexc32_access }, + { .name = "DACR32_EL2", .state = ARM_CP_STATE_AA64, + .opc0 = 3, .opc1 = 4, .crn = 3, .crm = 0, .opc2 = 0, + .access = PL2_RW, .resetvalue = 0, + .writefn = dacr_write, .raw_writefn = raw_write, + .fieldoffset = offsetof(CPUARMState, cp15.dacr32_el2) }, + { .name = "IFSR32_EL2", .state = ARM_CP_STATE_AA64, + .opc0 = 3, .opc1 = 4, .crn = 5, .crm = 0, .opc2 = 1, + .access = PL2_RW, .resetvalue = 0, + .fieldoffset = offsetof(CPUARMState, cp15.ifsr32_el2) }, + { .name = "SPSR_IRQ", .state = ARM_CP_STATE_AA64, + .type = ARM_CP_ALIAS, + .opc0 = 3, .opc1 = 4, .crn = 4, .crm = 3, .opc2 = 0, + .access = PL2_RW, + .fieldoffset = offsetof(CPUARMState, banked_spsr[BANK_IRQ]) }, + { .name = "SPSR_ABT", .state = ARM_CP_STATE_AA64, + .type = ARM_CP_ALIAS, + .opc0 = 3, .opc1 = 4, .crn = 4, .crm = 3, .opc2 = 1, + .access = PL2_RW, + .fieldoffset = offsetof(CPUARMState, banked_spsr[BANK_ABT]) }, + { .name = "SPSR_UND", .state = ARM_CP_STATE_AA64, + .type = ARM_CP_ALIAS, + .opc0 = 3, .opc1 = 4, .crn = 4, .crm = 3, .opc2 = 2, + .access = PL2_RW, + .fieldoffset = offsetof(CPUARMState, banked_spsr[BANK_UND]) }, + { .name = "SPSR_FIQ", .state = ARM_CP_STATE_AA64, + .type = ARM_CP_ALIAS, + .opc0 = 3, .opc1 = 4, .crn = 4, .crm = 3, .opc2 = 3, + .access = PL2_RW, + .fieldoffset = offsetof(CPUARMState, banked_spsr[BANK_FIQ]) }, REGINFO_SENTINEL }; @@ -3294,11 +3323,6 @@ static const ARMCPRegInfo el2_cp_reginfo[] = { .opc0 = 3, .opc1 = 4, .crn = 1, .crm = 1, .opc2 = 0, .access = PL2_RW, .fieldoffset = offsetof(CPUARMState, cp15.hcr_el2), .writefn = hcr_write }, - { .name = "DACR32_EL2", .state = ARM_CP_STATE_AA64, - .opc0 = 3, .opc1 = 4, .crn = 3, .crm = 0, .opc2 = 0, - .access = PL2_RW, .resetvalue = 0, - .writefn = dacr_write, .raw_writefn = raw_write, - .fieldoffset = offsetof(CPUARMState, cp15.dacr32_el2) }, { .name = "ELR_EL2", .state = ARM_CP_STATE_AA64, .type = ARM_CP_ALIAS, .opc0 = 3, .opc1 = 4, .crn = 4, .crm = 0, .opc2 = 1, @@ -3308,10 +3332,6 @@ static const ARMCPRegInfo el2_cp_reginfo[] = { .type = ARM_CP_ALIAS, .opc0 = 3, .opc1 = 4, .crn = 5, .crm = 2, .opc2 = 0, .access = PL2_RW, .fieldoffset = offsetof(CPUARMState, cp15.esr_el[2]) }, - { .name = "IFSR32_EL2", .state = ARM_CP_STATE_AA64, - .opc0 = 3, .opc1 = 4, .crn = 5, .crm = 0, .opc2 = 1, - .access = PL2_RW, .resetvalue = 0, - .fieldoffset = offsetof(CPUARMState, cp15.ifsr32_el2) }, { .name = "FAR_EL2", .state = ARM_CP_STATE_AA64, .opc0 = 3, .opc1 = 4, .crn = 6, .crm = 0, .opc2 = 0, .access = PL2_RW, .fieldoffset = offsetof(CPUARMState, cp15.far_el[2]) }, @@ -3320,26 +3340,6 @@ static const ARMCPRegInfo el2_cp_reginfo[] = { .opc0 = 3, .opc1 = 4, .crn = 4, .crm = 0, .opc2 = 0, .access = PL2_RW, .fieldoffset = offsetof(CPUARMState, banked_spsr[BANK_HYP]) }, - { .name = "SPSR_IRQ", .state = ARM_CP_STATE_AA64, - .type = ARM_CP_ALIAS, - .opc0 = 3, .opc1 = 4, .crn = 4, .crm = 3, .opc2 = 0, - .access = PL2_RW, - .fieldoffset = offsetof(CPUARMState, banked_spsr[BANK_IRQ]) }, - { .name = "SPSR_ABT", .state = ARM_CP_STATE_AA64, - .type = ARM_CP_ALIAS, - .opc0 = 3, .opc1 = 4, .crn = 4, .crm = 3, .opc2 = 1, - .access = PL2_RW, - .fieldoffset = offsetof(CPUARMState, banked_spsr[BANK_ABT]) }, - { .name = "SPSR_UND", .state = ARM_CP_STATE_AA64, - .type = ARM_CP_ALIAS, - .opc0 = 3, .opc1 = 4, .crn = 4, .crm = 3, .opc2 = 2, - .access = PL2_RW, - .fieldoffset = offsetof(CPUARMState, banked_spsr[BANK_UND]) }, - { .name = "SPSR_FIQ", .state = ARM_CP_STATE_AA64, - .type = ARM_CP_ALIAS, - .opc0 = 3, .opc1 = 4, .crn = 4, .crm = 3, .opc2 = 3, - .access = PL2_RW, - .fieldoffset = offsetof(CPUARMState, banked_spsr[BANK_FIQ]) }, { .name = "VBAR_EL2", .state = ARM_CP_STATE_AA64, .opc0 = 3, .opc1 = 4, .crn = 12, .crm = 0, .opc2 = 0, .access = PL2_RW, .writefn = vbar_write, @@ -6763,24 +6763,34 @@ typedef enum { } MMUFaultType; /* - * check_s2_startlevel + * check_s2_mmu_setup * @cpu: ARMCPU * @is_aa64: True if the translation regime is in AArch64 state * @startlevel: Suggested starting level * @inputsize: Bitsize of IPAs * @stride: Page-table stride (See the ARM ARM) * - * Returns true if the suggested starting level is OK and false otherwise. + * Returns true if the suggested S2 translation parameters are OK and + * false otherwise. */ -static bool check_s2_startlevel(ARMCPU *cpu, bool is_aa64, int level, - int inputsize, int stride) +static bool check_s2_mmu_setup(ARMCPU *cpu, bool is_aa64, int level, + int inputsize, int stride) { + const int grainsize = stride + 3; + int startsizecheck; + /* Negative levels are never allowed. */ if (level < 0) { return false; } + startsizecheck = inputsize - ((3 - level) * stride + grainsize); + if (startsizecheck < 1 || startsizecheck > stride + 4) { + return false; + } + if (is_aa64) { + CPUARMState *env = &cpu->env; unsigned int pamax = arm_pamax(cpu); switch (stride) { @@ -6802,21 +6812,20 @@ static bool check_s2_startlevel(ARMCPU *cpu, bool is_aa64, int level, default: g_assert_not_reached(); } - } else { - const int grainsize = stride + 3; - int startsizecheck; + /* Inputsize checks. */ + if (inputsize > pamax && + (arm_el_is_aa64(env, 1) || inputsize > 40)) { + /* This is CONSTRAINED UNPREDICTABLE and we choose to fault. */ + return false; + } + } else { /* AArch32 only supports 4KB pages. Assert on that. */ assert(stride == 9); if (level == 0) { return false; } - - startsizecheck = inputsize - ((3 - level) * stride + grainsize); - if (startsizecheck < 1 || startsizecheck > stride + 4) { - return false; - } } return true; } @@ -7013,8 +7022,7 @@ static bool get_phys_addr_lpae(CPUARMState *env, target_ulong address, } /* Check that the starting level is valid. */ - ok = check_s2_startlevel(cpu, va_size == 64, level, - inputsize, stride); + ok = check_s2_mmu_setup(cpu, va_size == 64, level, inputsize, stride); if (!ok) { /* AArch64 reports these as level 0 faults. * AArch32 reports these as level 1 faults. diff --git a/target-arm/translate-a64.c b/target-arm/translate-a64.c index 80f6c2058c..2af7d860a5 100644 --- a/target-arm/translate-a64.c +++ b/target-arm/translate-a64.c @@ -31,6 +31,7 @@ #include "exec/helper-proto.h" #include "exec/helper-gen.h" +#include "exec/log.h" #include "trace-tcg.h" diff --git a/target-arm/translate.c b/target-arm/translate.c index cff511b9c6..3ec758ad6f 100644 --- a/target-arm/translate.c +++ b/target-arm/translate.c @@ -32,6 +32,7 @@ #include "exec/helper-gen.h" #include "trace-tcg.h" +#include "exec/log.h" #define ENABLE_ARCH_4T arm_dc_feature(s, ARM_FEATURE_V4T) diff --git a/target-cris/translate.c b/target-cris/translate.c index 295005f141..0350cb59c0 100644 --- a/target-cris/translate.c +++ b/target-cris/translate.c @@ -35,6 +35,7 @@ #include "exec/helper-gen.h" #include "trace-tcg.h" +#include "exec/log.h" #define DISAS_CRIS 0 diff --git a/target-i386/seg_helper.c b/target-i386/seg_helper.c index 4c7cab79ff..4f269416a5 100644 --- a/target-i386/seg_helper.c +++ b/target-i386/seg_helper.c @@ -23,6 +23,7 @@ #include "qemu/log.h" #include "exec/helper-proto.h" #include "exec/cpu_ldst.h" +#include "exec/log.h" //#define DEBUG_PCALL diff --git a/target-i386/smm_helper.c b/target-i386/smm_helper.c index b930421724..e7bb5be521 100644 --- a/target-i386/smm_helper.c +++ b/target-i386/smm_helper.c @@ -20,6 +20,7 @@ #include "qemu/osdep.h" #include "cpu.h" #include "exec/helper-proto.h" +#include "exec/log.h" /* SMM support */ diff --git a/target-i386/translate.c b/target-i386/translate.c index 957a92d591..73a45c872e 100644 --- a/target-i386/translate.c +++ b/target-i386/translate.c @@ -28,6 +28,7 @@ #include "exec/helper-gen.h" #include "trace-tcg.h" +#include "exec/log.h" #define PREFIX_REPZ 0x01 diff --git a/target-lm32/helper.c b/target-lm32/helper.c index 3ebec68cba..655248f81a 100644 --- a/target-lm32/helper.c +++ b/target-lm32/helper.c @@ -22,6 +22,7 @@ #include "qemu/host-utils.h" #include "sysemu/sysemu.h" #include "exec/semihost.h" +#include "exec/log.h" int lm32_cpu_handle_mmu_fault(CPUState *cs, vaddr address, int rw, int mmu_idx) diff --git a/target-lm32/translate.c b/target-lm32/translate.c index 477d4285a5..52fe562737 100644 --- a/target-lm32/translate.c +++ b/target-lm32/translate.c @@ -29,6 +29,7 @@ #include "exec/helper-gen.h" #include "trace-tcg.h" +#include "exec/log.h" #define DISAS_LM32 1 diff --git a/target-m68k/translate.c b/target-m68k/translate.c index 342c040559..a402bd847a 100644 --- a/target-m68k/translate.c +++ b/target-m68k/translate.c @@ -29,6 +29,7 @@ #include "exec/helper-gen.h" #include "trace-tcg.h" +#include "exec/log.h" //#define DEBUG_DISPATCH 1 diff --git a/target-microblaze/helper.c b/target-microblaze/helper.c index 3b0fae8072..4de6bdbf83 100644 --- a/target-microblaze/helper.c +++ b/target-microblaze/helper.c @@ -21,6 +21,7 @@ #include "qemu/osdep.h" #include "cpu.h" #include "qemu/host-utils.h" +#include "exec/log.h" #define D(x) diff --git a/target-microblaze/translate.c b/target-microblaze/translate.c index 2e1293d953..40be4ec805 100644 --- a/target-microblaze/translate.c +++ b/target-microblaze/translate.c @@ -28,6 +28,7 @@ #include "exec/helper-gen.h" #include "trace-tcg.h" +#include "exec/log.h" #define SIM_COMPAT 0 diff --git a/target-mips/helper.c b/target-mips/helper.c index f9c4c11eb9..1004edee05 100644 --- a/target-mips/helper.c +++ b/target-mips/helper.c @@ -21,6 +21,7 @@ #include "cpu.h" #include "sysemu/kvm.h" #include "exec/cpu_ldst.h" +#include "exec/log.h" enum { TLBRET_XI = -6, diff --git a/target-mips/translate.c b/target-mips/translate.c index 383d4b5118..791866bf2d 100644 --- a/target-mips/translate.c +++ b/target-mips/translate.c @@ -33,6 +33,7 @@ #include "exec/semihost.h" #include "trace-tcg.h" +#include "exec/log.h" #define MIPS_DEBUG_DISAS 0 diff --git a/target-moxie/translate.c b/target-moxie/translate.c index 229ce3b64d..04ab278ea2 100644 --- a/target-moxie/translate.c +++ b/target-moxie/translate.c @@ -31,6 +31,7 @@ #include "exec/helper-proto.h" #include "exec/helper-gen.h" +#include "exec/log.h" /* This is the state at translation time. */ typedef struct DisasContext { diff --git a/target-openrisc/translate.c b/target-openrisc/translate.c index 62890c21ed..b766b27405 100644 --- a/target-openrisc/translate.c +++ b/target-openrisc/translate.c @@ -32,6 +32,7 @@ #include "exec/helper-gen.h" #include "trace-tcg.h" +#include "exec/log.h" #define OPENRISC_DISAS diff --git a/target-ppc/mmu-hash32.c b/target-ppc/mmu-hash32.c index b076d8006f..39abb2fd39 100644 --- a/target-ppc/mmu-hash32.c +++ b/target-ppc/mmu-hash32.c @@ -24,6 +24,7 @@ #include "sysemu/kvm.h" #include "kvm_ppc.h" #include "mmu-hash32.h" +#include "exec/log.h" //#define DEBUG_BAT diff --git a/target-ppc/mmu-hash64.c b/target-ppc/mmu-hash64.c index 6d110ee342..9c58fbf009 100644 --- a/target-ppc/mmu-hash64.c +++ b/target-ppc/mmu-hash64.c @@ -25,6 +25,7 @@ #include "qemu/error-report.h" #include "kvm_ppc.h" #include "mmu-hash64.h" +#include "exec/log.h" //#define DEBUG_SLB diff --git a/target-ppc/mmu_helper.c b/target-ppc/mmu_helper.c index de4e286eee..e5ec8d6169 100644 --- a/target-ppc/mmu_helper.c +++ b/target-ppc/mmu_helper.c @@ -24,6 +24,7 @@ #include "mmu-hash64.h" #include "mmu-hash32.h" #include "exec/cpu_ldst.h" +#include "exec/log.h" //#define DEBUG_MMU //#define DEBUG_BATS diff --git a/target-ppc/translate.c b/target-ppc/translate.c index 7db3145cff..0057bdaf37 100644 --- a/target-ppc/translate.c +++ b/target-ppc/translate.c @@ -29,6 +29,7 @@ #include "exec/helper-gen.h" #include "trace-tcg.h" +#include "exec/log.h" #define CPU_SINGLE_STEP 0x1 diff --git a/target-s390x/translate.c b/target-s390x/translate.c index 811928b1c3..3087692aa7 100644 --- a/target-s390x/translate.c +++ b/target-s390x/translate.c @@ -44,6 +44,7 @@ static TCGv_ptr cpu_env; #include "exec/helper-gen.h" #include "trace-tcg.h" +#include "exec/log.h" /* Information that (most) every instruction needs to manipulate. */ diff --git a/target-sh4/helper.c b/target-sh4/helper.c index 9d8b61cce6..6438338f24 100644 --- a/target-sh4/helper.c +++ b/target-sh4/helper.c @@ -19,6 +19,7 @@ #include "qemu/osdep.h" #include "cpu.h" +#include "exec/log.h" #if !defined(CONFIG_USER_ONLY) #include "hw/sh4/sh_intc.h" diff --git a/target-sh4/translate.c b/target-sh4/translate.c index 9de5659cf7..3e4164b78f 100644 --- a/target-sh4/translate.c +++ b/target-sh4/translate.c @@ -29,6 +29,7 @@ #include "exec/helper-gen.h" #include "trace-tcg.h" +#include "exec/log.h" typedef struct DisasContext { diff --git a/target-sparc/int32_helper.c b/target-sparc/int32_helper.c index d4d6a4b8a5..09afe136e5 100644 --- a/target-sparc/int32_helper.c +++ b/target-sparc/int32_helper.c @@ -21,6 +21,7 @@ #include "cpu.h" #include "trace.h" #include "sysemu/sysemu.h" +#include "exec/log.h" #define DEBUG_PCALL diff --git a/target-sparc/int64_helper.c b/target-sparc/int64_helper.c index ddef66b569..aa876cd8ba 100644 --- a/target-sparc/int64_helper.c +++ b/target-sparc/int64_helper.c @@ -20,6 +20,7 @@ #include "qemu/osdep.h" #include "cpu.h" #include "exec/helper-proto.h" +#include "exec/log.h" #include "trace.h" #define DEBUG_PCALL diff --git a/target-sparc/translate.c b/target-sparc/translate.c index 747f94d60e..67268604ba 100644 --- a/target-sparc/translate.c +++ b/target-sparc/translate.c @@ -29,6 +29,7 @@ #include "exec/helper-gen.h" #include "trace-tcg.h" +#include "exec/log.h" #define DEBUG_DISAS diff --git a/target-tilegx/translate.c b/target-tilegx/translate.c index 2088f8796b..a5bb8d4aba 100644 --- a/target-tilegx/translate.c +++ b/target-tilegx/translate.c @@ -21,6 +21,7 @@ #include "qemu/osdep.h" #include "cpu.h" #include "qemu/log.h" +#include "exec/log.h" #include "disas/disas.h" #include "tcg-op.h" #include "exec/cpu_ldst.h" diff --git a/target-tricore/translate.c b/target-tricore/translate.c index 2b07b86694..e385fc71f7 100644 --- a/target-tricore/translate.c +++ b/target-tricore/translate.c @@ -28,6 +28,7 @@ #include "exec/helper-gen.h" #include "tricore-opcodes.h" +#include "exec/log.h" /* * TCG registers diff --git a/target-unicore32/translate.c b/target-unicore32/translate.c index 5db8f94931..7dbfe3bd03 100644 --- a/target-unicore32/translate.c +++ b/target-unicore32/translate.c @@ -20,6 +20,7 @@ #include "exec/helper-gen.h" #include "trace-tcg.h" +#include "exec/log.h" /* internal defines */ diff --git a/target-xtensa/translate.c b/target-xtensa/translate.c index a8c1113489..435ee035d5 100644 --- a/target-xtensa/translate.c +++ b/target-xtensa/translate.c @@ -43,6 +43,7 @@ #include "exec/helper-gen.h" #include "trace-tcg.h" +#include "exec/log.h" typedef struct DisasContext { diff --git a/tcg/tcg.c b/tcg/tcg.c index 3ce02dcd08..be765ad3a8 100644 --- a/tcg/tcg.c +++ b/tcg/tcg.c @@ -60,6 +60,7 @@ #endif #include "elf.h" +#include "exec/log.h" /* Forward declarations for functions declared in tcg-target.c and used here. */ static void tcg_target_init(TCGContext *s); diff --git a/trace/control-internal.h b/trace/control-internal.h index 5a8df28c58..07cb1c1685 100644 --- a/trace/control-internal.h +++ b/trace/control-internal.h @@ -14,6 +14,8 @@ extern TraceEvent trace_events[]; +extern bool trace_events_dstate[]; +extern int trace_events_enabled_count; static inline TraceEventID trace_event_count(void) @@ -51,17 +53,24 @@ static inline bool trace_event_get_state_static(TraceEvent *ev) return ev->sstate; } +static inline bool trace_event_get_state_dynamic_by_id(int id) +{ + return unlikely(trace_events_enabled_count) && trace_events_dstate[id]; +} + static inline bool trace_event_get_state_dynamic(TraceEvent *ev) { - assert(ev != NULL); - return ev->dstate; + int id = trace_event_get_id(ev); + return trace_event_get_state_dynamic_by_id(id); } static inline void trace_event_set_state_dynamic(TraceEvent *ev, bool state) { + int id = trace_event_get_id(ev); assert(ev != NULL); assert(trace_event_get_state_static(ev)); - ev->dstate = state; + trace_events_enabled_count += state - trace_events_dstate[id]; + trace_events_dstate[id] = state; } #endif /* TRACE__CONTROL_INTERNAL_H */ diff --git a/trace/control.c b/trace/control.c index 995beb384c..84ea840892 100644 --- a/trace/control.c +++ b/trace/control.c @@ -14,8 +14,14 @@ #ifdef CONFIG_TRACE_FTRACE #include "trace/ftrace.h" #endif +#ifdef CONFIG_TRACE_LOG +#include "qemu/log.h" +#endif #include "qemu/error-report.h" +int trace_events_enabled_count; +bool trace_events_dstate[TRACE_EVENT_COUNT]; + TraceEvent *trace_event_name(const char *name) { assert(name != NULL); @@ -85,7 +91,52 @@ TraceEvent *trace_event_pattern(const char *pat, TraceEvent *ev) return NULL; } -static void trace_init_events(const char *fname) +void trace_list_events(void) +{ + int i; + for (i = 0; i < trace_event_count(); i++) { + TraceEvent *res = trace_event_id(i); + fprintf(stderr, "%s\n", trace_event_get_name(res)); + } +} + +static void do_trace_enable_events(const char *line_buf) +{ + const bool enable = ('-' != line_buf[0]); + const char *line_ptr = enable ? line_buf : line_buf + 1; + + if (trace_event_is_pattern(line_ptr)) { + TraceEvent *ev = NULL; + while ((ev = trace_event_pattern(line_ptr, ev)) != NULL) { + if (trace_event_get_state_static(ev)) { + trace_event_set_state_dynamic(ev, enable); + } + } + } else { + TraceEvent *ev = trace_event_name(line_ptr); + if (ev == NULL) { + error_report("WARNING: trace event '%s' does not exist", + line_ptr); + } else if (!trace_event_get_state_static(ev)) { + error_report("WARNING: trace event '%s' is not traceable", + line_ptr); + } else { + trace_event_set_state_dynamic(ev, enable); + } + } +} + +void trace_enable_events(const char *line_buf) +{ + if (is_help_option(line_buf)) { + trace_list_events(); + exit(0); + } else { + do_trace_enable_events(line_buf); + } +} + +void trace_init_events(const char *fname) { Location loc; FILE *fp; @@ -111,27 +162,7 @@ static void trace_init_events(const char *fname) if ('#' == line_buf[0]) { /* skip commented lines */ continue; } - const bool enable = ('-' != line_buf[0]); - char *line_ptr = enable ? line_buf : line_buf + 1; - if (trace_event_is_pattern(line_ptr)) { - TraceEvent *ev = NULL; - while ((ev = trace_event_pattern(line_ptr, ev)) != NULL) { - if (trace_event_get_state_static(ev)) { - trace_event_set_state_dynamic(ev, enable); - } - } - } else { - TraceEvent *ev = trace_event_name(line_ptr); - if (ev == NULL) { - error_report("WARNING: trace event '%s' does not exist", - line_ptr); - } else if (!trace_event_get_state_static(ev)) { - error_report("WARNING: trace event '%s' is not traceable", - line_ptr); - } else { - trace_event_set_state_dynamic(ev, enable); - } - } + trace_enable_events(line_buf); } } if (fclose(fp) != 0) { @@ -142,17 +173,31 @@ static void trace_init_events(const char *fname) loc_pop(&loc); } -bool trace_init_backends(const char *events, const char *file) +void trace_init_file(const char *file) { #ifdef CONFIG_TRACE_SIMPLE - if (!st_init(file)) { - fprintf(stderr, "failed to initialize simple tracing backend.\n"); - return false; + st_set_trace_file(file); +#elif defined CONFIG_TRACE_LOG + /* If both the simple and the log backends are enabled, "-trace file" + * only applies to the simple backend; use "-D" for the log backend. + */ + if (file) { + qemu_set_log_filename(file); } #else if (file) { fprintf(stderr, "error: -trace file=...: " "option not supported by the selected tracing backends\n"); + exit(1); + } +#endif +} + +bool trace_init_backends(void) +{ +#ifdef CONFIG_TRACE_SIMPLE + if (!st_init()) { + fprintf(stderr, "failed to initialize simple tracing backend.\n"); return false; } #endif @@ -164,6 +209,5 @@ bool trace_init_backends(const char *events, const char *file) } #endif - trace_init_events(events); return true; } diff --git a/trace/control.h b/trace/control.h index da9bb6b774..d5bc86e5f1 100644 --- a/trace/control.h +++ b/trace/control.h @@ -104,7 +104,7 @@ static const char * trace_event_get_name(TraceEvent *ev); * As a down side, you must always use an immediate #TraceEventID value. */ #define trace_event_get_state(id) \ - ((id ##_ENABLED) && trace_event_get_state_dynamic(trace_event_id(id))) + ((id ##_ENABLED) && trace_event_get_state_dynamic_by_id(id)) /** * trace_event_get_state_static: @@ -150,8 +150,6 @@ static void trace_event_set_state_dynamic(TraceEvent *ev, bool state); /** * trace_init_backends: - * @events: Name of file with events to be enabled at startup; may be NULL. - * Corresponds to commandline option "-trace events=...". * @file: Name of trace output file; may be NULL. * Corresponds to commandline option "-trace file=...". * @@ -159,7 +157,45 @@ static void trace_event_set_state_dynamic(TraceEvent *ev, bool state); * * Returns: Whether the backends could be successfully initialized. */ -bool trace_init_backends(const char *events, const char *file); +bool trace_init_backends(void); + +/** + * trace_init_events: + * @events: Name of file with events to be enabled at startup; may be NULL. + * Corresponds to commandline option "-trace events=...". + * + * Read the list of enabled tracing events. + * + * Returns: Whether the backends could be successfully initialized. + */ +void trace_init_events(const char *file); + +/** + * trace_init_file: + * @file: Name of trace output file; may be NULL. + * Corresponds to commandline option "-trace file=...". + * + * Record the name of the output file for the tracing backend. + * Exits if no selected backend does not support specifying the + * output file, and a non-NULL file was passed. + */ +void trace_init_file(const char *file); + +/** + * trace_list_events: + * + * List all available events. + */ +void trace_list_events(void); + +/** + * trace_enable_events: + * @line_buf: A string with a glob pattern of events to be enabled or, + * if the string starts with '-', disabled. + * + * Enable or disable matching events. + */ +void trace_enable_events(const char *line_buf); #include "trace/control-internal.h" diff --git a/trace/event-internal.h b/trace/event-internal.h index b2310d9bea..86f6a511be 100644 --- a/trace/event-internal.h +++ b/trace/event-internal.h @@ -18,7 +18,6 @@ * @id: Unique event identifier. * @name: Event name. * @sstate: Static tracing state. - * @dstate: Dynamic tracing state. * * Opaque generic description of a tracing event. */ @@ -26,7 +25,6 @@ typedef struct TraceEvent { TraceEventID id; const char * name; const bool sstate; - bool dstate; } TraceEvent; diff --git a/trace/simple.c b/trace/simple.c index 56a624cac8..e8594cd00d 100644 --- a/trace/simple.c +++ b/trace/simple.c @@ -322,7 +322,7 @@ void st_set_trace_file_enabled(bool enable) * @file The trace file name or NULL for the default name-<pid> set at * config time */ -bool st_set_trace_file(const char *file) +void st_set_trace_file(const char *file) { st_set_trace_file_enabled(false); @@ -336,7 +336,6 @@ bool st_set_trace_file(const char *file) } st_set_trace_file_enabled(true); - return true; } void st_print_trace_file_status(FILE *stream, int (*stream_printf)(FILE *stream, const char *fmt, ...)) @@ -374,7 +373,7 @@ static GThread *trace_thread_create(GThreadFunc fn) return thread; } -bool st_init(const char *file) +bool st_init(void) { GThread *thread; @@ -387,6 +386,5 @@ bool st_init(const char *file) } atexit(st_flush_trace_buffer); - st_set_trace_file(file); return true; } diff --git a/trace/simple.h b/trace/simple.h index 6997996855..8d1a32eba0 100644 --- a/trace/simple.h +++ b/trace/simple.h @@ -20,8 +20,8 @@ void st_print_trace_file_status(FILE *stream, fprintf_function stream_printf); void st_set_trace_file_enabled(bool enable); -bool st_set_trace_file(const char *file); -bool st_init(const char *file); +void st_set_trace_file(const char *file); +bool st_init(void); void st_flush_trace_buffer(void); typedef struct { diff --git a/translate-all.c b/translate-all.c index ab61fac66c..e9f409b762 100644 --- a/translate-all.c +++ b/translate-all.c @@ -55,6 +55,7 @@ #include "translate-all.h" #include "qemu/bitmap.h" #include "qemu/timer.h" +#include "exec/log.h" //#define DEBUG_TB_INVALIDATE //#define DEBUG_FLUSH diff --git a/util/Makefile.objs b/util/Makefile.objs index 8620a80b45..a8a777ec40 100644 --- a/util/Makefile.objs +++ b/util/Makefile.objs @@ -31,3 +31,4 @@ util-obj-y += coroutine-$(CONFIG_COROUTINE_BACKEND).o util-obj-y += buffer.o util-obj-y += timed-average.o util-obj-y += base64.o +util-obj-y += log.o diff --git a/qemu-log.c b/util/log.c index 901b93087d..46c88c93e2 100644 --- a/qemu-log.c +++ b/util/log.c @@ -19,6 +19,7 @@ #include "qemu-common.h" #include "qemu/log.h" +#include "trace/control.h" static char *logfilename; FILE *qemu_logfile; @@ -51,6 +52,9 @@ void qemu_log_mask(int mask, const char *fmt, ...) void do_qemu_set_log(int log_flags, bool use_own_buffers) { qemu_loglevel = log_flags; +#ifdef CONFIG_TRACE_LOG + qemu_loglevel |= LOG_TRACE; +#endif if (qemu_loglevel && !qemu_logfile) { if (logfilename) { qemu_logfile = fopen(logfilename, log_append ? "a" : "w"); @@ -151,6 +155,11 @@ int qemu_str_to_log_mask(const char *str) for (item = qemu_log_items; item->mask != 0; item++) { mask |= item->mask; } +#ifdef CONFIG_TRACE_LOG + } else if (strncmp(p, "trace:", 6) == 0 && p + 6 != p1) { + trace_enable_events(p + 6); + mask |= LOG_TRACE; +#endif } else { for (item = qemu_log_items; item->mask != 0; item++) { if (cmp1(p, p1 - p, item->name)) { @@ -158,9 +167,9 @@ int qemu_str_to_log_mask(const char *str) } } return 0; + found: + mask |= item->mask; } - found: - mask |= item->mask; if (*p1 != ',') { break; } @@ -174,6 +183,10 @@ void qemu_print_log_usage(FILE *f) const QEMULogItem *item; fprintf(f, "Log items (comma separated):\n"); for (item = qemu_log_items; item->mask != 0; item++) { - fprintf(f, "%-10s %s\n", item->name, item->help); + fprintf(f, "%-15s %s\n", item->name, item->help); } +#ifdef CONFIG_TRACE_LOG + fprintf(f, "trace:PATTERN enable trace events\n"); + fprintf(f, "\nUse \"-d trace:help\" to get a list of trace events.\n\n"); +#endif } diff --git a/vl.c b/vl.c index f043009f67..a7d7c39c34 100644 --- a/vl.c +++ b/vl.c @@ -270,10 +270,14 @@ static QemuOptsList qemu_sandbox_opts = { static QemuOptsList qemu_trace_opts = { .name = "trace", - .implied_opt_name = "trace", + .implied_opt_name = "enable", .head = QTAILQ_HEAD_INITIALIZER(qemu_trace_opts.head), .desc = { { + .name = "enable", + .type = QEMU_OPT_STRING, + }, + { .name = "events", .type = QEMU_OPT_STRING, },{ @@ -2988,8 +2992,7 @@ int main(int argc, char **argv, char **envp) bool userconfig = true; const char *log_mask = NULL; const char *log_file = NULL; - const char *trace_events = NULL; - const char *trace_file = NULL; + char *trace_file = NULL; ram_addr_t maxram_size; uint64_t ram_slots = 0; FILE *vmstate_dump_file = NULL; @@ -3901,12 +3904,19 @@ int main(int argc, char **argv, char **envp) case QEMU_OPTION_trace: { opts = qemu_opts_parse_noisily(qemu_find_opts("trace"), - optarg, false); + optarg, true); if (!opts) { exit(1); } - trace_events = qemu_opt_get(opts, "events"); - trace_file = qemu_opt_get(opts, "file"); + if (qemu_opt_get(opts, "enable")) { + trace_enable_events(qemu_opt_get(opts, "enable")); + } + trace_init_events(qemu_opt_get(opts, "events")); + if (trace_file) { + g_free(trace_file); + } + trace_file = g_strdup(qemu_opt_get(opts, "file")); + qemu_opts_del(opts); break; } case QEMU_OPTION_readconfig: @@ -4089,6 +4099,8 @@ int main(int argc, char **argv, char **envp) exit(0); } + trace_init_file(trace_file); + /* Open the logfile at this point and set the log mask if necessary. */ if (log_file) { @@ -4103,12 +4115,12 @@ int main(int argc, char **argv, char **envp) exit(1); } qemu_set_log(mask); + } else { + qemu_set_log(0); } - if (!is_daemonized()) { - if (!trace_init_backends(trace_events, trace_file)) { - exit(1); - } + if (!trace_init_backends()) { + exit(1); } /* If no data_dir is specified then try to find it relative to the @@ -4652,12 +4664,6 @@ int main(int argc, char **argv, char **envp) os_setup_post(); - if (is_daemonized()) { - if (!trace_init_backends(trace_events, trace_file)) { - exit(1); - } - } - main_loop(); replay_disable_events(); |