diff options
123 files changed, 3411 insertions, 856 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; @@ -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); } @@ -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/audio/cs4231a.c b/hw/audio/cs4231a.c index b0c7c93e21..3ecd0582bf 100644 --- a/hw/audio/cs4231a.c +++ b/hw/audio/cs4231a.c @@ -70,6 +70,7 @@ typedef struct CSState { uint32_t irq; uint32_t dma; uint32_t port; + IsaDma *isa_dma; int shift; int dma_running; int audio_free; @@ -265,6 +266,7 @@ static void cs_reset_voices (CSState *s, uint32_t val) { int xtal; struct audsettings as; + IsaDmaClass *k = ISADMA_GET_CLASS(s->isa_dma); #ifdef DEBUG_XLAW if (val == 0 || val == 32) @@ -328,7 +330,7 @@ static void cs_reset_voices (CSState *s, uint32_t val) if (s->dregs[Interface_Configuration] & PEN) { if (!s->dma_running) { - DMA_hold_DREQ (s->dma); + k->hold_DREQ(s->isa_dma, s->dma); AUD_set_active_out (s->voice, 1); s->transferred = 0; } @@ -336,7 +338,7 @@ static void cs_reset_voices (CSState *s, uint32_t val) } else { if (s->dma_running) { - DMA_release_DREQ (s->dma); + k->release_DREQ(s->isa_dma, s->dma); AUD_set_active_out (s->voice, 0); } s->dma_running = 0; @@ -345,7 +347,7 @@ static void cs_reset_voices (CSState *s, uint32_t val) error: if (s->dma_running) { - DMA_release_DREQ (s->dma); + k->release_DREQ(s->isa_dma, s->dma); AUD_set_active_out (s->voice, 0); } } @@ -453,7 +455,8 @@ static void cs_write (void *opaque, hwaddr addr, } else { if (s->dma_running) { - DMA_release_DREQ (s->dma); + IsaDmaClass *k = ISADMA_GET_CLASS(s->isa_dma); + k->release_DREQ(s->isa_dma, s->dma); AUD_set_active_out (s->voice, 0); s->dma_running = 0; } @@ -518,6 +521,7 @@ static int cs_write_audio (CSState *s, int nchan, int dma_pos, { int temp, net; uint8_t tmpbuf[4096]; + IsaDmaClass *k = ISADMA_GET_CLASS(s->isa_dma); temp = len; net = 0; @@ -532,7 +536,7 @@ static int cs_write_audio (CSState *s, int nchan, int dma_pos, to_copy = sizeof (tmpbuf); } - copied = DMA_read_memory (nchan, tmpbuf, dma_pos, to_copy); + copied = k->read_memory(s->isa_dma, nchan, tmpbuf, dma_pos, to_copy); if (s->tab) { int i; int16_t linbuf[4096]; @@ -600,7 +604,8 @@ static int cs4231a_pre_load (void *opaque) CSState *s = opaque; if (s->dma_running) { - DMA_release_DREQ (s->dma); + IsaDmaClass *k = ISADMA_GET_CLASS(s->isa_dma); + k->release_DREQ(s->isa_dma, s->dma); AUD_set_active_out (s->voice, 0); } s->dma_running = 0; @@ -656,13 +661,15 @@ static void cs4231a_realizefn (DeviceState *dev, Error **errp) { ISADevice *d = ISA_DEVICE (dev); CSState *s = CS4231A (dev); + IsaDmaClass *k; isa_init_irq (d, &s->pic, s->irq); + s->isa_dma = isa_get_dma(isa_bus_from_device(d), s->dma); + k = ISADMA_GET_CLASS(s->isa_dma); + k->register_channel(s->isa_dma, s->dma, cs_dma_read, s); isa_register_ioport (d, &s->ioports, s->port); - DMA_register_channel (s->dma, cs_dma_read, s); - AUD_register_card ("cs4231a", &s->card); } diff --git a/hw/audio/gus.c b/hw/audio/gus.c index 47c0fcfb4c..b416a54909 100644 --- a/hw/audio/gus.c +++ b/hw/audio/gus.c @@ -58,6 +58,7 @@ typedef struct GUSState { SWVoiceOut *voice; int64_t last_ticks; qemu_irq pic; + IsaDma *isa_dma; } GUSState; static uint32_t gus_readb(void *opaque, uint32_t nport) @@ -168,34 +169,36 @@ void GUS_irqclear (GUSEmuState *emu, int hwirq) #endif } -void GUS_dmarequest (GUSEmuState *der) +void GUS_dmarequest (GUSEmuState *emu) { - /* GUSState *s = (GUSState *) der; */ + GUSState *s = emu->opaque; + IsaDmaClass *k = ISADMA_GET_CLASS(s->isa_dma); ldebug ("dma request %d\n", der->gusdma); - DMA_hold_DREQ (der->gusdma); + k->hold_DREQ(s->isa_dma, s->emu.gusdma); } static int GUS_read_DMA (void *opaque, int nchan, int dma_pos, int dma_len) { GUSState *s = opaque; + IsaDmaClass *k = ISADMA_GET_CLASS(s->isa_dma); char tmpbuf[4096]; int pos = dma_pos, mode, left = dma_len - dma_pos; ldebug ("read DMA %#x %d\n", dma_pos, dma_len); - mode = DMA_get_channel_mode (s->emu.gusdma); + mode = k->has_autoinitialization(s->isa_dma, s->emu.gusdma); while (left) { int to_copy = audio_MIN ((size_t) left, sizeof (tmpbuf)); int copied; ldebug ("left=%d to_copy=%d pos=%d\n", left, to_copy, pos); - copied = DMA_read_memory (nchan, tmpbuf, pos, to_copy); + copied = k->read_memory(s->isa_dma, nchan, tmpbuf, pos, to_copy); gus_dma_transferdata (&s->emu, tmpbuf, copied, left == copied); left -= copied; pos += copied; } if (((mode >> 4) & 1) == 0) { - DMA_release_DREQ (s->emu.gusdma); + k->release_DREQ(s->isa_dma, s->emu.gusdma); } return dma_len; } @@ -232,6 +235,7 @@ static void gus_realizefn (DeviceState *dev, Error **errp) { ISADevice *d = ISA_DEVICE(dev); GUSState *s = GUS (dev); + IsaDmaClass *k; struct audsettings as; AUD_register_card ("gus", &s->card); @@ -264,7 +268,9 @@ static void gus_realizefn (DeviceState *dev, Error **errp) isa_register_portio_list (d, (s->port + 0x100) & 0xf00, gus_portio_list2, s, "gus"); - DMA_register_channel (s->emu.gusdma, GUS_read_DMA, s); + s->isa_dma = isa_get_dma(isa_bus_from_device(d), s->emu.gusdma); + k = ISADMA_GET_CLASS(s->isa_dma); + k->register_channel(s->isa_dma, s->emu.gusdma, GUS_read_DMA, s); s->emu.himemaddr = s->himem; s->emu.gusdatapos = s->emu.himemaddr + 1024 * 1024 + 32; s->emu.opaque = s; diff --git a/hw/audio/sb16.c b/hw/audio/sb16.c index 3b2dcfdad9..6f8816cf64 100644 --- a/hw/audio/sb16.c +++ b/hw/audio/sb16.c @@ -56,6 +56,8 @@ typedef struct SB16State { uint32_t hdma; uint32_t port; uint32_t ver; + IsaDma *isa_dma; + IsaDma *isa_hdma; int in_index; int out_data_len; @@ -166,16 +168,18 @@ static void speaker (SB16State *s, int on) static void control (SB16State *s, int hold) { int dma = s->use_hdma ? s->hdma : s->dma; + IsaDma *isa_dma = s->use_hdma ? s->isa_hdma : s->isa_dma; + IsaDmaClass *k = ISADMA_GET_CLASS(isa_dma); s->dma_running = hold; ldebug ("hold %d high %d dma %d\n", hold, s->use_hdma, dma); if (hold) { - DMA_hold_DREQ (dma); + k->hold_DREQ(isa_dma, dma); AUD_set_active_out (s->voice, 1); } else { - DMA_release_DREQ (dma); + k->release_DREQ(isa_dma, dma); AUD_set_active_out (s->voice, 0); } } @@ -1137,6 +1141,8 @@ static uint32_t mixer_read(void *opaque, uint32_t nport) static int write_audio (SB16State *s, int nchan, int dma_pos, int dma_len, int len) { + IsaDma *isa_dma = nchan == s->dma ? s->isa_dma : s->isa_hdma; + IsaDmaClass *k = ISADMA_GET_CLASS(isa_dma); int temp, net; uint8_t tmpbuf[4096]; @@ -1153,7 +1159,7 @@ static int write_audio (SB16State *s, int nchan, int dma_pos, to_copy = sizeof (tmpbuf); } - copied = DMA_read_memory (nchan, tmpbuf, dma_pos, to_copy); + copied = k->read_memory(isa_dma, nchan, tmpbuf, dma_pos, to_copy); copied = AUD_write (s->voice, tmpbuf, copied); temp -= copied; @@ -1355,6 +1361,7 @@ static void sb16_realizefn (DeviceState *dev, Error **errp) { ISADevice *isadev = ISA_DEVICE (dev); SB16State *s = SB16 (dev); + IsaDmaClass *k; isa_init_irq (isadev, &s->pic, s->irq); @@ -1373,8 +1380,14 @@ static void sb16_realizefn (DeviceState *dev, Error **errp) isa_register_portio_list (isadev, s->port, sb16_ioport_list, s, "sb16"); - DMA_register_channel (s->hdma, SB_read_DMA, s); - DMA_register_channel (s->dma, SB_read_DMA, s); + s->isa_hdma = isa_get_dma(isa_bus_from_device(isadev), s->hdma); + k = ISADMA_GET_CLASS(s->isa_hdma); + k->register_channel(s->isa_hdma, s->hdma, SB_read_DMA, s); + + s->isa_dma = isa_get_dma(isa_bus_from_device(isadev), s->dma); + k = ISADMA_GET_CLASS(s->isa_dma); + k->register_channel(s->isa_dma, s->dma, SB_read_DMA, s); + s->can_write = 1; AUD_register_card ("sb16", &s->card); diff --git a/hw/block/fdc.c b/hw/block/fdc.c index 818e8a4072..a6f22ef200 100644 --- a/hw/block/fdc.c +++ b/hw/block/fdc.c @@ -179,6 +179,21 @@ typedef struct FDrive { static FloppyDriveType get_fallback_drive_type(FDrive *drv); +/* Hack: FD_SEEK is expected to work on empty drives. However, QEMU + * currently goes through some pains to keep seeks within the bounds + * established by last_sect and max_track. Correcting this is difficult, + * as refactoring FDC code tends to expose nasty bugs in the Linux kernel. + * + * For now: allow empty drives to have large bounds so we can seek around, + * with the understanding that when a diskette is inserted, the bounds will + * properly tighten to match the geometry of that inserted medium. + */ +static void fd_empty_seek_hack(FDrive *drv) +{ + drv->last_sect = 0xFF; + drv->max_track = 0xFF; +} + static void fd_init(FDrive *drv) { /* Drive */ @@ -394,6 +409,7 @@ static void fd_revalidate(FDrive *drv) if (!blk_is_inserted(drv->blk)) { FLOPPY_DPRINTF("No disk in drive\n"); drv->disk = FLOPPY_DRIVE_TYPE_NONE; + fd_empty_seek_hack(drv); } else if (!drv->media_validated) { rc = pick_geometry(drv); if (rc) { @@ -628,6 +644,7 @@ struct FDCtrl { QEMUTimer *result_timer; int dma_chann; uint8_t phase; + IsaDma *dma; /* Controller's identification */ uint8_t version; /* HW */ @@ -1413,7 +1430,8 @@ static void fdctrl_stop_transfer(FDCtrl *fdctrl, uint8_t status0, fdctrl->fifo[6] = FD_SECTOR_SC; fdctrl->data_dir = FD_DIR_READ; if (!(fdctrl->msr & FD_MSR_NONDMA)) { - DMA_release_DREQ(fdctrl->dma_chann); + IsaDmaClass *k = ISADMA_GET_CLASS(fdctrl->dma); + k->release_DREQ(fdctrl->dma, fdctrl->dma_chann); } fdctrl->msr |= FD_MSR_RQM | FD_MSR_DIO; fdctrl->msr &= ~FD_MSR_NONDMA; @@ -1499,27 +1517,43 @@ static void fdctrl_start_transfer(FDCtrl *fdctrl, int direction) } fdctrl->eot = fdctrl->fifo[6]; if (fdctrl->dor & FD_DOR_DMAEN) { - int dma_mode; + IsaDmaTransferMode dma_mode; + IsaDmaClass *k = ISADMA_GET_CLASS(fdctrl->dma); + bool dma_mode_ok; /* DMA transfer are enabled. Check if DMA channel is well programmed */ - dma_mode = DMA_get_channel_mode(fdctrl->dma_chann); - dma_mode = (dma_mode >> 2) & 3; + dma_mode = k->get_transfer_mode(fdctrl->dma, fdctrl->dma_chann); FLOPPY_DPRINTF("dma_mode=%d direction=%d (%d - %d)\n", dma_mode, direction, (128 << fdctrl->fifo[5]) * (cur_drv->last_sect - ks + 1), fdctrl->data_len); - if (((direction == FD_DIR_SCANE || direction == FD_DIR_SCANL || - direction == FD_DIR_SCANH) && dma_mode == 0) || - (direction == FD_DIR_WRITE && dma_mode == 2) || - (direction == FD_DIR_READ && dma_mode == 1) || - (direction == FD_DIR_VERIFY)) { + switch (direction) { + case FD_DIR_SCANE: + case FD_DIR_SCANL: + case FD_DIR_SCANH: + dma_mode_ok = (dma_mode == ISADMA_TRANSFER_VERIFY); + break; + case FD_DIR_WRITE: + dma_mode_ok = (dma_mode == ISADMA_TRANSFER_WRITE); + break; + case FD_DIR_READ: + dma_mode_ok = (dma_mode == ISADMA_TRANSFER_READ); + break; + case FD_DIR_VERIFY: + dma_mode_ok = true; + break; + default: + dma_mode_ok = false; + break; + } + if (dma_mode_ok) { /* No access is allowed until DMA transfer has completed */ fdctrl->msr &= ~FD_MSR_RQM; if (direction != FD_DIR_VERIFY) { /* Now, we just have to wait for the DMA controller to * recall us... */ - DMA_hold_DREQ(fdctrl->dma_chann); - DMA_schedule(); + k->hold_DREQ(fdctrl->dma, fdctrl->dma_chann); + k->schedule(fdctrl->dma); } else { /* Start transfer */ fdctrl_transfer_handler(fdctrl, fdctrl->dma_chann, 0, @@ -1558,12 +1592,14 @@ static int fdctrl_transfer_handler (void *opaque, int nchan, FDrive *cur_drv; int len, start_pos, rel_pos; uint8_t status0 = 0x00, status1 = 0x00, status2 = 0x00; + IsaDmaClass *k; fdctrl = opaque; if (fdctrl->msr & FD_MSR_RQM) { FLOPPY_DPRINTF("Not in DMA transfer mode !\n"); return 0; } + k = ISADMA_GET_CLASS(fdctrl->dma); cur_drv = get_cur_drv(fdctrl); if (fdctrl->data_dir == FD_DIR_SCANE || fdctrl->data_dir == FD_DIR_SCANL || fdctrl->data_dir == FD_DIR_SCANH) @@ -1602,8 +1638,8 @@ static int fdctrl_transfer_handler (void *opaque, int nchan, switch (fdctrl->data_dir) { case FD_DIR_READ: /* READ commands */ - DMA_write_memory (nchan, fdctrl->fifo + rel_pos, - fdctrl->data_pos, len); + k->write_memory(fdctrl->dma, nchan, fdctrl->fifo + rel_pos, + fdctrl->data_pos, len); break; case FD_DIR_WRITE: /* WRITE commands */ @@ -1617,8 +1653,8 @@ static int fdctrl_transfer_handler (void *opaque, int nchan, goto transfer_error; } - DMA_read_memory (nchan, fdctrl->fifo + rel_pos, - fdctrl->data_pos, len); + k->read_memory(fdctrl->dma, nchan, fdctrl->fifo + rel_pos, + fdctrl->data_pos, len); if (blk_write(cur_drv->blk, fd_sector(cur_drv), fdctrl->fifo, 1) < 0) { FLOPPY_DPRINTF("error writing sector %d\n", @@ -1635,7 +1671,8 @@ static int fdctrl_transfer_handler (void *opaque, int nchan, { uint8_t tmpbuf[FD_SECTOR_LEN]; int ret; - DMA_read_memory (nchan, tmpbuf, fdctrl->data_pos, len); + k->read_memory(fdctrl->dma, nchan, tmpbuf, fdctrl->data_pos, + len); ret = memcmp(tmpbuf, fdctrl->fifo + rel_pos, len); if (ret == 0) { status2 = FD_SR2_SEH; @@ -2425,7 +2462,11 @@ static void fdctrl_realize_common(FDCtrl *fdctrl, Error **errp) fdctrl->num_floppies = MAX_FD; if (fdctrl->dma_chann != -1) { - DMA_register_channel(fdctrl->dma_chann, &fdctrl_transfer_handler, fdctrl); + IsaDmaClass *k; + assert(fdctrl->dma); + k = ISADMA_GET_CLASS(fdctrl->dma); + k->register_channel(fdctrl->dma, fdctrl->dma_chann, + &fdctrl_transfer_handler, fdctrl); } fdctrl_connect_drives(fdctrl, errp); } @@ -2448,6 +2489,10 @@ static void isabus_fdc_realize(DeviceState *dev, Error **errp) isa_init_irq(isadev, &fdctrl->irq, isa->irq); fdctrl->dma_chann = isa->dma; + if (fdctrl->dma_chann != -1) { + fdctrl->dma = isa_get_dma(isa_bus_from_device(isadev), isa->dma); + assert(fdctrl->dma); + } qdev_set_legacy_instance_id(dev, isa->iobase, 2); fdctrl_realize_common(fdctrl, &err); @@ -2476,6 +2521,8 @@ static void sun4m_fdc_initfn(Object *obj) FDCtrlSysBus *sys = SYSBUS_FDC(obj); FDCtrl *fdctrl = &sys->state; + fdctrl->dma_chann = -1; + memory_region_init_io(&fdctrl->iomem, obj, &fdctrl_mem_strict_ops, fdctrl, "fdctrl", 0x08); sysbus_init_mmio(sbd, &fdctrl->iomem); diff --git a/hw/dma/i82374.c b/hw/dma/i82374.c index 869721d848..6c0f975df0 100644 --- a/hw/dma/i82374.c +++ b/hw/dma/i82374.c @@ -25,6 +25,9 @@ #include "qemu/osdep.h" #include "hw/isa/isa.h" +#define TYPE_I82374 "i82374" +#define I82374(obj) OBJECT_CHECK(I82374State, (obj), TYPE_I82374) + //#define DEBUG_I82374 #ifdef DEBUG_I82374 @@ -38,6 +41,9 @@ do {} while (0) do { fprintf(stderr, "i82374 ERROR: " fmt , ## __VA_ARGS__); } while (0) typedef struct I82374State { + ISADevice parent_obj; + + uint32_t iobase; uint8_t commands[8]; PortioList port_list; } I82374State; @@ -99,32 +105,6 @@ static uint32_t i82374_read_descriptor(void *opaque, uint32_t nport) return val; } -static void i82374_realize(I82374State *s, Error **errp) -{ - DMA_init(1); - memset(s->commands, 0, sizeof(s->commands)); -} - -#define TYPE_I82374 "i82374" -#define I82374(obj) OBJECT_CHECK(ISAi82374State, (obj), TYPE_I82374) - -typedef struct ISAi82374State { - ISADevice parent_obj; - - uint32_t iobase; - I82374State state; -} ISAi82374State; - -static const VMStateDescription vmstate_isa_i82374 = { - .name = "isa-i82374", - .version_id = 0, - .minimum_version_id = 0, - .fields = (VMStateField[]) { - VMSTATE_STRUCT(state, ISAi82374State, 0, vmstate_i82374, I82374State), - VMSTATE_END_OF_LIST() - }, -}; - static const MemoryRegionPortio i82374_portio_list[] = { { 0x0A, 1, 1, .read = i82374_read_isr, }, { 0x10, 8, 1, .write = i82374_write_command, }, @@ -134,21 +114,21 @@ static const MemoryRegionPortio i82374_portio_list[] = { PORTIO_END_OF_LIST(), }; -static void i82374_isa_realize(DeviceState *dev, Error **errp) +static void i82374_realize(DeviceState *dev, Error **errp) { - ISAi82374State *isa = I82374(dev); - I82374State *s = &isa->state; + I82374State *s = I82374(dev); - portio_list_init(&s->port_list, OBJECT(isa), i82374_portio_list, s, + portio_list_init(&s->port_list, OBJECT(s), i82374_portio_list, s, "i82374"); - portio_list_add(&s->port_list, isa_address_space_io(&isa->parent_obj), - isa->iobase); + portio_list_add(&s->port_list, isa_address_space_io(&s->parent_obj), + s->iobase); - i82374_realize(s, errp); + DMA_init(isa_bus_from_device(ISA_DEVICE(dev)), 1); + memset(s->commands, 0, sizeof(s->commands)); } static Property i82374_properties[] = { - DEFINE_PROP_UINT32("iobase", ISAi82374State, iobase, 0x400), + DEFINE_PROP_UINT32("iobase", I82374State, iobase, 0x400), DEFINE_PROP_END_OF_LIST() }; @@ -156,21 +136,21 @@ static void i82374_class_init(ObjectClass *klass, void *data) { DeviceClass *dc = DEVICE_CLASS(klass); - dc->realize = i82374_isa_realize; - dc->vmsd = &vmstate_isa_i82374; + dc->realize = i82374_realize; + dc->vmsd = &vmstate_i82374; dc->props = i82374_properties; } -static const TypeInfo i82374_isa_info = { +static const TypeInfo i82374_info = { .name = TYPE_I82374, .parent = TYPE_ISA_DEVICE, - .instance_size = sizeof(ISAi82374State), + .instance_size = sizeof(I82374State), .class_init = i82374_class_init, }; static void i82374_register_types(void) { - type_register_static(&i82374_isa_info); + type_register_static(&i82374_info); } type_init(i82374_register_types) diff --git a/hw/dma/i8257.c b/hw/dma/i8257.c index 1ff6c4da71..5a52707ae2 100644 --- a/hw/dma/i8257.c +++ b/hw/dma/i8257.c @@ -24,9 +24,13 @@ #include "qemu/osdep.h" #include "hw/hw.h" #include "hw/isa/isa.h" +#include "hw/isa/i8257.h" #include "qemu/main-loop.h" #include "trace.h" +#define I8257(obj) \ + OBJECT_CHECK(I8257State, (obj), TYPE_I8257) + /* #define DEBUG_DMA */ #define dolog(...) fprintf (stderr, "dma: " __VA_ARGS__) @@ -38,32 +42,9 @@ #define ldebug(...) #endif -struct dma_regs { - int now[2]; - uint16_t base[2]; - uint8_t mode; - uint8_t page; - uint8_t pageh; - uint8_t dack; - uint8_t eop; - DMA_transfer_handler transfer_handler; - void *opaque; -}; - #define ADDR 0 #define COUNT 1 -static struct dma_cont { - uint8_t status; - uint8_t command; - uint8_t mask; - uint8_t flip_flop; - int dshift; - struct dma_regs regs[4]; - MemoryRegion channel_io; - MemoryRegion cont_io; -} dma_controllers[2]; - enum { CMD_MEMORY_TO_MEMORY = 0x01, CMD_FIXED_ADDRESS = 0x02, @@ -79,13 +60,13 @@ enum { }; -static void DMA_run (void); +static void i8257_dma_run(void *opaque); -static int channels[8] = {-1, 2, 3, 1, -1, -1, -1, 0}; +static const int channels[8] = {-1, 2, 3, 1, -1, -1, -1, 0}; -static void write_page (void *opaque, uint32_t nport, uint32_t data) +static void i8257_write_page(void *opaque, uint32_t nport, uint32_t data) { - struct dma_cont *d = opaque; + I8257State *d = opaque; int ichan; ichan = channels[nport & 7]; @@ -96,9 +77,9 @@ static void write_page (void *opaque, uint32_t nport, uint32_t data) d->regs[ichan].page = data; } -static void write_pageh (void *opaque, uint32_t nport, uint32_t data) +static void i8257_write_pageh(void *opaque, uint32_t nport, uint32_t data) { - struct dma_cont *d = opaque; + I8257State *d = opaque; int ichan; ichan = channels[nport & 7]; @@ -109,9 +90,9 @@ static void write_pageh (void *opaque, uint32_t nport, uint32_t data) d->regs[ichan].pageh = data; } -static uint32_t read_page (void *opaque, uint32_t nport) +static uint32_t i8257_read_page(void *opaque, uint32_t nport) { - struct dma_cont *d = opaque; + I8257State *d = opaque; int ichan; ichan = channels[nport & 7]; @@ -122,9 +103,9 @@ static uint32_t read_page (void *opaque, uint32_t nport) return d->regs[ichan].page; } -static uint32_t read_pageh (void *opaque, uint32_t nport) +static uint32_t i8257_read_pageh(void *opaque, uint32_t nport) { - struct dma_cont *d = opaque; + I8257State *d = opaque; int ichan; ichan = channels[nport & 7]; @@ -135,16 +116,16 @@ static uint32_t read_pageh (void *opaque, uint32_t nport) return d->regs[ichan].pageh; } -static inline void init_chan (struct dma_cont *d, int ichan) +static inline void i8257_init_chan(I8257State *d, int ichan) { - struct dma_regs *r; + I8257Regs *r; r = d->regs + ichan; r->now[ADDR] = r->base[ADDR] << d->dshift; r->now[COUNT] = 0; } -static inline int getff (struct dma_cont *d) +static inline int i8257_getff(I8257State *d) { int ff; @@ -153,11 +134,11 @@ static inline int getff (struct dma_cont *d) return ff; } -static uint64_t read_chan(void *opaque, hwaddr nport, unsigned size) +static uint64_t i8257_read_chan(void *opaque, hwaddr nport, unsigned size) { - struct dma_cont *d = opaque; + I8257State *d = opaque; int ichan, nreg, iport, ff, val, dir; - struct dma_regs *r; + I8257Regs *r; iport = (nport >> d->dshift) & 0x0f; ichan = iport >> 1; @@ -165,7 +146,7 @@ static uint64_t read_chan(void *opaque, hwaddr nport, unsigned size) r = d->regs + ichan; dir = ((r->mode >> 5) & 1) ? -1 : 1; - ff = getff (d); + ff = i8257_getff(d); if (nreg) val = (r->base[COUNT] << d->dshift) - r->now[COUNT]; else @@ -175,29 +156,29 @@ static uint64_t read_chan(void *opaque, hwaddr nport, unsigned size) return (val >> (d->dshift + (ff << 3))) & 0xff; } -static void write_chan(void *opaque, hwaddr nport, uint64_t data, - unsigned size) +static void i8257_write_chan(void *opaque, hwaddr nport, uint64_t data, + unsigned int size) { - struct dma_cont *d = opaque; + I8257State *d = opaque; int iport, ichan, nreg; - struct dma_regs *r; + I8257Regs *r; iport = (nport >> d->dshift) & 0x0f; ichan = iport >> 1; nreg = iport & 1; r = d->regs + ichan; - if (getff (d)) { + if (i8257_getff(d)) { r->base[nreg] = (r->base[nreg] & 0xff) | ((data << 8) & 0xff00); - init_chan (d, ichan); + i8257_init_chan(d, ichan); } else { r->base[nreg] = (r->base[nreg] & 0xff00) | (data & 0xff); } } -static void write_cont(void *opaque, hwaddr nport, uint64_t data, - unsigned size) +static void i8257_write_cont(void *opaque, hwaddr nport, uint64_t data, + unsigned int size) { - struct dma_cont *d = opaque; + I8257State *d = opaque; int iport, ichan = 0; iport = (nport >> d->dshift) & 0x0f; @@ -219,7 +200,7 @@ static void write_cont(void *opaque, hwaddr nport, uint64_t data, d->status &= ~(1 << (ichan + 4)); } d->status &= ~(1 << ichan); - DMA_run(); + i8257_dma_run(d); break; case 0x02: /* single mask */ @@ -227,7 +208,7 @@ static void write_cont(void *opaque, hwaddr nport, uint64_t data, d->mask |= 1 << (data & 3); else d->mask &= ~(1 << (data & 3)); - DMA_run(); + i8257_dma_run(d); break; case 0x03: /* mode */ @@ -262,12 +243,12 @@ static void write_cont(void *opaque, hwaddr nport, uint64_t data, case 0x06: /* clear mask for all channels */ d->mask = 0; - DMA_run(); + i8257_dma_run(d); break; case 0x07: /* write mask for all channels */ d->mask = data; - DMA_run(); + i8257_dma_run(d); break; default: @@ -283,9 +264,9 @@ static void write_cont(void *opaque, hwaddr nport, uint64_t data, #endif } -static uint64_t read_cont(void *opaque, hwaddr nport, unsigned size) +static uint64_t i8257_read_cont(void *opaque, hwaddr nport, unsigned size) { - struct dma_cont *d = opaque; + I8257State *d = opaque; int iport, val; iport = (nport >> d->dshift) & 0x0f; @@ -306,37 +287,43 @@ static uint64_t read_cont(void *opaque, hwaddr nport, unsigned size) return val; } -int DMA_get_channel_mode (int nchan) +static IsaDmaTransferMode i8257_dma_get_transfer_mode(IsaDma *obj, int nchan) { - return dma_controllers[nchan > 3].regs[nchan & 3].mode; + I8257State *d = I8257(obj); + return (d->regs[nchan & 3].mode >> 2) & 3; } -void DMA_hold_DREQ (int nchan) +static bool i8257_dma_has_autoinitialization(IsaDma *obj, int nchan) { - int ncont, ichan; + I8257State *d = I8257(obj); + return (d->regs[nchan & 3].mode >> 4) & 1; +} + +static void i8257_dma_hold_DREQ(IsaDma *obj, int nchan) +{ + I8257State *d = I8257(obj); + int ichan; - ncont = nchan > 3; ichan = nchan & 3; - linfo ("held cont=%d chan=%d\n", ncont, ichan); - dma_controllers[ncont].status |= 1 << (ichan + 4); - DMA_run(); + d->status |= 1 << (ichan + 4); + i8257_dma_run(d); } -void DMA_release_DREQ (int nchan) +static void i8257_dma_release_DREQ(IsaDma *obj, int nchan) { - int ncont, ichan; + I8257State *d = I8257(obj); + int ichan; - ncont = nchan > 3; ichan = nchan & 3; - linfo ("released cont=%d chan=%d\n", ncont, ichan); - dma_controllers[ncont].status &= ~(1 << (ichan + 4)); - DMA_run(); + d->status &= ~(1 << (ichan + 4)); + i8257_dma_run(d); } -static void channel_run (int ncont, int ichan) +static void i8257_channel_run(I8257State *d, int ichan) { + int ncont = d->dshift; int n; - struct dma_regs *r = &dma_controllers[ncont].regs[ichan]; + I8257Regs *r = &d->regs[ichan]; #ifdef DEBUG_DMA int dir, opmode; @@ -357,70 +344,58 @@ static void channel_run (int ncont, int ichan) ldebug ("dma_pos %d size %d\n", n, (r->base[COUNT] + 1) << ncont); } -static QEMUBH *dma_bh; -static bool dma_bh_scheduled; - -static void DMA_run (void) +static void i8257_dma_run(void *opaque) { - struct dma_cont *d; - int icont, ichan; + I8257State *d = opaque; + int ichan; int rearm = 0; - static int running = 0; - if (running) { + if (d->running) { rearm = 1; goto out; } else { - running = 1; + d->running = 1; } - d = dma_controllers; - - for (icont = 0; icont < 2; icont++, d++) { - for (ichan = 0; ichan < 4; ichan++) { - int mask; + for (ichan = 0; ichan < 4; ichan++) { + int mask; - mask = 1 << ichan; + mask = 1 << ichan; - if ((0 == (d->mask & mask)) && (0 != (d->status & (mask << 4)))) { - channel_run (icont, ichan); - rearm = 1; - } + if ((0 == (d->mask & mask)) && (0 != (d->status & (mask << 4)))) { + i8257_channel_run(d, ichan); + rearm = 1; } } - running = 0; + d->running = 0; out: if (rearm) { - qemu_bh_schedule_idle(dma_bh); - dma_bh_scheduled = true; + qemu_bh_schedule_idle(d->dma_bh); + d->dma_bh_scheduled = true; } } -static void DMA_run_bh(void *unused) +static void i8257_dma_register_channel(IsaDma *obj, int nchan, + DMA_transfer_handler transfer_handler, + void *opaque) { - dma_bh_scheduled = false; - DMA_run(); -} - -void DMA_register_channel (int nchan, - DMA_transfer_handler transfer_handler, - void *opaque) -{ - struct dma_regs *r; - int ichan, ncont; + I8257State *d = I8257(obj); + I8257Regs *r; + int ichan; - ncont = nchan > 3; ichan = nchan & 3; - r = dma_controllers[ncont].regs + ichan; + r = d->regs + ichan; r->transfer_handler = transfer_handler; r->opaque = opaque; } -int DMA_read_memory (int nchan, void *buf, int pos, int len) +static int i8257_dma_read_memory(IsaDma *obj, int nchan, void *buf, int pos, + int len) { - struct dma_regs *r = &dma_controllers[nchan > 3].regs[nchan & 3]; + I8257State *d = I8257(obj); + I8257Regs *r = &d->regs[nchan & 3]; hwaddr addr = ((r->pageh & 0x7f) << 24) | (r->page << 16) | r->now[ADDR]; if (r->mode & 0x20) { @@ -440,9 +415,11 @@ int DMA_read_memory (int nchan, void *buf, int pos, int len) return len; } -int DMA_write_memory (int nchan, void *buf, int pos, int len) +static int i8257_dma_write_memory(IsaDma *obj, int nchan, void *buf, int pos, + int len) { - struct dma_regs *r = &dma_controllers[nchan > 3].regs[nchan & 3]; + I8257State *s = I8257(obj); + I8257Regs *r = &s->regs[nchan & 3]; hwaddr addr = ((r->pageh & 0x7f) << 24) | (r->page << 16) | r->now[ADDR]; if (r->mode & 0x20) { @@ -465,20 +442,22 @@ int DMA_write_memory (int nchan, void *buf, int pos, int len) /* request the emulator to transfer a new DMA memory block ASAP (even * if the idle bottom half would not have exited the iothread yet). */ -void DMA_schedule(void) +static void i8257_dma_schedule(IsaDma *obj) { - if (dma_bh_scheduled) { + I8257State *d = I8257(obj); + if (d->dma_bh_scheduled) { qemu_notify_event(); } } -static void dma_reset(void *opaque) +static void i8257_reset(DeviceState *dev) { - struct dma_cont *d = opaque; - write_cont(d, (0x05 << d->dshift), 0, 1); + I8257State *d = I8257(dev); + i8257_write_cont(d, (0x05 << d->dshift), 0, 1); } -static int dma_phony_handler (void *opaque, int nchan, int dma_pos, int dma_len) +static int i8257_phony_handler(void *opaque, int nchan, int dma_pos, + int dma_len) { trace_i8257_unregistered_dma(nchan, dma_pos, dma_len); return dma_pos; @@ -486,8 +465,8 @@ static int dma_phony_handler (void *opaque, int nchan, int dma_pos, int dma_len) static const MemoryRegionOps channel_io_ops = { - .read = read_chan, - .write = write_chan, + .read = i8257_read_chan, + .write = i8257_write_chan, .endianness = DEVICE_NATIVE_ENDIAN, .impl = { .min_access_size = 1, @@ -497,21 +476,21 @@ static const MemoryRegionOps channel_io_ops = { /* IOport from page_base */ static const MemoryRegionPortio page_portio_list[] = { - { 0x01, 3, 1, .write = write_page, .read = read_page, }, - { 0x07, 1, 1, .write = write_page, .read = read_page, }, + { 0x01, 3, 1, .write = i8257_write_page, .read = i8257_read_page, }, + { 0x07, 1, 1, .write = i8257_write_page, .read = i8257_read_page, }, PORTIO_END_OF_LIST(), }; /* IOport from pageh_base */ static const MemoryRegionPortio pageh_portio_list[] = { - { 0x01, 3, 1, .write = write_pageh, .read = read_pageh, }, - { 0x07, 3, 1, .write = write_pageh, .read = read_pageh, }, + { 0x01, 3, 1, .write = i8257_write_pageh, .read = i8257_read_pageh, }, + { 0x07, 3, 1, .write = i8257_write_pageh, .read = i8257_read_pageh, }, PORTIO_END_OF_LIST(), }; static const MemoryRegionOps cont_io_ops = { - .read = read_cont, - .write = write_cont, + .read = i8257_read_cont, + .write = i8257_write_cont, .endianness = DEVICE_NATIVE_ENDIAN, .impl = { .min_access_size = 1, @@ -519,82 +498,142 @@ static const MemoryRegionOps cont_io_ops = { }, }; -/* dshift = 0: 8 bit DMA, 1 = 16 bit DMA */ -static void dma_init2(struct dma_cont *d, int base, int dshift, - int page_base, int pageh_base) +static const VMStateDescription vmstate_i8257_regs = { + .name = "dma_regs", + .version_id = 1, + .minimum_version_id = 1, + .fields = (VMStateField[]) { + VMSTATE_INT32_ARRAY(now, I8257Regs, 2), + VMSTATE_UINT16_ARRAY(base, I8257Regs, 2), + VMSTATE_UINT8(mode, I8257Regs), + VMSTATE_UINT8(page, I8257Regs), + VMSTATE_UINT8(pageh, I8257Regs), + VMSTATE_UINT8(dack, I8257Regs), + VMSTATE_UINT8(eop, I8257Regs), + VMSTATE_END_OF_LIST() + } +}; + +static int i8257_post_load(void *opaque, int version_id) { - int i; + I8257State *d = opaque; + i8257_dma_run(d); - d->dshift = dshift; + return 0; +} + +static const VMStateDescription vmstate_i8257 = { + .name = "dma", + .version_id = 1, + .minimum_version_id = 1, + .post_load = i8257_post_load, + .fields = (VMStateField[]) { + VMSTATE_UINT8(command, I8257State), + VMSTATE_UINT8(mask, I8257State), + VMSTATE_UINT8(flip_flop, I8257State), + VMSTATE_INT32(dshift, I8257State), + VMSTATE_STRUCT_ARRAY(regs, I8257State, 4, 1, vmstate_i8257_regs, + I8257Regs), + VMSTATE_END_OF_LIST() + } +}; + +static void i8257_realize(DeviceState *dev, Error **errp) +{ + ISADevice *isa = ISA_DEVICE(dev); + I8257State *d = I8257(dev); + int i; memory_region_init_io(&d->channel_io, NULL, &channel_io_ops, d, "dma-chan", 8 << d->dshift); - memory_region_add_subregion(isa_address_space_io(NULL), - base, &d->channel_io); + memory_region_add_subregion(isa_address_space_io(isa), + d->base, &d->channel_io); - isa_register_portio_list(NULL, page_base, page_portio_list, d, + isa_register_portio_list(isa, d->page_base, page_portio_list, d, "dma-page"); - if (pageh_base >= 0) { - isa_register_portio_list(NULL, pageh_base, pageh_portio_list, d, + if (d->pageh_base >= 0) { + isa_register_portio_list(isa, d->pageh_base, pageh_portio_list, d, "dma-pageh"); } - memory_region_init_io(&d->cont_io, NULL, &cont_io_ops, d, "dma-cont", - 8 << d->dshift); - memory_region_add_subregion(isa_address_space_io(NULL), - base + (8 << d->dshift), &d->cont_io); + memory_region_init_io(&d->cont_io, OBJECT(isa), &cont_io_ops, d, + "dma-cont", 8 << d->dshift); + memory_region_add_subregion(isa_address_space_io(isa), + d->base + (8 << d->dshift), &d->cont_io); - qemu_register_reset(dma_reset, d); - dma_reset(d); - for (i = 0; i < ARRAY_SIZE (d->regs); ++i) { - d->regs[i].transfer_handler = dma_phony_handler; + for (i = 0; i < ARRAY_SIZE(d->regs); ++i) { + d->regs[i].transfer_handler = i8257_phony_handler; } + + d->dma_bh = qemu_bh_new(i8257_dma_run, d); } -static const VMStateDescription vmstate_dma_regs = { - .name = "dma_regs", - .version_id = 1, - .minimum_version_id = 1, - .fields = (VMStateField[]) { - VMSTATE_INT32_ARRAY(now, struct dma_regs, 2), - VMSTATE_UINT16_ARRAY(base, struct dma_regs, 2), - VMSTATE_UINT8(mode, struct dma_regs), - VMSTATE_UINT8(page, struct dma_regs), - VMSTATE_UINT8(pageh, struct dma_regs), - VMSTATE_UINT8(dack, struct dma_regs), - VMSTATE_UINT8(eop, struct dma_regs), - VMSTATE_END_OF_LIST() - } +static Property i8257_properties[] = { + DEFINE_PROP_INT32("base", I8257State, base, 0x00), + DEFINE_PROP_INT32("page-base", I8257State, page_base, 0x80), + DEFINE_PROP_INT32("pageh-base", I8257State, pageh_base, 0x480), + DEFINE_PROP_INT32("dshift", I8257State, dshift, 0), + DEFINE_PROP_END_OF_LIST() }; -static int dma_post_load(void *opaque, int version_id) +static void i8257_class_init(ObjectClass *klass, void *data) { - DMA_run(); - - return 0; + DeviceClass *dc = DEVICE_CLASS(klass); + IsaDmaClass *idc = ISADMA_CLASS(klass); + + dc->realize = i8257_realize; + dc->reset = i8257_reset; + dc->vmsd = &vmstate_i8257; + dc->props = i8257_properties; + + idc->get_transfer_mode = i8257_dma_get_transfer_mode; + idc->has_autoinitialization = i8257_dma_has_autoinitialization; + idc->read_memory = i8257_dma_read_memory; + idc->write_memory = i8257_dma_write_memory; + idc->hold_DREQ = i8257_dma_hold_DREQ; + idc->release_DREQ = i8257_dma_release_DREQ; + idc->schedule = i8257_dma_schedule; + idc->register_channel = i8257_dma_register_channel; } -static const VMStateDescription vmstate_dma = { - .name = "dma", - .version_id = 1, - .minimum_version_id = 1, - .post_load = dma_post_load, - .fields = (VMStateField[]) { - VMSTATE_UINT8(command, struct dma_cont), - VMSTATE_UINT8(mask, struct dma_cont), - VMSTATE_UINT8(flip_flop, struct dma_cont), - VMSTATE_INT32(dshift, struct dma_cont), - VMSTATE_STRUCT_ARRAY(regs, struct dma_cont, 4, 1, vmstate_dma_regs, struct dma_regs), - VMSTATE_END_OF_LIST() +static const TypeInfo i8257_info = { + .name = TYPE_I8257, + .parent = TYPE_ISA_DEVICE, + .instance_size = sizeof(I8257State), + .class_init = i8257_class_init, + .interfaces = (InterfaceInfo[]) { + { TYPE_ISADMA }, + { } } }; -void DMA_init(int high_page_enable) +static void i8257_register_types(void) { - dma_init2(&dma_controllers[0], 0x00, 0, 0x80, high_page_enable ? 0x480 : -1); - dma_init2(&dma_controllers[1], 0xc0, 1, 0x88, high_page_enable ? 0x488 : -1); - vmstate_register (NULL, 0, &vmstate_dma, &dma_controllers[0]); - vmstate_register (NULL, 1, &vmstate_dma, &dma_controllers[1]); + type_register_static(&i8257_info); +} + +type_init(i8257_register_types) - dma_bh = qemu_bh_new(DMA_run_bh, NULL); +void DMA_init(ISABus *bus, int high_page_enable) +{ + ISADevice *isa1, *isa2; + DeviceState *d; + + isa1 = isa_create(bus, TYPE_I8257); + d = DEVICE(isa1); + qdev_prop_set_int32(d, "base", 0x00); + qdev_prop_set_int32(d, "page-base", 0x80); + qdev_prop_set_int32(d, "pageh-base", high_page_enable ? 0x480 : -1); + qdev_prop_set_int32(d, "dshift", 0); + qdev_init_nofail(d); + + isa2 = isa_create(bus, TYPE_I8257); + d = DEVICE(isa2); + qdev_prop_set_int32(d, "base", 0xc0); + qdev_prop_set_int32(d, "page-base", 0x88); + qdev_prop_set_int32(d, "pageh-base", high_page_enable ? 0x488 : -1); + qdev_prop_set_int32(d, "dshift", 1); + qdev_init_nofail(d); + + isa_bus_dma(bus, ISADMA(isa1), ISADMA(isa2)); } diff --git a/hw/i386/pc.c b/hw/i386/pc.c index 942ac0659a..b28bac4b66 100644 --- a/hw/i386/pc.c +++ b/hw/i386/pc.c @@ -1542,7 +1542,7 @@ void pc_basic_device_init(ISABus *isa_bus, qemu_irq *gsi, port92 = isa_create_simple(isa_bus, "port92"); port92_init(port92, &a20_line[1]); - DMA_init(0); + DMA_init(isa_bus, 0); for(i = 0; i < MAX_FD; i++) { fd[i] = drive_get(IF_FLOPPY, 0, i); 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/isa/isa-bus.c b/hw/isa/isa-bus.c index b487cb1839..c3b7388529 100644 --- a/hw/isa/isa-bus.c +++ b/hw/isa/isa-bus.c @@ -37,6 +37,12 @@ static void isa_bus_class_init(ObjectClass *klass, void *data) k->get_fw_dev_path = isabus_get_fw_dev_path; } +static const TypeInfo isa_dma_info = { + .name = TYPE_ISADMA, + .parent = TYPE_INTERFACE, + .class_size = sizeof(IsaDmaClass), +}; + static const TypeInfo isa_bus_info = { .name = TYPE_ISA_BUS, .parent = TYPE_BUS, @@ -90,6 +96,20 @@ void isa_init_irq(ISADevice *dev, qemu_irq *p, int isairq) dev->nirqs++; } +void isa_bus_dma(ISABus *bus, IsaDma *dma8, IsaDma *dma16) +{ + assert(bus && dma8 && dma16); + assert(!bus->dma[0] && !bus->dma[1]); + bus->dma[0] = dma8; + bus->dma[1] = dma16; +} + +IsaDma *isa_get_dma(ISABus *bus, int nchan) +{ + assert(bus); + return bus->dma[nchan > 3 ? 1 : 0]; +} + static inline void isa_init_ioport(ISADevice *dev, uint16_t ioport) { if (dev && (dev->ioport_id == 0 || ioport < dev->ioport_id)) { @@ -223,6 +243,7 @@ static const TypeInfo isa_device_type_info = { static void isabus_register_types(void) { + type_register_static(&isa_dma_info); type_register_static(&isa_bus_info); type_register_static(&isabus_bridge_info); type_register_static(&isa_device_type_info); diff --git a/hw/mips/mips_fulong2e.c b/hw/mips/mips_fulong2e.c index 6748d89478..184c404454 100644 --- a/hw/mips/mips_fulong2e.c +++ b/hw/mips/mips_fulong2e.c @@ -366,7 +366,7 @@ static void mips_fulong2e_init(MachineState *machine) /* init other devices */ pit = pit_init(isa_bus, 0x40, 0, NULL); - DMA_init(0); + DMA_init(isa_bus, 0); /* Super I/O */ isa_create_simple(isa_bus, "i8042"); diff --git a/hw/mips/mips_jazz.c b/hw/mips/mips_jazz.c index 62527fdbe8..d6d8058602 100644 --- a/hw/mips/mips_jazz.c +++ b/hw/mips/mips_jazz.c @@ -225,7 +225,7 @@ static void mips_jazz_init(MachineState *machine, /* ISA devices */ i8259 = i8259_init(isa_bus, env->irq[4]); isa_bus_irqs(isa_bus, i8259); - DMA_init(0); + DMA_init(isa_bus, 0); pit = pit_init(isa_bus, 0x40, 0, NULL); pcspk_init(isa_bus, pit); @@ -297,7 +297,8 @@ static void mips_jazz_init(MachineState *machine, for (n = 0; n < MAX_FD; n++) { fds[n] = drive_get(IF_FLOPPY, 0, n); } - fdctrl_init_sysbus(qdev_get_gpio_in(rc4030, 1), 0, 0x80003000, fds); + /* FIXME: we should enable DMA with a custom IsaDma device */ + fdctrl_init_sysbus(qdev_get_gpio_in(rc4030, 1), -1, 0x80003000, fds); /* Real time clock */ rtc_init(isa_bus, 1980, NULL); diff --git a/hw/mips/mips_malta.c b/hw/mips/mips_malta.c index c5da83fde8..c04aa2b8cc 100644 --- a/hw/mips/mips_malta.c +++ b/hw/mips/mips_malta.c @@ -1166,7 +1166,7 @@ void mips_malta_init(MachineState *machine) smbus_eeprom_init(smbus, 8, smbus_eeprom_buf, smbus_eeprom_size); g_free(smbus_eeprom_buf); pit = pit_init(isa_bus, 0x40, 0, NULL); - DMA_init(0); + DMA_init(isa_bus, 0); /* Super I/O */ isa_create_simple(isa_bus, "i8042"); 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/net/cadence_gem.c b/hw/net/cadence_gem.c index f9e409192b..0346f3e335 100644 --- a/hw/net/cadence_gem.c +++ b/hw/net/cadence_gem.c @@ -678,6 +678,10 @@ static ssize_t gem_receive(NetClientState *nc, const uint8_t *buf, size_t size) } else { unsigned crc_val; + if (size > sizeof(rxbuf) - sizeof(crc_val)) { + size = sizeof(rxbuf) - sizeof(crc_val); + } + bytes_to_copy = size; /* The application wants the FCS field, which QEMU does not provide. * We must try and calculate one. */ @@ -863,6 +867,14 @@ static void gem_transmit(CadenceGEMState *s) break; } + if (tx_desc_get_length(desc) > sizeof(tx_packet) - (p - tx_packet)) { + DB_PRINT("TX descriptor @ 0x%x too large: size 0x%x space 0x%x\n", + (unsigned)packet_desc_addr, + (unsigned)tx_desc_get_length(desc), + sizeof(tx_packet) - (p - tx_packet)); + break; + } + /* Gather this fragment of the packet from "dma memory" to our contig. * buffer. */ diff --git a/hw/net/e1000.c b/hw/net/e1000.c index 4eda7a3289..0387fa0646 100644 --- a/hw/net/e1000.c +++ b/hw/net/e1000.c @@ -909,7 +909,8 @@ start_xmit(E1000State *s) * bogus values to TDT/TDLEN. * there's nothing too intelligent we could do about this. */ - if (s->mac_reg[TDH] == tdh_start) { + if (s->mac_reg[TDH] == tdh_start || + tdh_start >= s->mac_reg[TDLEN] / sizeof(desc)) { DBGOUT(TXERR, "TDH wraparound @%x, TDT %x, TDLEN %x\n", tdh_start, s->mac_reg[TDT], s->mac_reg[TDLEN]); break; @@ -1166,7 +1167,8 @@ e1000_receive_iov(NetClientState *nc, const struct iovec *iov, int iovcnt) if (++s->mac_reg[RDH] * sizeof(desc) >= s->mac_reg[RDLEN]) s->mac_reg[RDH] = 0; /* see comment in start_xmit; same here */ - if (s->mac_reg[RDH] == rdh_start) { + if (s->mac_reg[RDH] == rdh_start || + rdh_start >= s->mac_reg[RDLEN] / sizeof(desc)) { DBGOUT(RXERR, "RDH wraparound @%x, RDT %x, RDLEN %x\n", rdh_start, s->mac_reg[RDT], s->mac_reg[RDLEN]); set_ics(s, 0, E1000_ICS_RXO); diff --git a/hw/sparc/sun4m.c b/hw/sparc/sun4m.c index 1fcec4478f..20dc341710 100644 --- a/hw/sparc/sun4m.c +++ b/hw/sparc/sun4m.c @@ -96,29 +96,7 @@ struct sun4m_hwdef { uint8_t nvram_machine_id; }; -int DMA_get_channel_mode (int nchan) -{ - return 0; -} -int DMA_read_memory (int nchan, void *buf, int pos, int size) -{ - return 0; -} -int DMA_write_memory (int nchan, void *buf, int pos, int size) -{ - return 0; -} -void DMA_hold_DREQ (int nchan) {} -void DMA_release_DREQ (int nchan) {} -void DMA_schedule(void) {} - -void DMA_init(int high_page_enable) -{ -} - -void DMA_register_channel (int nchan, - DMA_transfer_handler transfer_handler, - void *opaque) +void DMA_init(ISABus *bus, int high_page_enable) { } diff --git a/hw/sparc64/sun4u.c b/hw/sparc64/sun4u.c index 124c376897..add1e752f3 100644 --- a/hw/sparc64/sun4u.c +++ b/hw/sparc64/sun4u.c @@ -99,29 +99,7 @@ typedef struct EbusState { MemoryRegion bar1; } EbusState; -int DMA_get_channel_mode (int nchan) -{ - return 0; -} -int DMA_read_memory (int nchan, void *buf, int pos, int size) -{ - return 0; -} -int DMA_write_memory (int nchan, void *buf, int pos, int size) -{ - return 0; -} -void DMA_hold_DREQ (int nchan) {} -void DMA_release_DREQ (int nchan) {} -void DMA_schedule(void) {} - -void DMA_init(int high_page_enable) -{ -} - -void DMA_register_channel (int nchan, - DMA_transfer_handler transfer_handler, - void *opaque) +void DMA_init(ISABus *bus, int high_page_enable) { } @@ -816,6 +794,7 @@ static void sun4uv_init(MemoryRegion *address_space_mem, qemu_irq *ivec_irqs, *pbm_irqs; DriveInfo *hd[MAX_IDE_BUS * MAX_IDE_DEVS]; DriveInfo *fd[MAX_FD]; + DeviceState *dev; FWCfgState *fw_cfg; /* init CPUs */ @@ -852,10 +831,22 @@ static void sun4uv_init(MemoryRegion *address_space_mem, pci_cmd646_ide_init(pci_bus, hd, 1); isa_create_simple(isa_bus, "i8042"); + + /* Floppy */ for(i = 0; i < MAX_FD; i++) { fd[i] = drive_get(IF_FLOPPY, 0, i); } - fdctrl_init_isa(isa_bus, fd); + dev = DEVICE(isa_create(isa_bus, TYPE_ISA_FDC)); + if (fd[0]) { + qdev_prop_set_drive(dev, "driveA", blk_by_legacy_dinfo(fd[0]), + &error_abort); + } + if (fd[1]) { + qdev_prop_set_drive(dev, "driveB", blk_by_legacy_dinfo(fd[1]), + &error_abort); + } + qdev_prop_set_uint32(dev, "dma", -1); + qdev_init_nofail(dev); /* Map NVRAM into I/O (ebus) space */ nvram = m48t59_init(NULL, 0, 0, NVRAM_SIZE, 1968, 59); 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/isa/i8257.h b/include/hw/isa/i8257.h new file mode 100644 index 0000000000..8d34ed17b7 --- /dev/null +++ b/include/hw/isa/i8257.h @@ -0,0 +1,42 @@ +#ifndef HW_I8257_H +#define HW_I8257_H + +#define TYPE_I8257 "i8257" + +typedef struct I8257Regs { + int now[2]; + uint16_t base[2]; + uint8_t mode; + uint8_t page; + uint8_t pageh; + uint8_t dack; + uint8_t eop; + DMA_transfer_handler transfer_handler; + void *opaque; +} I8257Regs; + +typedef struct I8257State { + /* <private> */ + ISADevice parent_obj; + + /* <public> */ + int32_t base; + int32_t page_base; + int32_t pageh_base; + int32_t dshift; + + uint8_t status; + uint8_t command; + uint8_t mask; + uint8_t flip_flop; + I8257Regs regs[4]; + MemoryRegion channel_io; + MemoryRegion cont_io; + + QEMUBH *dma_bh; + bool dma_bh_scheduled; + int running; +} I8257State; + +#endif + diff --git a/include/hw/isa/isa.h b/include/hw/isa/isa.h index de3cd3d38a..0bbe21cd48 100644 --- a/include/hw/isa/isa.h +++ b/include/hw/isa/isa.h @@ -34,6 +34,41 @@ static inline uint16_t applesmc_port(void) return 0; } +#define TYPE_ISADMA "isa-dma" + +#define ISADMA_CLASS(klass) \ + OBJECT_CLASS_CHECK(IsaDmaClass, (klass), TYPE_ISADMA) +#define ISADMA_GET_CLASS(obj) \ + OBJECT_GET_CLASS(IsaDmaClass, (obj), TYPE_ISADMA) +#define ISADMA(obj) \ + INTERFACE_CHECK(IsaDma, (obj), TYPE_ISADMA) + +struct IsaDma { + Object parent; +}; + +typedef enum { + ISADMA_TRANSFER_VERIFY, + ISADMA_TRANSFER_READ, + ISADMA_TRANSFER_WRITE, + ISADMA_TRANSFER_ILLEGAL, +} IsaDmaTransferMode; + +typedef struct IsaDmaClass { + InterfaceClass parent; + + IsaDmaTransferMode (*get_transfer_mode)(IsaDma *obj, int nchan); + bool (*has_autoinitialization)(IsaDma *obj, int nchan); + int (*read_memory)(IsaDma *obj, int nchan, void *buf, int pos, int len); + int (*write_memory)(IsaDma *obj, int nchan, void *buf, int pos, int len); + void (*hold_DREQ)(IsaDma *obj, int nchan); + void (*release_DREQ)(IsaDma *obj, int nchan); + void (*schedule)(IsaDma *obj); + void (*register_channel)(IsaDma *obj, int nchan, + DMA_transfer_handler transfer_handler, + void *opaque); +} IsaDmaClass; + typedef struct ISADeviceClass { DeviceClass parent_class; } ISADeviceClass; @@ -46,6 +81,7 @@ struct ISABus { MemoryRegion *address_space; MemoryRegion *address_space_io; qemu_irq *irqs; + IsaDma *dma[2]; }; struct ISADevice { @@ -63,6 +99,8 @@ ISABus *isa_bus_new(DeviceState *dev, MemoryRegion *address_space, void isa_bus_irqs(ISABus *bus, qemu_irq *irqs); qemu_irq isa_get_irq(ISADevice *dev, int isairq); void isa_init_irq(ISADevice *dev, qemu_irq *p, int isairq); +void isa_bus_dma(ISABus *bus, IsaDma *dma8, IsaDma *dma16); +IsaDma *isa_get_dma(ISABus *bus, int nchan); MemoryRegion *isa_address_space(ISADevice *dev); MemoryRegion *isa_address_space_io(ISADevice *dev); ISADevice *isa_create(ISABus *bus, const char *name); @@ -106,15 +144,6 @@ static inline ISABus *isa_bus_from_device(ISADevice *d) return ISA_BUS(qdev_get_parent_bus(DEVICE(d))); } -/* dma.c */ -int DMA_get_channel_mode (int nchan); -int DMA_read_memory (int nchan, void *buf, int pos, int size); -int DMA_write_memory (int nchan, void *buf, int pos, int size); -void DMA_hold_DREQ (int nchan); -void DMA_release_DREQ (int nchan); -void DMA_schedule(void); -void DMA_init(int high_page_enable); -void DMA_register_channel (int nchan, - DMA_transfer_handler transfer_handler, - void *opaque); +/* i8257.c */ +void DMA_init(ISABus *bus, int high_page_enable); #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/net/filter.h b/include/net/filter.h index 2deda362a6..56399763cc 100644 --- a/include/net/filter.h +++ b/include/net/filter.h @@ -55,7 +55,6 @@ struct NetFilterState { char *netdev_id; NetClientState *netdev; NetFilterDirection direction; - char info_str[256]; QTAILQ_ENTRY(NetFilterState) next; }; diff --git a/include/net/net.h b/include/net/net.h index 7af3e15f83..73e4c466e2 100644 --- a/include/net/net.h +++ b/include/net/net.h @@ -92,7 +92,7 @@ struct NetClientState { NetClientDestructor *destructor; unsigned int queue_index; unsigned rxfilter_notify_enabled:1; - QTAILQ_HEAD(, NetFilterState) filters; + QTAILQ_HEAD(NetFilterHead, NetFilterState) filters; }; typedef struct NICState { 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/include/qemu/typedefs.h b/include/qemu/typedefs.h index 78fe6e86e3..6ed91b4968 100644 --- a/include/qemu/typedefs.h +++ b/include/qemu/typedefs.h @@ -33,6 +33,7 @@ typedef struct I2CBus I2CBus; typedef struct I2SCodec I2SCodec; typedef struct ISABus ISABus; typedef struct ISADevice ISADevice; +typedef struct IsaDma IsaDma; typedef struct LoadStateEntry LoadStateEntry; typedef struct MACAddr MACAddr; typedef struct MachineClass MachineClass; 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/net/filter.c b/net/filter.c index 5d90f83429..8f07b99127 100644 --- a/net/filter.c +++ b/net/filter.c @@ -15,7 +15,6 @@ #include "net/vhost_net.h" #include "qom/object_interfaces.h" #include "qemu/iov.h" -#include "qapi/string-output-visitor.h" ssize_t qemu_netfilter_receive(NetFilterState *nf, NetFilterDirection direction, @@ -34,6 +33,22 @@ ssize_t qemu_netfilter_receive(NetFilterState *nf, return 0; } +static NetFilterState *netfilter_next(NetFilterState *nf, + NetFilterDirection dir) +{ + NetFilterState *next; + + if (dir == NET_FILTER_DIRECTION_TX) { + /* forward walk through filters */ + next = QTAILQ_NEXT(nf, next); + } else { + /* reverse order */ + next = QTAILQ_PREV(nf, NetFilterHead, next); + } + + return next; +} + ssize_t qemu_netfilter_pass_to_next(NetClientState *sender, unsigned flags, const struct iovec *iov, @@ -43,7 +58,7 @@ ssize_t qemu_netfilter_pass_to_next(NetClientState *sender, int ret = 0; int direction; NetFilterState *nf = opaque; - NetFilterState *next = QTAILQ_NEXT(nf, next); + NetFilterState *next = NULL; if (!sender || !sender->peer) { /* no receiver, or sender been deleted, no need to pass it further */ @@ -61,6 +76,7 @@ ssize_t qemu_netfilter_pass_to_next(NetClientState *sender, direction = nf->direction; } + next = netfilter_next(nf, direction); while (next) { /* * if qemu_netfilter_pass_to_next been called, means that @@ -73,7 +89,7 @@ ssize_t qemu_netfilter_pass_to_next(NetClientState *sender, if (ret) { return ret; } - next = QTAILQ_NEXT(next, next); + next = netfilter_next(next, direction); } /* @@ -135,10 +151,6 @@ static void netfilter_complete(UserCreatable *uc, Error **errp) NetFilterClass *nfc = NETFILTER_GET_CLASS(uc); int queues; Error *local_err = NULL; - char *str, *info; - ObjectProperty *prop; - ObjectPropertyIterator iter; - StringOutputVisitor *ov; if (!nf->netdev_id) { error_setg(errp, "Parameter 'netdev' is required"); @@ -172,23 +184,6 @@ static void netfilter_complete(UserCreatable *uc, Error **errp) } } QTAILQ_INSERT_TAIL(&nf->netdev->filters, nf, next); - - /* generate info str */ - object_property_iter_init(&iter, OBJECT(nf)); - while ((prop = object_property_iter_next(&iter))) { - if (!strcmp(prop->name, "type")) { - continue; - } - ov = string_output_visitor_new(false); - object_property_get(OBJECT(nf), string_output_get_visitor(ov), - prop->name, errp); - str = string_output_get_string(ov); - string_output_visitor_cleanup(ov); - info = g_strdup_printf(",%s=%s", prop->name, str); - g_strlcat(nf->info_str, info, sizeof(nf->info_str)); - g_free(str); - g_free(info); - } } static void netfilter_finalize(Object *obj) @@ -45,6 +45,7 @@ #include "qapi/dealloc-visitor.h" #include "sysemu/sysemu.h" #include "net/filter.h" +#include "qapi/string-output-visitor.h" /* Net bridge is currently not supported for W32. */ #if !defined(_WIN32) @@ -580,11 +581,21 @@ static ssize_t filter_receive_iov(NetClientState *nc, ssize_t ret = 0; NetFilterState *nf = NULL; - QTAILQ_FOREACH(nf, &nc->filters, next) { - ret = qemu_netfilter_receive(nf, direction, sender, flags, iov, - iovcnt, sent_cb); - if (ret) { - return ret; + if (direction == NET_FILTER_DIRECTION_TX) { + QTAILQ_FOREACH(nf, &nc->filters, next) { + ret = qemu_netfilter_receive(nf, direction, sender, flags, iov, + iovcnt, sent_cb); + if (ret) { + return ret; + } + } + } else { + QTAILQ_FOREACH_REVERSE(nf, &nc->filters, NetFilterHead, next) { + ret = qemu_netfilter_receive(nf, direction, sender, flags, iov, + iovcnt, sent_cb); + if (ret) { + return ret; + } } } @@ -1185,6 +1196,30 @@ void qmp_netdev_del(const char *id, Error **errp) qemu_opts_del(opts); } +static void netfilter_print_info(Monitor *mon, NetFilterState *nf) +{ + char *str; + ObjectProperty *prop; + ObjectPropertyIterator iter; + StringOutputVisitor *ov; + + /* generate info str */ + object_property_iter_init(&iter, OBJECT(nf)); + while ((prop = object_property_iter_next(&iter))) { + if (!strcmp(prop->name, "type")) { + continue; + } + ov = string_output_visitor_new(false); + object_property_get(OBJECT(nf), string_output_get_visitor(ov), + prop->name, NULL); + str = string_output_get_string(ov); + string_output_visitor_cleanup(ov); + monitor_printf(mon, ",%s=%s", prop->name, str); + g_free(str); + } + monitor_printf(mon, "\n"); +} + void print_net_client(Monitor *mon, NetClientState *nc) { NetFilterState *nf; @@ -1198,9 +1233,10 @@ void print_net_client(Monitor *mon, NetClientState *nc) } QTAILQ_FOREACH(nf, &nc->filters, next) { char *path = object_get_canonical_path_component(OBJECT(nf)); - monitor_printf(mon, " - %s: type=%s%s\n", path, - object_get_typename(OBJECT(nf)), - nf->info_str); + + monitor_printf(mon, " - %s: type=%s", path, + object_get_typename(OBJECT(nf))); + netfilter_print_info(mon, nf); g_free(path); } } diff --git a/net/netmap.c b/net/netmap.c index 555836829e..27295ab2e2 100644 --- a/net/netmap.c +++ b/net/netmap.c @@ -39,21 +39,12 @@ #include "qemu/error-report.h" #include "qemu/iov.h" -/* Private netmap device info. */ -typedef struct NetmapPriv { - int fd; - size_t memsize; - void *mem; - struct netmap_if *nifp; - struct netmap_ring *rx; - struct netmap_ring *tx; - char fdname[PATH_MAX]; /* Normally "/dev/netmap". */ - char ifname[IFNAMSIZ]; -} NetmapPriv; - typedef struct NetmapState { NetClientState nc; - NetmapPriv me; + struct nm_desc *nmd; + char ifname[IFNAMSIZ]; + struct netmap_ring *tx; + struct netmap_ring *rx; bool read_poll; bool write_poll; struct iovec iov[IOV_MAX]; @@ -90,44 +81,23 @@ pkt_copy(const void *_src, void *_dst, int l) * Open a netmap device. We assume there is only one queue * (which is the case for the VALE bridge). */ -static void netmap_open(NetmapPriv *me, Error **errp) +static struct nm_desc *netmap_open(const NetdevNetmapOptions *nm_opts, + Error **errp) { - int fd; - int err; - size_t l; + struct nm_desc *nmd; struct nmreq req; - me->fd = fd = open(me->fdname, O_RDWR); - if (fd < 0) { - error_setg_file_open(errp, errno, me->fdname); - return; - } memset(&req, 0, sizeof(req)); - pstrcpy(req.nr_name, sizeof(req.nr_name), me->ifname); - req.nr_ringid = NETMAP_NO_TX_POLL; - req.nr_version = NETMAP_API; - err = ioctl(fd, NIOCREGIF, &req); - if (err) { - error_setg_errno(errp, errno, "Unable to register %s", me->ifname); - goto error; - } - l = me->memsize = req.nr_memsize; - me->mem = mmap(0, l, PROT_WRITE | PROT_READ, MAP_SHARED, fd, 0); - if (me->mem == MAP_FAILED) { - error_setg_errno(errp, errno, "Unable to mmap netmap shared memory"); - me->mem = NULL; - goto error; + nmd = nm_open(nm_opts->ifname, &req, NETMAP_NO_TX_POLL, + NULL); + if (nmd == NULL) { + error_setg_errno(errp, errno, "Failed to nm_open() %s", + nm_opts->ifname); + return NULL; } - me->nifp = NETMAP_IF(me->mem, req.nr_offset); - me->tx = NETMAP_TXRING(me->nifp, 0); - me->rx = NETMAP_RXRING(me->nifp, 0); - - return; - -error: - close(me->fd); + return nmd; } static void netmap_send(void *opaque); @@ -136,7 +106,7 @@ static void netmap_writable(void *opaque); /* Set the event-loop handlers for the netmap backend. */ static void netmap_update_fd_handler(NetmapState *s) { - qemu_set_fd_handler(s->me.fd, + qemu_set_fd_handler(s->nmd->fd, s->read_poll ? netmap_send : NULL, s->write_poll ? netmap_writable : NULL, s); @@ -188,7 +158,7 @@ static ssize_t netmap_receive(NetClientState *nc, const uint8_t *buf, size_t size) { NetmapState *s = DO_UPCAST(NetmapState, nc, nc); - struct netmap_ring *ring = s->me.tx; + struct netmap_ring *ring = s->tx; uint32_t i; uint32_t idx; uint8_t *dst; @@ -218,7 +188,7 @@ static ssize_t netmap_receive(NetClientState *nc, ring->slot[i].flags = 0; pkt_copy(buf, dst, size); ring->cur = ring->head = nm_ring_next(ring, i); - ioctl(s->me.fd, NIOCTXSYNC, NULL); + ioctl(s->nmd->fd, NIOCTXSYNC, NULL); return size; } @@ -227,7 +197,7 @@ static ssize_t netmap_receive_iov(NetClientState *nc, const struct iovec *iov, int iovcnt) { NetmapState *s = DO_UPCAST(NetmapState, nc, nc); - struct netmap_ring *ring = s->me.tx; + struct netmap_ring *ring = s->tx; uint32_t last; uint32_t idx; uint8_t *dst; @@ -284,7 +254,7 @@ static ssize_t netmap_receive_iov(NetClientState *nc, /* Now update ring->cur and ring->head. */ ring->cur = ring->head = i; - ioctl(s->me.fd, NIOCTXSYNC, NULL); + ioctl(s->nmd->fd, NIOCTXSYNC, NULL); return iov_size(iov, iovcnt); } @@ -301,7 +271,7 @@ static void netmap_send_completed(NetClientState *nc, ssize_t len) static void netmap_send(void *opaque) { NetmapState *s = opaque; - struct netmap_ring *ring = s->me.rx; + struct netmap_ring *ring = s->rx; /* Keep sending while there are available packets into the netmap RX ring and the forwarding path towards the peer is open. */ @@ -349,10 +319,8 @@ static void netmap_cleanup(NetClientState *nc) qemu_purge_queued_packets(nc); netmap_poll(nc, false); - munmap(s->me.mem, s->me.memsize); - close(s->me.fd); - - s->me.fd = -1; + nm_close(s->nmd); + s->nmd = NULL; } /* Offloading manipulation support callbacks. */ @@ -383,17 +351,17 @@ static void netmap_set_vnet_hdr_len(NetClientState *nc, int len) struct nmreq req; /* Issue a NETMAP_BDG_VNET_HDR command to change the virtio-net header - * length for the netmap adapter associated to 'me->ifname'. + * length for the netmap adapter associated to 's->ifname'. */ memset(&req, 0, sizeof(req)); - pstrcpy(req.nr_name, sizeof(req.nr_name), s->me.ifname); + pstrcpy(req.nr_name, sizeof(req.nr_name), s->ifname); req.nr_version = NETMAP_API; req.nr_cmd = NETMAP_BDG_VNET_HDR; req.nr_arg1 = len; - err = ioctl(s->me.fd, NIOCREGIF, &req); + err = ioctl(s->nmd->fd, NIOCREGIF, &req); if (err) { error_report("Unable to execute NETMAP_BDG_VNET_HDR on %s: %s", - s->me.ifname, strerror(errno)); + s->ifname, strerror(errno)); } else { /* Keep track of the current length. */ s->vnet_hdr_len = len; @@ -437,16 +405,12 @@ int net_init_netmap(const NetClientOptions *opts, const char *name, NetClientState *peer, Error **errp) { const NetdevNetmapOptions *netmap_opts = opts->u.netmap; + struct nm_desc *nmd; NetClientState *nc; Error *err = NULL; - NetmapPriv me; NetmapState *s; - pstrcpy(me.fdname, sizeof(me.fdname), - netmap_opts->has_devname ? netmap_opts->devname : "/dev/netmap"); - /* Set default name for the port if not supplied. */ - pstrcpy(me.ifname, sizeof(me.ifname), netmap_opts->ifname); - netmap_open(&me, &err); + nmd = netmap_open(netmap_opts, &err); if (err) { error_propagate(errp, err); return -1; @@ -454,8 +418,11 @@ int net_init_netmap(const NetClientOptions *opts, /* Create the object. */ nc = qemu_new_net_client(&net_netmap_info, peer, "netmap", name); s = DO_UPCAST(NetmapState, nc, nc); - s->me = me; + s->nmd = nmd; + s->tx = NETMAP_TXRING(nmd->nifp, 0); + s->rx = NETMAP_RXRING(nmd->nifp, 0); s->vnet_hdr_len = 0; + pstrcpy(s->ifname, sizeof(s->ifname), netmap_opts->ifname); netmap_read_poll(s, true); /* Initially only poll for reads. */ return 0; diff --git a/net/slirp.c b/net/slirp.c index f505570adb..eac4fc2506 100644 --- a/net/slirp.c +++ b/net/slirp.c @@ -784,6 +784,9 @@ int net_slirp_parse_legacy(QemuOptsList *opts_list, const char *optarg, int *ret return 0; } + error_report("The '-net channel' option is deprecated. " + "Please use '-netdev user,guestfwd=...' instead."); + /* handle legacy -net channel,port:chr */ optarg += strlen("channel,"); diff --git a/os-posix.c b/os-posix.c index e4da406f38..87e2a1696d 100644 --- a/os-posix.c +++ b/os-posix.c @@ -40,6 +40,7 @@ #include "net/slirp.h" #include "qemu-options.h" #include "qemu/rcu.h" +#include "qemu/error-report.h" #ifdef CONFIG_LINUX #include <sys/prctl.h> @@ -139,6 +140,8 @@ void os_parse_cmd_args(int index, const char *optarg) switch (index) { #ifdef CONFIG_SLIRP case QEMU_OPTION_smb: + error_report("The -smb option is deprecated. " + "Please use '-netdev user,smb=...' instead."); if (net_slirp_smb(optarg) < 0) exit(1); break; diff --git a/qemu-doc.texi b/qemu-doc.texi index ca4d9de15e..212aba3c08 100644 --- a/qemu-doc.texi +++ b/qemu-doc.texi @@ -1237,9 +1237,9 @@ echo 100 100 > /proc/sys/net/ipv4/ping_group_range When using the built-in TFTP server, the router is also the TFTP server. -When using the @option{-redir} option, TCP or UDP connections can be -redirected from the host to the guest. It allows for example to -redirect X11, telnet or SSH connections. +When using the @option{'-netdev user,hostfwd=...'} option, TCP or UDP +connections can be redirected from the host to the guest. It allows for +example to redirect X11, telnet or SSH connections. @subsection Connecting VLANs between QEMU instances @@ -1889,7 +1889,8 @@ correctly instructs QEMU to shutdown at the appropriate moment. @subsubsection Share a directory between Unix and Windows -See @ref{sec_invocation} about the help of the option @option{-smb}. +See @ref{sec_invocation} about the help of the option +@option{'-netdev user,smb=...'}. @subsubsection Windows XP security problem @@ -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 @@ -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/slirp/bootp.c b/slirp/bootp.c index 1baaab1ab1..00272793e0 100644 --- a/slirp/bootp.c +++ b/slirp/bootp.c @@ -325,7 +325,7 @@ static void bootp_reply(Slirp *slirp, const struct bootp_t *bp) m->m_len = sizeof(struct bootp_t) - sizeof(struct ip) - sizeof(struct udphdr); - udp_output2(NULL, m, &saddr, &daddr, IPTOS_LOWDELAY); + udp_output(NULL, m, &saddr, &daddr, IPTOS_LOWDELAY); } void bootp_input(struct mbuf *m) diff --git a/slirp/ip_icmp.c b/slirp/ip_icmp.c index 23b9f0fa45..592f33a827 100644 --- a/slirp/ip_icmp.c +++ b/slirp/ip_icmp.c @@ -157,12 +157,12 @@ icmp_input(struct mbuf *m, int hlen) goto freeit; } else { struct socket *so; - struct sockaddr_in addr; + struct sockaddr_storage addr; if ((so = socreate(slirp)) == NULL) goto freeit; if (icmp_send(so, m, hlen) == 0) { return; } - if(udp_attach(so) == -1) { + if (udp_attach(so, AF_INET) == -1) { DEBUG_MISC((dfd,"icmp_input udp_attach errno = %d-%s\n", errno,strerror(errno))); sofree(so); @@ -170,8 +170,10 @@ icmp_input(struct mbuf *m, int hlen) goto end_error; } so->so_m = m; + so->so_ffamily = AF_INET; so->so_faddr = ip->ip_dst; so->so_fport = htons(7); + so->so_lfamily = AF_INET; so->so_laddr = ip->ip_src; so->so_lport = htons(9); so->so_iptos = ip->ip_tos; @@ -179,20 +181,9 @@ icmp_input(struct mbuf *m, int hlen) so->so_state = SS_ISFCONNECTED; /* Send the packet */ - addr.sin_family = AF_INET; - if ((so->so_faddr.s_addr & slirp->vnetwork_mask.s_addr) == - slirp->vnetwork_addr.s_addr) { - /* It's an alias */ - if (so->so_faddr.s_addr == slirp->vnameserver_addr.s_addr) { - if (get_dns_addr(&addr.sin_addr) < 0) - addr.sin_addr = loopback_addr; - } else { - addr.sin_addr = loopback_addr; - } - } else { - addr.sin_addr = so->so_faddr; - } - addr.sin_port = so->so_fport; + addr = so->fhost.ss; + sotranslate_out(so, &addr); + if(sendto(so->s, icmp_ping_msg, strlen(icmp_ping_msg), 0, (struct sockaddr *)&addr, sizeof(addr)) == -1) { DEBUG_MISC((dfd,"icmp_input udp sendto tx errno = %d-%s\n", diff --git a/slirp/mbuf.c b/slirp/mbuf.c index 795fc29f98..bc942b63e4 100644 --- a/slirp/mbuf.c +++ b/slirp/mbuf.c @@ -91,7 +91,7 @@ m_get(Slirp *slirp) m->m_len = 0; m->m_nextpkt = NULL; m->m_prevpkt = NULL; - m->arp_requested = false; + m->resolution_requested = false; m->expiration_date = (uint64_t)-1; end_error: DEBUG_ARG("m = %p", m); diff --git a/slirp/mbuf.h b/slirp/mbuf.h index b144f1ce3a..38fedf46de 100644 --- a/slirp/mbuf.h +++ b/slirp/mbuf.h @@ -79,7 +79,7 @@ struct mbuf { int m_len; /* Amount of data in this mbuf */ Slirp *slirp; - bool arp_requested; + bool resolution_requested; uint64_t expiration_date; /* start of dynamic buffer area, must be last element */ union { diff --git a/slirp/slirp.c b/slirp/slirp.c index 35f819afb7..b900775eff 100644 --- a/slirp/slirp.c +++ b/slirp/slirp.c @@ -23,6 +23,7 @@ */ #include "qemu-common.h" #include "qemu/timer.h" +#include "qemu/error-report.h" #include "sysemu/char.h" #include "slirp.h" #include "hw/hw.h" @@ -234,7 +235,7 @@ Slirp *slirp_init(int restricted, struct in_addr vnetwork, slirp->opaque = opaque; - register_savevm(NULL, "slirp", 0, 3, + register_savevm(NULL, "slirp", 0, 4, slirp_state_save, slirp_state_load, slirp); QTAILQ_INSERT_TAIL(&slirp_instances, slirp, entry); @@ -762,20 +763,15 @@ void slirp_input(Slirp *slirp, const uint8_t *pkt, int pkt_len) } } -/* Output the IP packet to the ethernet device. Returns 0 if the packet must be - * re-queued. +/* Prepare the IPv4 packet to be sent to the ethernet device. Returns 1 if no + * packet should be sent, 0 if the packet must be re-queued, 2 if the packet + * is ready to go. */ -int if_encap(Slirp *slirp, struct mbuf *ifm) +static int if_encap4(Slirp *slirp, struct mbuf *ifm, struct ethhdr *eh, + uint8_t ethaddr[ETH_ALEN]) { - uint8_t buf[1600]; - struct ethhdr *eh = (struct ethhdr *)buf; - uint8_t ethaddr[ETH_ALEN]; const struct ip *iph = (const struct ip *)ifm->m_data; - if (ifm->m_len + ETH_HLEN > sizeof(buf)) { - return 1; - } - if (iph->ip_dst.s_addr == 0) { /* 0.0.0.0 can not be a destination address, something went wrong, * avoid making it worse */ @@ -786,7 +782,7 @@ int if_encap(Slirp *slirp, struct mbuf *ifm) struct ethhdr *reh = (struct ethhdr *)arp_req; struct arphdr *rah = (struct arphdr *)(arp_req + ETH_HLEN); - if (!ifm->arp_requested) { + if (!ifm->resolution_requested) { /* If the client addr is not known, send an ARP request */ memset(reh->h_dest, 0xff, ETH_ALEN); memcpy(reh->h_source, special_ethaddr, ETH_ALEN - 4); @@ -812,22 +808,62 @@ int if_encap(Slirp *slirp, struct mbuf *ifm) rah->ar_tip = iph->ip_dst.s_addr; slirp->client_ipaddr = iph->ip_dst; slirp_output(slirp->opaque, arp_req, sizeof(arp_req)); - ifm->arp_requested = true; + ifm->resolution_requested = true; /* Expire request and drop outgoing packet after 1 second */ ifm->expiration_date = qemu_clock_get_ns(QEMU_CLOCK_REALTIME) + 1000000000ULL; } return 0; } else { - memcpy(eh->h_dest, ethaddr, ETH_ALEN); memcpy(eh->h_source, special_ethaddr, ETH_ALEN - 4); /* XXX: not correct */ memcpy(&eh->h_source[2], &slirp->vhost_addr, 4); eh->h_proto = htons(ETH_P_IP); - memcpy(buf + sizeof(struct ethhdr), ifm->m_data, ifm->m_len); - slirp_output(slirp->opaque, buf, ifm->m_len + ETH_HLEN); + + /* Send this */ + return 2; + } +} + +/* Output the IP packet to the ethernet device. Returns 0 if the packet must be + * re-queued. + */ +int if_encap(Slirp *slirp, struct mbuf *ifm) +{ + uint8_t buf[1600]; + struct ethhdr *eh = (struct ethhdr *)buf; + uint8_t ethaddr[ETH_ALEN]; + const struct ip *iph = (const struct ip *)ifm->m_data; + int ret; + + if (ifm->m_len + ETH_HLEN > sizeof(buf)) { return 1; } + + switch (iph->ip_v) { + case IPVERSION: + ret = if_encap4(slirp, ifm, eh, ethaddr); + if (ret < 2) { + return ret; + } + break; + + default: + /* Do not assert while we don't manage IP6VERSION */ + /* assert(0); */ + break; + } + + memcpy(eh->h_dest, ethaddr, ETH_ALEN); + DEBUG_ARGS((dfd, " src = %02x:%02x:%02x:%02x:%02x:%02x\n", + eh->h_source[0], eh->h_source[1], eh->h_source[2], + eh->h_source[3], eh->h_source[4], eh->h_source[5])); + DEBUG_ARGS((dfd, " dst = %02x:%02x:%02x:%02x:%02x:%02x\n", + eh->h_dest[0], eh->h_dest[1], eh->h_dest[2], + eh->h_dest[3], eh->h_dest[4], eh->h_dest[5])); + memcpy(buf + sizeof(struct ethhdr), ifm->m_data, ifm->m_len); + slirp_output(slirp->opaque, buf, ifm->m_len + ETH_HLEN); + return 1; } /* Drop host forwarding rule, return 0 if found. */ @@ -1011,10 +1047,26 @@ static void slirp_sbuf_save(QEMUFile *f, struct sbuf *sbuf) static void slirp_socket_save(QEMUFile *f, struct socket *so) { qemu_put_be32(f, so->so_urgc); - qemu_put_be32(f, so->so_faddr.s_addr); - qemu_put_be32(f, so->so_laddr.s_addr); - qemu_put_be16(f, so->so_fport); - qemu_put_be16(f, so->so_lport); + qemu_put_be16(f, so->so_ffamily); + switch (so->so_ffamily) { + case AF_INET: + qemu_put_be32(f, so->so_faddr.s_addr); + qemu_put_be16(f, so->so_fport); + break; + default: + error_report( + "so_ffamily unknown, unable to save so_faddr and so_fport\n"); + } + qemu_put_be16(f, so->so_lfamily); + switch (so->so_lfamily) { + case AF_INET: + qemu_put_be32(f, so->so_laddr.s_addr); + qemu_put_be16(f, so->so_lport); + break; + default: + error_report( + "so_ffamily unknown, unable to save so_laddr and so_lport\n"); + } qemu_put_byte(f, so->so_iptos); qemu_put_byte(f, so->so_emu); qemu_put_byte(f, so->so_type); @@ -1134,10 +1186,26 @@ static int slirp_socket_load(QEMUFile *f, struct socket *so) return -ENOMEM; so->so_urgc = qemu_get_be32(f); - so->so_faddr.s_addr = qemu_get_be32(f); - so->so_laddr.s_addr = qemu_get_be32(f); - so->so_fport = qemu_get_be16(f); - so->so_lport = qemu_get_be16(f); + so->so_ffamily = qemu_get_be16(f); + switch (so->so_ffamily) { + case AF_INET: + so->so_faddr.s_addr = qemu_get_be32(f); + so->so_fport = qemu_get_be16(f); + break; + default: + error_report( + "so_ffamily unknown, unable to restore so_faddr and so_lport\n"); + } + so->so_lfamily = qemu_get_be16(f); + switch (so->so_lfamily) { + case AF_INET: + so->so_laddr.s_addr = qemu_get_be32(f); + so->so_lport = qemu_get_be16(f); + break; + default: + error_report( + "so_ffamily unknown, unable to restore so_laddr and so_lport\n"); + } so->so_iptos = qemu_get_byte(f); so->so_emu = qemu_get_byte(f); so->so_type = qemu_get_byte(f); diff --git a/slirp/slirp.h b/slirp/slirp.h index ec0a4c2415..239fe2917a 100644 --- a/slirp/slirp.h +++ b/slirp/slirp.h @@ -327,7 +327,7 @@ void tcp_respond(struct tcpcb *, register struct tcpiphdr *, register struct mbu struct tcpcb * tcp_newtcpcb(struct socket *); struct tcpcb * tcp_close(register struct tcpcb *); void tcp_sockclosed(struct tcpcb *); -int tcp_fconnect(struct socket *); +int tcp_fconnect(struct socket *, unsigned short af); void tcp_connect(struct socket *); int tcp_attach(struct socket *); uint8_t tcp_tos(struct socket *); diff --git a/slirp/socket.c b/slirp/socket.c index 1673e3afce..f7e596859f 100644 --- a/slirp/socket.c +++ b/slirp/socket.c @@ -15,24 +15,26 @@ static void sofcantrcvmore(struct socket *so); static void sofcantsendmore(struct socket *so); -struct socket * -solookup(struct socket *head, struct in_addr laddr, u_int lport, - struct in_addr faddr, u_int fport) +struct socket *solookup(struct socket **last, struct socket *head, + struct sockaddr_storage *lhost, struct sockaddr_storage *fhost) { - struct socket *so; - - for (so = head->so_next; so != head; so = so->so_next) { - if (so->so_lport == lport && - so->so_laddr.s_addr == laddr.s_addr && - so->so_faddr.s_addr == faddr.s_addr && - so->so_fport == fport) - break; - } - - if (so == head) - return (struct socket *)NULL; - return so; + struct socket *so = *last; + + /* Optimisation */ + if (so != head && sockaddr_equal(&(so->lhost.ss), lhost) + && (!fhost || sockaddr_equal(&so->fhost.ss, fhost))) { + return so; + } + + for (so = head->so_next; so != head; so = so->so_next) { + if (sockaddr_equal(&(so->lhost.ss), lhost) + && (!fhost || sockaddr_equal(&so->fhost.ss, fhost))) { + *last = so; + return so; + } + } + return (struct socket *)NULL; } /* @@ -437,8 +439,9 @@ sowrite(struct socket *so) void sorecvfrom(struct socket *so) { - struct sockaddr_in addr; - socklen_t addrlen = sizeof(struct sockaddr_in); + struct sockaddr_storage addr; + struct sockaddr_storage saddr, daddr; + socklen_t addrlen = sizeof(struct sockaddr_storage); DEBUG_CALL("sorecvfrom"); DEBUG_ARG("so = %p", so); @@ -525,9 +528,21 @@ sorecvfrom(struct socket *so) /* * If this packet was destined for CTL_ADDR, - * make it look like that's where it came from, done by udp_output + * make it look like that's where it came from */ - udp_output(so, m, &addr); + saddr = addr; + sotranslate_in(so, &saddr); + daddr = so->lhost.ss; + + switch (so->so_ffamily) { + case AF_INET: + udp_output(so, m, (struct sockaddr_in *) &saddr, + (struct sockaddr_in *) &daddr, + so->so_iptos); + break; + default: + break; + } } /* rx error */ } /* if ping packet */ } @@ -538,33 +553,20 @@ sorecvfrom(struct socket *so) int sosendto(struct socket *so, struct mbuf *m) { - Slirp *slirp = so->slirp; int ret; - struct sockaddr_in addr; + struct sockaddr_storage addr; DEBUG_CALL("sosendto"); DEBUG_ARG("so = %p", so); DEBUG_ARG("m = %p", m); - addr.sin_family = AF_INET; - if ((so->so_faddr.s_addr & slirp->vnetwork_mask.s_addr) == - slirp->vnetwork_addr.s_addr) { - /* It's an alias */ - if (so->so_faddr.s_addr == slirp->vnameserver_addr.s_addr) { - if (get_dns_addr(&addr.sin_addr) < 0) - addr.sin_addr = loopback_addr; - } else { - addr.sin_addr = loopback_addr; - } - } else - addr.sin_addr = so->so_faddr; - addr.sin_port = so->so_fport; - - DEBUG_MISC((dfd, " sendto()ing, addr.sin_port=%d, addr.sin_addr.s_addr=%.16s\n", ntohs(addr.sin_port), inet_ntoa(addr.sin_addr))); + addr = so->fhost.ss; + DEBUG_CALL(" sendto()ing)"); + sotranslate_out(so, &addr); /* Don't care what port we get */ ret = sendto(so->s, m->m_data, m->m_len, 0, - (struct sockaddr *)&addr, sizeof (struct sockaddr)); + (struct sockaddr *)&addr, sizeof(addr)); if (ret < 0) return -1; @@ -619,6 +621,7 @@ tcp_listen(Slirp *slirp, uint32_t haddr, u_int hport, uint32_t laddr, so->so_state &= SS_PERSISTENT_MASK; so->so_state |= (SS_FACCEPTCONN | flags); + so->so_lfamily = AF_INET; so->so_lport = lport; /* Kept in network format */ so->so_laddr.s_addr = laddr; /* Ditto */ @@ -645,6 +648,7 @@ tcp_listen(Slirp *slirp, uint32_t haddr, u_int hport, uint32_t laddr, qemu_setsockopt(s, SOL_SOCKET, SO_OOBINLINE, &opt, sizeof(int)); getsockname(s,(struct sockaddr *)&addr,&addrlen); + so->so_ffamily = AF_INET; so->so_fport = addr.sin_port; if (addr.sin_addr.s_addr == 0 || addr.sin_addr.s_addr == loopback_addr.s_addr) so->so_faddr = slirp->vhost_addr; @@ -718,3 +722,81 @@ sofwdrain(struct socket *so) else sofcantsendmore(so); } + +/* + * Translate addr in host addr when it is a virtual address + */ +void sotranslate_out(struct socket *so, struct sockaddr_storage *addr) +{ + Slirp *slirp = so->slirp; + struct sockaddr_in *sin = (struct sockaddr_in *)addr; + + switch (addr->ss_family) { + case AF_INET: + if ((so->so_faddr.s_addr & slirp->vnetwork_mask.s_addr) == + slirp->vnetwork_addr.s_addr) { + /* It's an alias */ + if (so->so_faddr.s_addr == slirp->vnameserver_addr.s_addr) { + if (get_dns_addr(&sin->sin_addr) < 0) { + sin->sin_addr = loopback_addr; + } + } else { + sin->sin_addr = loopback_addr; + } + } + + DEBUG_MISC((dfd, " addr.sin_port=%d, " + "addr.sin_addr.s_addr=%.16s\n", + ntohs(sin->sin_port), inet_ntoa(sin->sin_addr))); + break; + + default: + break; + } +} + +void sotranslate_in(struct socket *so, struct sockaddr_storage *addr) +{ + Slirp *slirp = so->slirp; + struct sockaddr_in *sin = (struct sockaddr_in *)addr; + + switch (addr->ss_family) { + case AF_INET: + if ((so->so_faddr.s_addr & slirp->vnetwork_mask.s_addr) == + slirp->vnetwork_addr.s_addr) { + uint32_t inv_mask = ~slirp->vnetwork_mask.s_addr; + + if ((so->so_faddr.s_addr & inv_mask) == inv_mask) { + sin->sin_addr = slirp->vhost_addr; + } else if (sin->sin_addr.s_addr == loopback_addr.s_addr || + so->so_faddr.s_addr != slirp->vhost_addr.s_addr) { + sin->sin_addr = so->so_faddr; + } + } + break; + + default: + break; + } +} + +/* + * Translate connections from localhost to the real hostname + */ +void sotranslate_accept(struct socket *so) +{ + Slirp *slirp = so->slirp; + + switch (so->so_ffamily) { + case AF_INET: + if (so->so_faddr.s_addr == INADDR_ANY || + (so->so_faddr.s_addr & loopback_mask) == + (loopback_addr.s_addr & loopback_mask)) { + so->so_faddr = slirp->vhost_addr; + } + break; + + default: + break; + } +} diff --git a/slirp/socket.h b/slirp/socket.h index 57e0407ebc..c4afc9494f 100644 --- a/slirp/socket.h +++ b/slirp/socket.h @@ -31,10 +31,21 @@ struct socket { struct tcpiphdr *so_ti; /* Pointer to the original ti within * so_mconn, for non-blocking connections */ int so_urgc; - struct in_addr so_faddr; /* foreign host table entry */ - struct in_addr so_laddr; /* local host table entry */ - uint16_t so_fport; /* foreign port */ - uint16_t so_lport; /* local port */ + union { /* foreign host */ + struct sockaddr_storage ss; + struct sockaddr_in sin; + } fhost; +#define so_faddr fhost.sin.sin_addr +#define so_fport fhost.sin.sin_port +#define so_ffamily fhost.ss.ss_family + + union { /* local host */ + struct sockaddr_storage ss; + struct sockaddr_in sin; + } lhost; +#define so_laddr lhost.sin.sin_addr +#define so_lport lhost.sin.sin_port +#define so_lfamily lhost.ss.ss_family uint8_t so_iptos; /* Type of service */ uint8_t so_emu; /* Is the socket emulated? */ @@ -76,8 +87,31 @@ struct socket { #define SS_HOSTFWD 0x1000 /* Socket describes host->guest forwarding */ #define SS_INCOMING 0x2000 /* Connection was initiated by a host on the internet */ -struct socket * solookup(struct socket *, struct in_addr, u_int, struct in_addr, u_int); -struct socket * socreate(Slirp *); +static inline int sockaddr_equal(struct sockaddr_storage *a, + struct sockaddr_storage *b) +{ + if (a->ss_family != b->ss_family) { + return 0; + } + + switch (a->ss_family) { + case AF_INET: + { + struct sockaddr_in *a4 = (struct sockaddr_in *) a; + struct sockaddr_in *b4 = (struct sockaddr_in *) b; + return a4->sin_addr.s_addr == b4->sin_addr.s_addr + && a4->sin_port == b4->sin_port; + } + default: + g_assert_not_reached(); + } + + return 0; +} + +struct socket *solookup(struct socket **, struct socket *, + struct sockaddr_storage *, struct sockaddr_storage *); +struct socket *socreate(Slirp *); void sofree(struct socket *); int soread(struct socket *); void sorecvoob(struct socket *); @@ -94,4 +128,9 @@ struct iovec; /* For win32 */ size_t sopreprbuf(struct socket *so, struct iovec *iov, int *np); int soreadbuf(struct socket *so, const char *buf, int size); +void sotranslate_out(struct socket *, struct sockaddr_storage *); +void sotranslate_in(struct socket *, struct sockaddr_storage *); +void sotranslate_accept(struct socket *); + + #endif /* _SOCKET_H_ */ diff --git a/slirp/tcp_input.c b/slirp/tcp_input.c index 6b096ecb3c..f24e7060a4 100644 --- a/slirp/tcp_input.c +++ b/slirp/tcp_input.c @@ -227,6 +227,8 @@ tcp_input(struct mbuf *m, int iphlen, struct socket *inso) int iss = 0; u_long tiwin; int ret; + struct sockaddr_storage lhost, fhost; + struct sockaddr_in *lhost4, *fhost4; struct ex_list *ex_ptr; Slirp *slirp; @@ -320,16 +322,16 @@ tcp_input(struct mbuf *m, int iphlen, struct socket *inso) * Locate pcb for segment. */ findso: - so = slirp->tcp_last_so; - if (so->so_fport != ti->ti_dport || - so->so_lport != ti->ti_sport || - so->so_laddr.s_addr != ti->ti_src.s_addr || - so->so_faddr.s_addr != ti->ti_dst.s_addr) { - so = solookup(&slirp->tcb, ti->ti_src, ti->ti_sport, - ti->ti_dst, ti->ti_dport); - if (so) - slirp->tcp_last_so = so; - } + lhost.ss_family = AF_INET; + lhost4 = (struct sockaddr_in *) &lhost; + lhost4->sin_addr = ti->ti_src; + lhost4->sin_port = ti->ti_sport; + fhost.ss_family = AF_INET; + fhost4 = (struct sockaddr_in *) &fhost; + fhost4->sin_addr = ti->ti_dst; + fhost4->sin_port = ti->ti_dport; + + so = solookup(&slirp->tcp_last_so, &slirp->tcb, &lhost, &fhost); /* * If the state is CLOSED (i.e., TCB does not exist) then @@ -374,10 +376,8 @@ findso: sbreserve(&so->so_snd, TCP_SNDSPACE); sbreserve(&so->so_rcv, TCP_RCVSPACE); - so->so_laddr = ti->ti_src; - so->so_lport = ti->ti_sport; - so->so_faddr = ti->ti_dst; - so->so_fport = ti->ti_dport; + so->lhost.ss = lhost; + so->fhost.ss = fhost; if ((so->so_iptos = tcp_tos(so)) == 0) so->so_iptos = ((struct ip *)ti)->ip_tos; @@ -584,7 +584,7 @@ findso: goto cont_input; } - if ((tcp_fconnect(so) == -1) && + if ((tcp_fconnect(so, so->so_ffamily) == -1) && #if defined(_WIN32) socket_error() != WSAEWOULDBLOCK #else diff --git a/slirp/tcp_subr.c b/slirp/tcp_subr.c index e161ed2a96..36e325618d 100644 --- a/slirp/tcp_subr.c +++ b/slirp/tcp_subr.c @@ -324,40 +324,27 @@ tcp_sockclosed(struct tcpcb *tp) * nonblocking. Connect returns after the SYN is sent, and does * not wait for ACK+SYN. */ -int tcp_fconnect(struct socket *so) +int tcp_fconnect(struct socket *so, unsigned short af) { - Slirp *slirp = so->slirp; int ret=0; DEBUG_CALL("tcp_fconnect"); DEBUG_ARG("so = %p", so); - if( (ret = so->s = qemu_socket(AF_INET,SOCK_STREAM,0)) >= 0) { + ret = so->s = qemu_socket(af, SOCK_STREAM, 0); + if (ret >= 0) { int opt, s=so->s; - struct sockaddr_in addr; + struct sockaddr_storage addr; qemu_set_nonblock(s); socket_set_fast_reuse(s); opt = 1; qemu_setsockopt(s, SOL_SOCKET, SO_OOBINLINE, &opt, sizeof(opt)); - addr.sin_family = AF_INET; - if ((so->so_faddr.s_addr & slirp->vnetwork_mask.s_addr) == - slirp->vnetwork_addr.s_addr) { - /* It's an alias */ - if (so->so_faddr.s_addr == slirp->vnameserver_addr.s_addr) { - if (get_dns_addr(&addr.sin_addr) < 0) - addr.sin_addr = loopback_addr; - } else { - addr.sin_addr = loopback_addr; - } - } else - addr.sin_addr = so->so_faddr; - addr.sin_port = so->so_fport; - - DEBUG_MISC((dfd, " connect()ing, addr.sin_port=%d, " - "addr.sin_addr.s_addr=%.16s\n", - ntohs(addr.sin_port), inet_ntoa(addr.sin_addr))); + addr = so->fhost.ss; + DEBUG_CALL(" connect()ing") + sotranslate_out(so, &addr); + /* We don't care what port we get */ ret = connect(s,(struct sockaddr *)&addr,sizeof (addr)); @@ -413,6 +400,7 @@ void tcp_connect(struct socket *inso) free(so); /* NOT sofree */ return; } + so->so_lfamily = AF_INET; so->so_laddr = inso->so_laddr; so->so_lport = inso->so_lport; } @@ -430,14 +418,8 @@ void tcp_connect(struct socket *inso) qemu_setsockopt(s, SOL_SOCKET, SO_OOBINLINE, &opt, sizeof(int)); socket_set_nodelay(s); - so->so_fport = addr.sin_port; - so->so_faddr = addr.sin_addr; - /* Translate connections from localhost to the real hostname */ - if (so->so_faddr.s_addr == 0 || - (so->so_faddr.s_addr & loopback_mask) == - (loopback_addr.s_addr & loopback_mask)) { - so->so_faddr = slirp->vhost_addr; - } + so->fhost.sin = addr; + sotranslate_accept(so); /* Close the accept() socket, set right state */ if (inso->so_state & SS_FACCEPTONCE) { diff --git a/slirp/tftp.c b/slirp/tftp.c index a329fb281b..ccb613014a 100644 --- a/slirp/tftp.c +++ b/slirp/tftp.c @@ -155,7 +155,7 @@ static int tftp_send_oack(struct tftp_session *spt, m->m_len = sizeof(struct tftp_t) - 514 + n - sizeof(struct ip) - sizeof(struct udphdr); - udp_output2(NULL, m, &saddr, &daddr, IPTOS_LOWDELAY); + udp_output(NULL, m, &saddr, &daddr, IPTOS_LOWDELAY); return 0; } @@ -193,7 +193,7 @@ static void tftp_send_error(struct tftp_session *spt, m->m_len = sizeof(struct tftp_t) - 514 + 3 + strlen(msg) - sizeof(struct ip) - sizeof(struct udphdr); - udp_output2(NULL, m, &saddr, &daddr, IPTOS_LOWDELAY); + udp_output(NULL, m, &saddr, &daddr, IPTOS_LOWDELAY); out: tftp_session_terminate(spt); @@ -243,7 +243,7 @@ static void tftp_send_next_block(struct tftp_session *spt, m->m_len = sizeof(struct tftp_t) - (512 - nobytes) - sizeof(struct ip) - sizeof(struct udphdr); - udp_output2(NULL, m, &saddr, &daddr, IPTOS_LOWDELAY); + udp_output(NULL, m, &saddr, &daddr, IPTOS_LOWDELAY); if (nobytes == 512) { tftp_session_update(spt); diff --git a/slirp/udp.c b/slirp/udp.c index fee13b4dbd..92c48c491e 100644 --- a/slirp/udp.c +++ b/slirp/udp.c @@ -70,6 +70,8 @@ udp_input(register struct mbuf *m, int iphlen) int len; struct ip save_ip; struct socket *so; + struct sockaddr_storage lhost; + struct sockaddr_in *lhost4; DEBUG_CALL("udp_input"); DEBUG_ARG("m = %p", m); @@ -151,25 +153,12 @@ udp_input(register struct mbuf *m, int iphlen) /* * Locate pcb for datagram. */ - so = slirp->udp_last_so; - if (so == &slirp->udb || so->so_lport != uh->uh_sport || - so->so_laddr.s_addr != ip->ip_src.s_addr) { - struct socket *tmp; - - for (tmp = slirp->udb.so_next; tmp != &slirp->udb; - tmp = tmp->so_next) { - if (tmp->so_lport == uh->uh_sport && - tmp->so_laddr.s_addr == ip->ip_src.s_addr) { - so = tmp; - break; - } - } - if (tmp == &slirp->udb) { - so = NULL; - } else { - slirp->udp_last_so = so; - } - } + lhost.ss_family = AF_INET; + lhost4 = (struct sockaddr_in *) &lhost; + lhost4->sin_addr = ip->ip_src; + lhost4->sin_port = uh->uh_sport; + + so = solookup(&slirp->udp_last_so, &slirp->udb, &lhost, NULL); if (so == NULL) { /* @@ -180,7 +169,7 @@ udp_input(register struct mbuf *m, int iphlen) if (!so) { goto bad; } - if(udp_attach(so) == -1) { + if (udp_attach(so, AF_INET) == -1) { DEBUG_MISC((dfd," udp_attach errno = %d-%s\n", errno,strerror(errno))); sofree(so); @@ -190,6 +179,7 @@ udp_input(register struct mbuf *m, int iphlen) /* * Setup fields */ + so->so_lfamily = AF_INET; so->so_laddr = ip->ip_src; so->so_lport = uh->uh_sport; @@ -202,6 +192,7 @@ udp_input(register struct mbuf *m, int iphlen) */ } + so->so_ffamily = AF_INET; so->so_faddr = ip->ip_dst; /* XXX */ so->so_fport = uh->uh_dport; /* XXX */ @@ -218,6 +209,7 @@ udp_input(register struct mbuf *m, int iphlen) *ip=save_ip; DEBUG_MISC((dfd,"udp tx errno = %d-%s\n",errno,strerror(errno))); icmp_error(m, ICMP_UNREACH,ICMP_UNREACH_NET, 0,strerror(errno)); + goto bad; } m_free(so->so_m); /* used for ICMP if error on sorecvfrom */ @@ -233,7 +225,7 @@ bad: m_free(m); } -int udp_output2(struct socket *so, struct mbuf *m, +int udp_output(struct socket *so, struct mbuf *m, struct sockaddr_in *saddr, struct sockaddr_in *daddr, int iptos) { @@ -284,35 +276,11 @@ int udp_output2(struct socket *so, struct mbuf *m, return (error); } -int udp_output(struct socket *so, struct mbuf *m, - struct sockaddr_in *addr) - -{ - Slirp *slirp = so->slirp; - struct sockaddr_in saddr, daddr; - - saddr = *addr; - if ((so->so_faddr.s_addr & slirp->vnetwork_mask.s_addr) == - slirp->vnetwork_addr.s_addr) { - uint32_t inv_mask = ~slirp->vnetwork_mask.s_addr; - - if ((so->so_faddr.s_addr & inv_mask) == inv_mask) { - saddr.sin_addr = slirp->vhost_addr; - } else if (addr->sin_addr.s_addr == loopback_addr.s_addr || - so->so_faddr.s_addr != slirp->vhost_addr.s_addr) { - saddr.sin_addr = so->so_faddr; - } - } - daddr.sin_addr = so->so_laddr; - daddr.sin_port = so->so_lport; - - return udp_output2(so, m, &saddr, &daddr, so->so_iptos); -} - int -udp_attach(struct socket *so) +udp_attach(struct socket *so, unsigned short af) { - if((so->s = qemu_socket(AF_INET,SOCK_DGRAM,0)) != -1) { + so->s = qemu_socket(af, SOCK_DGRAM, 0); + if (so->s != -1) { so->so_expire = curtime + SO_EXPIRE; insque(so, &so->slirp->udb); } @@ -375,13 +343,9 @@ udp_listen(Slirp *slirp, uint32_t haddr, u_int hport, uint32_t laddr, socket_set_fast_reuse(so->s); getsockname(so->s,(struct sockaddr *)&addr,&addrlen); - so->so_fport = addr.sin_port; - if (addr.sin_addr.s_addr == 0 || - addr.sin_addr.s_addr == loopback_addr.s_addr) { - so->so_faddr = slirp->vhost_addr; - } else { - so->so_faddr = addr.sin_addr; - } + so->fhost.sin = addr; + sotranslate_accept(so); + so->so_lfamily = AF_INET; so->so_lport = lport; so->so_laddr.s_addr = laddr; if (flags != SS_FACCEPTONCE) diff --git a/slirp/udp.h b/slirp/udp.h index 9bf31fe7be..2f9de3886c 100644 --- a/slirp/udp.h +++ b/slirp/udp.h @@ -76,12 +76,11 @@ struct mbuf; void udp_init(Slirp *); void udp_cleanup(Slirp *); void udp_input(register struct mbuf *, int); -int udp_output(struct socket *, struct mbuf *, struct sockaddr_in *); -int udp_attach(struct socket *); +int udp_attach(struct socket *, unsigned short af); void udp_detach(struct socket *); struct socket * udp_listen(Slirp *, uint32_t, u_int, uint32_t, u_int, int); -int udp_output2(struct socket *so, struct mbuf *m, +int udp_output(struct socket *so, struct mbuf *m, struct sockaddr_in *saddr, struct sockaddr_in *daddr, int iptos); #endif 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 { @@ -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 } @@ -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; @@ -3308,12 +3311,18 @@ int main(int argc, char **argv, char **envp) #endif #ifdef CONFIG_SLIRP case QEMU_OPTION_tftp: + error_report("The -tftp option is deprecated. " + "Please use '-netdev user,tftp=...' instead."); legacy_tftp_prefix = optarg; break; case QEMU_OPTION_bootp: + error_report("The -bootp option is deprecated. " + "Please use '-netdev user,bootfile=...' instead."); legacy_bootp_filename = optarg; break; case QEMU_OPTION_redir: + error_report("The -redir option is deprecated. " + "Please use '-netdev user,hostfwd=...' instead."); if (net_slirp_redir(optarg) < 0) exit(1); break; @@ -3901,12 +3910,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 +4105,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 +4121,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 +4670,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(); |