diff options
147 files changed, 4751 insertions, 872 deletions
diff --git a/.gitignore b/.gitignore index 5584b5fcb0..1c9d63d651 100644 --- a/.gitignore +++ b/.gitignore @@ -3,6 +3,7 @@ config-all-devices.* config-all-disas.* config-host.* config-target.* +config.status trace/generated-tracers.h trace/generated-tracers.c trace/generated-tracers-dtrace.h diff --git a/MAINTAINERS b/MAINTAINERS index 3e61ac839c..7eed206101 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -710,6 +710,14 @@ S: Maintained F: net/ T: git git://github.com/stefanha/qemu.git net +Netmap network backend +M: Luigi Rizzo <rizzo@iet.unipi.it> +M: Giuseppe Lettieri <g.lettieri@iet.unipi.it> +M: Vincenzo Maffione <v.maffione@gmail.com> +W: http://info.iet.unipi.it/~luigi/netmap/ +S: Maintained +F: net/netmap.c + Network Block Device (NBD) M: Paolo Bonzini <pbonzini@redhat.com> S: Odd Fixes diff --git a/audio/audio.c b/audio/audio.c index b3db67979d..fc775110af 100644 --- a/audio/audio.c +++ b/audio/audio.c @@ -95,7 +95,7 @@ static struct { } }, - .period = { .hertz = 250 }, + .period = { .hertz = 100 }, .plive = 0, .log_to_monitor = 0, .try_poll_in = 1, diff --git a/audio/paaudio.c b/audio/paaudio.c index 8b69778ad9..90ff24500b 100644 --- a/audio/paaudio.c +++ b/audio/paaudio.c @@ -547,11 +547,11 @@ static int qpa_init_out (HWVoiceOut *hw, struct audsettings *as) ss.rate = as->freq; /* - * qemu audio tick runs at 250 Hz (by default), so processing - * data chunks worth 4 ms of sound should be a good fit. + * qemu audio tick runs at 100 Hz (by default), so processing + * data chunks worth 10 ms of sound should be a good fit. */ - ba.tlength = pa_usec_to_bytes (4 * 1000, &ss); - ba.minreq = pa_usec_to_bytes (2 * 1000, &ss); + ba.tlength = pa_usec_to_bytes (10 * 1000, &ss); + ba.minreq = pa_usec_to_bytes (5 * 1000, &ss); ba.maxlength = -1; ba.prebuf = -1; @@ -169,6 +169,7 @@ curl="" curses="" docs="" fdt="" +netmap="no" pixman="" sdl="" virtfs="" @@ -488,6 +489,7 @@ FreeBSD) audio_possible_drivers="oss sdl esd pa" # needed for kinfo_getvmmap(3) in libutil.h LIBS="-lutil $LIBS" + netmap="" # enable netmap autodetect ;; DragonFly) bsd="yes" @@ -797,6 +799,10 @@ for opt do ;; --enable-vde) vde="yes" ;; + --disable-netmap) netmap="no" + ;; + --enable-netmap) netmap="yes" + ;; --disable-xen) xen="no" ;; --enable-xen) xen="yes" @@ -1182,6 +1188,8 @@ echo " --disable-uuid disable uuid support" echo " --enable-uuid enable uuid support" echo " --disable-vde disable support for vde network" echo " --enable-vde enable support for vde network" +echo " --disable-netmap disable support for netmap network" +echo " --enable-netmap enable support for netmap network" echo " --disable-linux-aio disable Linux AIO support" echo " --enable-linux-aio enable Linux AIO support" echo " --disable-cap-ng disable libcap-ng support" @@ -2095,6 +2103,26 @@ EOF fi ########################################## +# netmap headers probe +if test "$netmap" != "no" ; then + cat > $TMPC << EOF +#include <inttypes.h> +#include <net/if.h> +#include <net/netmap.h> +#include <net/netmap_user.h> +int main(void) { return 0; } +EOF + if compile_prog "" "" ; then + netmap=yes + else + if test "$netmap" = "yes" ; then + feature_not_found "netmap" + fi + netmap=no + fi +fi + +########################################## # libcap-ng library probe if test "$cap_ng" != "no" ; then cap_libs="-lcap-ng" @@ -3751,6 +3779,7 @@ echo "uname -r $uname_release" echo "GUEST_BASE $guest_base" echo "PIE $pie" echo "vde support $vde" +echo "netmap support $netmap" echo "Linux AIO support $linux_aio" echo "ATTR/XATTR support $attr" echo "Install blobs $blobs" @@ -3888,6 +3917,9 @@ fi if test "$vde" = "yes" ; then echo "CONFIG_VDE=y" >> $config_host_mak fi +if test "$netmap" = "yes" ; then + echo "CONFIG_NETMAP=y" >> $config_host_mak +fi if test "$cap_ng" = "yes" ; then echo "CONFIG_LIBCAP=y" >> $config_host_mak fi diff --git a/default-configs/arm-softmmu.mak b/default-configs/arm-softmmu.mak index a555eefed5..e48f102af6 100644 --- a/default-configs/arm-softmmu.mak +++ b/default-configs/arm-softmmu.mak @@ -41,6 +41,7 @@ CONFIG_ARM_GIC=y CONFIG_ARM_GIC_KVM=$(CONFIG_KVM) CONFIG_ARM_TIMER=y CONFIG_ARM_MPTIMER=y +CONFIG_A9_GTIMER=y CONFIG_PL011=y CONFIG_PL022=y CONFIG_PL031=y diff --git a/device_tree.c b/device_tree.c index ffec99ae29..391da8c45e 100644 --- a/device_tree.c +++ b/device_tree.c @@ -41,6 +41,10 @@ void *create_device_tree(int *sizep) if (ret < 0) { goto fail; } + ret = fdt_finish_reservemap(fdt); + if (ret < 0) { + goto fail; + } ret = fdt_begin_node(fdt, ""); if (ret < 0) { goto fail; @@ -904,6 +904,13 @@ static long gethugepagesize(const char *path) return fs.f_bsize; } +static sigjmp_buf sigjump; + +static void sigbus_handler(int signal) +{ + siglongjmp(sigjump, 1); +} + static void *file_ram_alloc(RAMBlock *block, ram_addr_t memory, const char *path) @@ -913,9 +920,6 @@ static void *file_ram_alloc(RAMBlock *block, char *c; void *area; int fd; -#ifdef MAP_POPULATE - int flags; -#endif unsigned long hpagesize; hpagesize = gethugepagesize(path); @@ -963,21 +967,52 @@ static void *file_ram_alloc(RAMBlock *block, if (ftruncate(fd, memory)) perror("ftruncate"); -#ifdef MAP_POPULATE - /* NB: MAP_POPULATE won't exhaustively alloc all phys pages in the case - * MAP_PRIVATE is requested. For mem_prealloc we mmap as MAP_SHARED - * to sidestep this quirk. - */ - flags = mem_prealloc ? MAP_POPULATE | MAP_SHARED : MAP_PRIVATE; - area = mmap(0, memory, PROT_READ | PROT_WRITE, flags, fd, 0); -#else area = mmap(0, memory, PROT_READ | PROT_WRITE, MAP_PRIVATE, fd, 0); -#endif if (area == MAP_FAILED) { perror("file_ram_alloc: can't mmap RAM pages"); close(fd); return (NULL); } + + if (mem_prealloc) { + int ret, i; + struct sigaction act, oldact; + sigset_t set, oldset; + + memset(&act, 0, sizeof(act)); + act.sa_handler = &sigbus_handler; + act.sa_flags = 0; + + ret = sigaction(SIGBUS, &act, &oldact); + if (ret) { + perror("file_ram_alloc: failed to install signal handler"); + exit(1); + } + + /* unblock SIGBUS */ + sigemptyset(&set); + sigaddset(&set, SIGBUS); + pthread_sigmask(SIG_UNBLOCK, &set, &oldset); + + if (sigsetjmp(sigjump, 1)) { + fprintf(stderr, "file_ram_alloc: failed to preallocate pages\n"); + exit(1); + } + + /* MAP_POPULATE silently ignores failures */ + for (i = 0; i < (memory/hpagesize)-1; i++) { + memset(area + (hpagesize*i), 0, 1); + } + + ret = sigaction(SIGBUS, &oldact, NULL); + if (ret) { + perror("file_ram_alloc: failed to reinstall signal handler"); + exit(1); + } + + pthread_sigmask(SIG_SETMASK, &oldset, NULL); + } + block->fd = fd; return area; } diff --git a/fpu/softfloat.c b/fpu/softfloat.c index 7ba51b6f3c..dbda61bc8e 100644 --- a/fpu/softfloat.c +++ b/fpu/softfloat.c @@ -6705,10 +6705,17 @@ int float128_compare_quiet( float128 a, float128 b STATUS_PARAM ) /* min() and max() functions. These can't be implemented as * 'compare and pick one input' because that would mishandle * NaNs and +0 vs -0. + * + * minnum() and maxnum() functions. These are similar to the min() + * and max() functions but if one of the arguments is a QNaN and + * the other is numerical then the numerical argument is returned. + * minnum() and maxnum correspond to the IEEE 754-2008 minNum() + * and maxNum() operations. min() and max() are the typical min/max + * semantics provided by many CPUs which predate that specification. */ -#define MINMAX(s, nan_exp) \ +#define MINMAX(s) \ INLINE float ## s float ## s ## _minmax(float ## s a, float ## s b, \ - int ismin STATUS_PARAM ) \ + int ismin, int isieee STATUS_PARAM) \ { \ flag aSign, bSign; \ uint ## s ## _t av, bv; \ @@ -6716,6 +6723,15 @@ INLINE float ## s float ## s ## _minmax(float ## s a, float ## s b, \ b = float ## s ## _squash_input_denormal(b STATUS_VAR); \ if (float ## s ## _is_any_nan(a) || \ float ## s ## _is_any_nan(b)) { \ + if (isieee) { \ + if (float ## s ## _is_quiet_nan(a) && \ + !float ## s ##_is_any_nan(b)) { \ + return b; \ + } else if (float ## s ## _is_quiet_nan(b) && \ + !float ## s ## _is_any_nan(a)) { \ + return a; \ + } \ + } \ return propagateFloat ## s ## NaN(a, b STATUS_VAR); \ } \ aSign = extractFloat ## s ## Sign(a); \ @@ -6739,16 +6755,26 @@ INLINE float ## s float ## s ## _minmax(float ## s a, float ## s b, \ \ float ## s float ## s ## _min(float ## s a, float ## s b STATUS_PARAM) \ { \ - return float ## s ## _minmax(a, b, 1 STATUS_VAR); \ + return float ## s ## _minmax(a, b, 1, 0 STATUS_VAR); \ } \ \ float ## s float ## s ## _max(float ## s a, float ## s b STATUS_PARAM) \ { \ - return float ## s ## _minmax(a, b, 0 STATUS_VAR); \ + return float ## s ## _minmax(a, b, 0, 0 STATUS_VAR); \ +} \ + \ +float ## s float ## s ## _minnum(float ## s a, float ## s b STATUS_PARAM) \ +{ \ + return float ## s ## _minmax(a, b, 1, 1 STATUS_VAR); \ +} \ + \ +float ## s float ## s ## _maxnum(float ## s a, float ## s b STATUS_PARAM) \ +{ \ + return float ## s ## _minmax(a, b, 0, 1 STATUS_VAR); \ } -MINMAX(32, 0xff) -MINMAX(64, 0x7ff) +MINMAX(32) +MINMAX(64) /* Multiply A by 2 raised to the power N. */ diff --git a/hmp-commands.hx b/hmp-commands.hx index caae5ad9e9..ebe8e78bb9 100644 --- a/hmp-commands.hx +++ b/hmp-commands.hx @@ -1190,7 +1190,7 @@ ETEXI { .name = "host_net_add", .args_type = "device:s,opts:s?", - .params = "tap|user|socket|vde|dump [options]", + .params = "tap|user|socket|vde|netmap|dump [options]", .help = "add host VLAN client", .mhandler.cmd = net_host_device_add, }, @@ -1218,7 +1218,7 @@ ETEXI { .name = "netdev_add", .args_type = "netdev:O", - .params = "[user|tap|socket|hubport],id=str[,prop=value][,...]", + .params = "[user|tap|socket|hubport|netmap],id=str[,prop=value][,...]", .help = "add host network device", .mhandler.cmd = hmp_netdev_add, }, diff --git a/hw/arm/Makefile.objs b/hw/arm/Makefile.objs index 3671b42738..78b56149b6 100644 --- a/hw/arm/Makefile.objs +++ b/hw/arm/Makefile.objs @@ -1,7 +1,7 @@ obj-y += boot.o collie.o exynos4_boards.o gumstix.o highbank.o obj-y += integratorcp.o kzm.o mainstone.o musicpal.o nseries.o obj-y += omap_sx1.o palm.o realview.o spitz.o stellaris.o -obj-y += tosa.o versatilepb.o vexpress.o xilinx_zynq.o z2.o +obj-y += tosa.o versatilepb.o vexpress.o virt.o xilinx_zynq.o z2.o obj-y += armv7m.o exynos4210.o pxa2xx.o pxa2xx_gpio.o pxa2xx_pic.o obj-y += omap1.o omap2.o strongarm.o diff --git a/hw/arm/boot.c b/hw/arm/boot.c index 583ec7992e..55d552f3a8 100644 --- a/hw/arm/boot.c +++ b/hw/arm/boot.c @@ -228,23 +228,31 @@ static void set_kernel_args_old(const struct arm_boot_info *info) static int load_dtb(hwaddr addr, const struct arm_boot_info *binfo) { void *fdt = NULL; - char *filename; int size, rc; uint32_t acells, scells; - filename = qemu_find_file(QEMU_FILE_TYPE_BIOS, binfo->dtb_filename); - if (!filename) { - fprintf(stderr, "Couldn't open dtb file %s\n", binfo->dtb_filename); - goto fail; - } + if (binfo->dtb_filename) { + char *filename; + filename = qemu_find_file(QEMU_FILE_TYPE_BIOS, binfo->dtb_filename); + if (!filename) { + fprintf(stderr, "Couldn't open dtb file %s\n", binfo->dtb_filename); + goto fail; + } - fdt = load_device_tree(filename, &size); - if (!fdt) { - fprintf(stderr, "Couldn't open dtb file %s\n", filename); + fdt = load_device_tree(filename, &size); + if (!fdt) { + fprintf(stderr, "Couldn't open dtb file %s\n", filename); + g_free(filename); + goto fail; + } g_free(filename); - goto fail; + } else if (binfo->get_dtb) { + fdt = binfo->get_dtb(binfo, &size); + if (!fdt) { + fprintf(stderr, "Board was unable to create a dtb blob\n"); + goto fail; + } } - g_free(filename); acells = qemu_devtree_getprop_cell(fdt, "/", "#address-cells"); scells = qemu_devtree_getprop_cell(fdt, "/", "#size-cells"); @@ -438,7 +446,7 @@ void arm_load_kernel(ARMCPU *cpu, struct arm_boot_info *info) /* for device tree boot, we pass the DTB directly in r2. Otherwise * we point to the kernel args. */ - if (info->dtb_filename) { + if (info->dtb_filename || info->get_dtb) { /* Place the DTB after the initrd in memory. Note that some * kernels will trash anything in the 4K page the initrd * ends in, so make sure the DTB isn't caught up in that. diff --git a/hw/arm/integratorcp.c b/hw/arm/integratorcp.c index c44b2a499c..a759689b44 100644 --- a/hw/arm/integratorcp.c +++ b/hw/arm/integratorcp.c @@ -36,6 +36,7 @@ typedef struct IntegratorCMState { uint32_t cm_init; uint32_t cm_flags; uint32_t cm_nvflags; + uint32_t cm_refcnt_offset; uint32_t int_level; uint32_t irq_enabled; uint32_t fiq_enabled; @@ -82,9 +83,13 @@ static uint64_t integratorcm_read(void *opaque, hwaddr offset, return s->cm_sdram; case 9: /* CM_INIT */ return s->cm_init; - case 10: /* CM_REFCT */ - /* ??? High frequency timer. */ - hw_error("integratorcm_read: CM_REFCT"); + case 10: /* CM_REFCNT */ + /* This register, CM_REFCNT, provides a 32-bit count value. + * The count increments at the fixed reference clock frequency of 24MHz + * and can be used as a real-time counter. + */ + return (uint32_t)muldiv64(qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL), 24, + 1000) - s->cm_refcnt_offset; case 12: /* CM_FLAGS */ return s->cm_flags; case 14: /* CM_NVFLAGS */ @@ -257,6 +262,8 @@ static int integratorcm_init(SysBusDevice *dev) } memcpy(integrator_spd + 73, "QEMU-MEMORY", 11); s->cm_init = 0x00000112; + s->cm_refcnt_offset = muldiv64(qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL), 24, + 1000); memory_region_init_ram(&s->flash, OBJECT(s), "integrator.flash", 0x100000); vmstate_register_ram_global(&s->flash); diff --git a/hw/arm/virt.c b/hw/arm/virt.c new file mode 100644 index 0000000000..9531b5a574 --- /dev/null +++ b/hw/arm/virt.c @@ -0,0 +1,452 @@ +/* + * ARM mach-virt emulation + * + * Copyright (c) 2013 Linaro Limited + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2 or later, as published by the Free Software Foundation. + * + * This program is distributed in the hope 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, see <http://www.gnu.org/licenses/>. + * + * Emulate a virtual board which works by passing Linux all the information + * it needs about what devices are present via the device tree. + * There are some restrictions about what we can do here: + * + we can only present devices whose Linux drivers will work based + * purely on the device tree with no platform data at all + * + we want to present a very stripped-down minimalist platform, + * both because this reduces the security attack surface from the guest + * and also because it reduces our exposure to being broken when + * the kernel updates its device tree bindings and requires further + * information in a device binding that we aren't providing. + * This is essentially the same approach kvmtool uses. + */ + +#include "hw/sysbus.h" +#include "hw/arm/arm.h" +#include "hw/arm/primecell.h" +#include "hw/devices.h" +#include "net/net.h" +#include "sysemu/device_tree.h" +#include "sysemu/sysemu.h" +#include "sysemu/kvm.h" +#include "hw/boards.h" +#include "exec/address-spaces.h" +#include "qemu/bitops.h" +#include "qemu/error-report.h" + +#define NUM_VIRTIO_TRANSPORTS 32 + +/* Number of external interrupt lines to configure the GIC with */ +#define NUM_IRQS 128 + +#define GIC_FDT_IRQ_TYPE_SPI 0 +#define GIC_FDT_IRQ_TYPE_PPI 1 + +#define GIC_FDT_IRQ_FLAGS_EDGE_LO_HI 1 +#define GIC_FDT_IRQ_FLAGS_EDGE_HI_LO 2 +#define GIC_FDT_IRQ_FLAGS_LEVEL_HI 4 +#define GIC_FDT_IRQ_FLAGS_LEVEL_LO 8 + +#define GIC_FDT_IRQ_PPI_CPU_START 8 +#define GIC_FDT_IRQ_PPI_CPU_WIDTH 8 + +enum { + VIRT_FLASH, + VIRT_MEM, + VIRT_CPUPERIPHS, + VIRT_GIC_DIST, + VIRT_GIC_CPU, + VIRT_UART, + VIRT_MMIO, +}; + +typedef struct MemMapEntry { + hwaddr base; + hwaddr size; +} MemMapEntry; + +typedef struct VirtBoardInfo { + struct arm_boot_info bootinfo; + const char *cpu_model; + const char *qdevname; + const char *gic_compatible; + const MemMapEntry *memmap; + const int *irqmap; + int smp_cpus; + void *fdt; + int fdt_size; + uint32_t clock_phandle; +} VirtBoardInfo; + +/* Addresses and sizes of our components. + * 0..128MB is space for a flash device so we can run bootrom code such as UEFI. + * 128MB..256MB is used for miscellaneous device I/O. + * 256MB..1GB is reserved for possible future PCI support (ie where the + * PCI memory window will go if we add a PCI host controller). + * 1GB and up is RAM (which may happily spill over into the + * high memory region beyond 4GB). + * This represents a compromise between how much RAM can be given to + * a 32 bit VM and leaving space for expansion and in particular for PCI. + */ +static const MemMapEntry a15memmap[] = { + /* Space up to 0x8000000 is reserved for a boot ROM */ + [VIRT_FLASH] = { 0, 0x8000000 }, + [VIRT_CPUPERIPHS] = { 0x8000000, 0x8000 }, + /* GIC distributor and CPU interfaces sit inside the CPU peripheral space */ + [VIRT_GIC_DIST] = { 0x8001000, 0x1000 }, + [VIRT_GIC_CPU] = { 0x8002000, 0x1000 }, + [VIRT_UART] = { 0x9000000, 0x1000 }, + [VIRT_MMIO] = { 0xa000000, 0x200 }, + /* ...repeating for a total of NUM_VIRTIO_TRANSPORTS, each of that size */ + /* 0x10000000 .. 0x40000000 reserved for PCI */ + [VIRT_MEM] = { 0x40000000, 30ULL * 1024 * 1024 * 1024 }, +}; + +static const int a15irqmap[] = { + [VIRT_UART] = 1, + [VIRT_MMIO] = 16, /* ...to 16 + NUM_VIRTIO_TRANSPORTS - 1 */ +}; + +static VirtBoardInfo machines[] = { + { + .cpu_model = "cortex-a15", + .qdevname = "a15mpcore_priv", + .gic_compatible = "arm,cortex-a15-gic", + .memmap = a15memmap, + .irqmap = a15irqmap, + }, + { + .cpu_model = "host", + /* We use the A15 private peripheral model to get a V2 GIC */ + .qdevname = "a15mpcore_priv", + .gic_compatible = "arm,cortex-a15-gic", + .memmap = a15memmap, + .irqmap = a15irqmap, + }, +}; + +static VirtBoardInfo *find_machine_info(const char *cpu) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(machines); i++) { + if (strcmp(cpu, machines[i].cpu_model) == 0) { + return &machines[i]; + } + } + return NULL; +} + +static void create_fdt(VirtBoardInfo *vbi) +{ + void *fdt = create_device_tree(&vbi->fdt_size); + + if (!fdt) { + error_report("create_device_tree() failed"); + exit(1); + } + + vbi->fdt = fdt; + + /* Header */ + qemu_devtree_setprop_string(fdt, "/", "compatible", "linux,dummy-virt"); + qemu_devtree_setprop_cell(fdt, "/", "#address-cells", 0x2); + qemu_devtree_setprop_cell(fdt, "/", "#size-cells", 0x2); + + /* + * /chosen and /memory nodes must exist for load_dtb + * to fill in necessary properties later + */ + qemu_devtree_add_subnode(fdt, "/chosen"); + qemu_devtree_add_subnode(fdt, "/memory"); + qemu_devtree_setprop_string(fdt, "/memory", "device_type", "memory"); + + /* Clock node, for the benefit of the UART. The kernel device tree + * binding documentation claims the PL011 node clock properties are + * optional but in practice if you omit them the kernel refuses to + * probe for the device. + */ + vbi->clock_phandle = qemu_devtree_alloc_phandle(fdt); + qemu_devtree_add_subnode(fdt, "/apb-pclk"); + qemu_devtree_setprop_string(fdt, "/apb-pclk", "compatible", "fixed-clock"); + qemu_devtree_setprop_cell(fdt, "/apb-pclk", "#clock-cells", 0x0); + qemu_devtree_setprop_cell(fdt, "/apb-pclk", "clock-frequency", 24000000); + qemu_devtree_setprop_string(fdt, "/apb-pclk", "clock-output-names", + "clk24mhz"); + qemu_devtree_setprop_cell(fdt, "/apb-pclk", "phandle", vbi->clock_phandle); + + /* No PSCI for TCG yet */ + if (kvm_enabled()) { + qemu_devtree_add_subnode(fdt, "/psci"); + qemu_devtree_setprop_string(fdt, "/psci", "compatible", "arm,psci"); + qemu_devtree_setprop_string(fdt, "/psci", "method", "hvc"); + qemu_devtree_setprop_cell(fdt, "/psci", "cpu_suspend", + PSCI_FN_CPU_SUSPEND); + qemu_devtree_setprop_cell(fdt, "/psci", "cpu_off", PSCI_FN_CPU_OFF); + qemu_devtree_setprop_cell(fdt, "/psci", "cpu_on", PSCI_FN_CPU_ON); + qemu_devtree_setprop_cell(fdt, "/psci", "migrate", PSCI_FN_MIGRATE); + } +} + +static void fdt_add_timer_nodes(const VirtBoardInfo *vbi) +{ + /* Note that on A15 h/w these interrupts are level-triggered, + * but for the GIC implementation provided by both QEMU and KVM + * they are edge-triggered. + */ + uint32_t irqflags = GIC_FDT_IRQ_FLAGS_EDGE_LO_HI; + + irqflags = deposit32(irqflags, GIC_FDT_IRQ_PPI_CPU_START, + GIC_FDT_IRQ_PPI_CPU_WIDTH, (1 << vbi->smp_cpus) - 1); + + qemu_devtree_add_subnode(vbi->fdt, "/timer"); + qemu_devtree_setprop_string(vbi->fdt, "/timer", + "compatible", "arm,armv7-timer"); + qemu_devtree_setprop_cells(vbi->fdt, "/timer", "interrupts", + GIC_FDT_IRQ_TYPE_PPI, 13, irqflags, + GIC_FDT_IRQ_TYPE_PPI, 14, irqflags, + GIC_FDT_IRQ_TYPE_PPI, 11, irqflags, + GIC_FDT_IRQ_TYPE_PPI, 10, irqflags); +} + +static void fdt_add_cpu_nodes(const VirtBoardInfo *vbi) +{ + int cpu; + + qemu_devtree_add_subnode(vbi->fdt, "/cpus"); + qemu_devtree_setprop_cell(vbi->fdt, "/cpus", "#address-cells", 0x1); + qemu_devtree_setprop_cell(vbi->fdt, "/cpus", "#size-cells", 0x0); + + for (cpu = vbi->smp_cpus - 1; cpu >= 0; cpu--) { + char *nodename = g_strdup_printf("/cpus/cpu@%d", cpu); + ARMCPU *armcpu = ARM_CPU(qemu_get_cpu(cpu)); + + qemu_devtree_add_subnode(vbi->fdt, nodename); + qemu_devtree_setprop_string(vbi->fdt, nodename, "device_type", "cpu"); + qemu_devtree_setprop_string(vbi->fdt, nodename, "compatible", + armcpu->dtb_compatible); + + if (vbi->smp_cpus > 1) { + qemu_devtree_setprop_string(vbi->fdt, nodename, + "enable-method", "psci"); + } + + qemu_devtree_setprop_cell(vbi->fdt, nodename, "reg", cpu); + g_free(nodename); + } +} + +static void fdt_add_gic_node(const VirtBoardInfo *vbi) +{ + uint32_t gic_phandle; + + gic_phandle = qemu_devtree_alloc_phandle(vbi->fdt); + qemu_devtree_setprop_cell(vbi->fdt, "/", "interrupt-parent", gic_phandle); + + qemu_devtree_add_subnode(vbi->fdt, "/intc"); + qemu_devtree_setprop_string(vbi->fdt, "/intc", "compatible", + vbi->gic_compatible); + qemu_devtree_setprop_cell(vbi->fdt, "/intc", "#interrupt-cells", 3); + qemu_devtree_setprop(vbi->fdt, "/intc", "interrupt-controller", NULL, 0); + qemu_devtree_setprop_sized_cells(vbi->fdt, "/intc", "reg", + 2, vbi->memmap[VIRT_GIC_DIST].base, + 2, vbi->memmap[VIRT_GIC_DIST].size, + 2, vbi->memmap[VIRT_GIC_CPU].base, + 2, vbi->memmap[VIRT_GIC_CPU].size); + qemu_devtree_setprop_cell(vbi->fdt, "/intc", "phandle", gic_phandle); +} + +static void create_uart(const VirtBoardInfo *vbi, qemu_irq *pic) +{ + char *nodename; + hwaddr base = vbi->memmap[VIRT_UART].base; + hwaddr size = vbi->memmap[VIRT_UART].size; + int irq = vbi->irqmap[VIRT_UART]; + const char compat[] = "arm,pl011\0arm,primecell"; + const char clocknames[] = "uartclk\0apb_pclk"; + + sysbus_create_simple("pl011", base, pic[irq]); + + nodename = g_strdup_printf("/pl011@%" PRIx64, base); + qemu_devtree_add_subnode(vbi->fdt, nodename); + /* Note that we can't use setprop_string because of the embedded NUL */ + qemu_devtree_setprop(vbi->fdt, nodename, "compatible", + compat, sizeof(compat)); + qemu_devtree_setprop_sized_cells(vbi->fdt, nodename, "reg", + 2, base, 2, size); + qemu_devtree_setprop_cells(vbi->fdt, nodename, "interrupts", + GIC_FDT_IRQ_TYPE_SPI, irq, + GIC_FDT_IRQ_FLAGS_EDGE_LO_HI); + qemu_devtree_setprop_cells(vbi->fdt, nodename, "clocks", + vbi->clock_phandle, vbi->clock_phandle); + qemu_devtree_setprop(vbi->fdt, nodename, "clock-names", + clocknames, sizeof(clocknames)); + g_free(nodename); +} + +static void create_virtio_devices(const VirtBoardInfo *vbi, qemu_irq *pic) +{ + int i; + hwaddr size = vbi->memmap[VIRT_MMIO].size; + + /* Note that we have to create the transports in forwards order + * so that command line devices are inserted lowest address first, + * and then add dtb nodes in reverse order so that they appear in + * the finished device tree lowest address first. + */ + for (i = 0; i < NUM_VIRTIO_TRANSPORTS; i++) { + int irq = vbi->irqmap[VIRT_MMIO] + i; + hwaddr base = vbi->memmap[VIRT_MMIO].base + i * size; + + sysbus_create_simple("virtio-mmio", base, pic[irq]); + } + + for (i = NUM_VIRTIO_TRANSPORTS - 1; i >= 0; i--) { + char *nodename; + int irq = vbi->irqmap[VIRT_MMIO] + i; + hwaddr base = vbi->memmap[VIRT_MMIO].base + i * size; + + nodename = g_strdup_printf("/virtio_mmio@%" PRIx64, base); + qemu_devtree_add_subnode(vbi->fdt, nodename); + qemu_devtree_setprop_string(vbi->fdt, nodename, + "compatible", "virtio,mmio"); + qemu_devtree_setprop_sized_cells(vbi->fdt, nodename, "reg", + 2, base, 2, size); + qemu_devtree_setprop_cells(vbi->fdt, nodename, "interrupts", + GIC_FDT_IRQ_TYPE_SPI, irq, + GIC_FDT_IRQ_FLAGS_EDGE_LO_HI); + g_free(nodename); + } +} + +static void *machvirt_dtb(const struct arm_boot_info *binfo, int *fdt_size) +{ + const VirtBoardInfo *board = (const VirtBoardInfo *)binfo; + + *fdt_size = board->fdt_size; + return board->fdt; +} + +static void machvirt_init(QEMUMachineInitArgs *args) +{ + qemu_irq pic[NUM_IRQS]; + MemoryRegion *sysmem = get_system_memory(); + int n; + MemoryRegion *ram = g_new(MemoryRegion, 1); + DeviceState *dev; + SysBusDevice *busdev; + const char *cpu_model = args->cpu_model; + VirtBoardInfo *vbi; + + if (!cpu_model) { + cpu_model = "cortex-a15"; + } + + vbi = find_machine_info(cpu_model); + + if (!vbi) { + error_report("mach-virt: CPU %s not supported", cpu_model); + exit(1); + } + + vbi->smp_cpus = smp_cpus; + + /* + * Only supported method of starting secondary CPUs is PSCI and + * PSCI is not yet supported with TCG, so limit smp_cpus to 1 + * if we're not using KVM. + */ + if (!kvm_enabled() && smp_cpus > 1) { + error_report("mach-virt: must enable KVM to use multiple CPUs"); + exit(1); + } + + if (args->ram_size > vbi->memmap[VIRT_MEM].size) { + error_report("mach-virt: cannot model more than 30GB RAM"); + exit(1); + } + + create_fdt(vbi); + fdt_add_timer_nodes(vbi); + + for (n = 0; n < smp_cpus; n++) { + ObjectClass *oc = cpu_class_by_name(TYPE_ARM_CPU, cpu_model); + Object *cpuobj; + + if (!oc) { + fprintf(stderr, "Unable to find CPU definition\n"); + exit(1); + } + cpuobj = object_new(object_class_get_name(oc)); + + /* Secondary CPUs start in PSCI powered-down state */ + if (n > 0) { + object_property_set_bool(cpuobj, true, "start-powered-off", NULL); + } + object_property_set_bool(cpuobj, true, "realized", NULL); + } + fdt_add_cpu_nodes(vbi); + + memory_region_init_ram(ram, NULL, "mach-virt.ram", args->ram_size); + vmstate_register_ram_global(ram); + memory_region_add_subregion(sysmem, vbi->memmap[VIRT_MEM].base, ram); + + dev = qdev_create(NULL, vbi->qdevname); + qdev_prop_set_uint32(dev, "num-cpu", smp_cpus); + /* Note that the num-irq property counts both internal and external + * interrupts; there are always 32 of the former (mandated by GIC spec). + */ + qdev_prop_set_uint32(dev, "num-irq", NUM_IRQS + 32); + qdev_init_nofail(dev); + busdev = SYS_BUS_DEVICE(dev); + sysbus_mmio_map(busdev, 0, vbi->memmap[VIRT_CPUPERIPHS].base); + fdt_add_gic_node(vbi); + for (n = 0; n < smp_cpus; n++) { + DeviceState *cpudev = DEVICE(qemu_get_cpu(n)); + + sysbus_connect_irq(busdev, n, qdev_get_gpio_in(cpudev, ARM_CPU_IRQ)); + } + + for (n = 0; n < NUM_IRQS; n++) { + pic[n] = qdev_get_gpio_in(dev, n); + } + + create_uart(vbi, pic); + + /* Create mmio transports, so the user can create virtio backends + * (which will be automatically plugged in to the transports). If + * no backend is created the transport will just sit harmlessly idle. + */ + create_virtio_devices(vbi, pic); + + vbi->bootinfo.ram_size = args->ram_size; + vbi->bootinfo.kernel_filename = args->kernel_filename; + vbi->bootinfo.kernel_cmdline = args->kernel_cmdline; + vbi->bootinfo.initrd_filename = args->initrd_filename; + vbi->bootinfo.nb_cpus = smp_cpus; + vbi->bootinfo.board_id = -1; + vbi->bootinfo.loader_start = vbi->memmap[VIRT_MEM].base; + vbi->bootinfo.get_dtb = machvirt_dtb; + arm_load_kernel(ARM_CPU(first_cpu), &vbi->bootinfo); +} + +static QEMUMachine machvirt_a15_machine = { + .name = "virt", + .desc = "ARM Virtual Machine", + .init = machvirt_init, + .max_cpus = 4, +}; + +static void machvirt_machine_init(void) +{ + qemu_register_machine(&machvirt_a15_machine); +} + +machine_init(machvirt_machine_init); diff --git a/hw/audio/adlib.c b/hw/audio/adlib.c index bd8e9d9815..e88d2dd845 100644 --- a/hw/audio/adlib.c +++ b/hw/audio/adlib.c @@ -347,8 +347,8 @@ static void adlib_realizefn (DeviceState *dev, Error **errp) s->samples = AUD_get_buffer_size_out (s->voice) >> SHIFT; s->mixbuf = g_malloc0 (s->samples << SHIFT); - adlib_portio_list[1].offset = s->port; - adlib_portio_list[2].offset = s->port + 8; + adlib_portio_list[0].offset = s->port; + adlib_portio_list[1].offset = s->port + 8; portio_list_init (port_list, OBJECT(s), adlib_portio_list, s, "adlib"); portio_list_add (port_list, isa_address_space_io(&s->parent_obj), 0); } diff --git a/hw/audio/intel-hda.c b/hw/audio/intel-hda.c index 4327264394..6ab8c245d3 100644 --- a/hw/audio/intel-hda.c +++ b/hw/audio/intel-hda.c @@ -444,6 +444,7 @@ static bool intel_hda_xfer(HDACodecDevice *dev, uint32_t stnr, bool output, } } if (d->dp_lbase & 0x01) { + s = st - d->st; addr = intel_hda_addr(d->dp_lbase & ~0x01, d->dp_ubase); stl_le_pci_dma(&d->pci, addr + 8*s, st->lpib); } diff --git a/hw/char/cadence_uart.c b/hw/char/cadence_uart.c index f8ccbdd13a..f18db53bca 100644 --- a/hw/char/cadence_uart.c +++ b/hw/char/cadence_uart.c @@ -120,8 +120,8 @@ typedef struct { uint64_t char_tx_time; CharDriverState *chr; qemu_irq irq; - struct QEMUTimer *fifo_trigger_handle; - struct QEMUTimer *tx_time_handle; + QEMUTimer *fifo_trigger_handle; + QEMUTimer *tx_time_handle; } UartState; static void uart_update_status(UartState *s) diff --git a/hw/cpu/a9mpcore.c b/hw/cpu/a9mpcore.c index 918a7d1291..c09358c6e7 100644 --- a/hw/cpu/a9mpcore.c +++ b/hw/cpu/a9mpcore.c @@ -24,11 +24,14 @@ static void a9mp_priv_initfn(Object *obj) memory_region_init(&s->container, obj, "a9mp-priv-container", 0x2000); sysbus_init_mmio(SYS_BUS_DEVICE(obj), &s->container); + object_initialize(&s->scu, sizeof(s->scu), TYPE_A9_SCU); + qdev_set_parent_bus(DEVICE(&s->scu), sysbus_get_default()); + object_initialize(&s->gic, sizeof(s->gic), TYPE_ARM_GIC); qdev_set_parent_bus(DEVICE(&s->gic), sysbus_get_default()); - object_initialize(&s->scu, sizeof(s->scu), TYPE_A9_SCU); - qdev_set_parent_bus(DEVICE(&s->scu), sysbus_get_default()); + object_initialize(&s->gtimer, sizeof(s->gtimer), TYPE_A9_GTIMER); + qdev_set_parent_bus(DEVICE(&s->gtimer), sysbus_get_default()); object_initialize(&s->mptimer, sizeof(s->mptimer), TYPE_ARM_MPTIMER); qdev_set_parent_bus(DEVICE(&s->mptimer), sysbus_get_default()); @@ -41,11 +44,21 @@ static void a9mp_priv_realize(DeviceState *dev, Error **errp) { SysBusDevice *sbd = SYS_BUS_DEVICE(dev); A9MPPrivState *s = A9MPCORE_PRIV(dev); - DeviceState *gicdev, *scudev, *mptimerdev, *wdtdev; - SysBusDevice *timerbusdev, *wdtbusdev, *gicbusdev, *scubusdev; + DeviceState *scudev, *gicdev, *gtimerdev, *mptimerdev, *wdtdev; + SysBusDevice *scubusdev, *gicbusdev, *gtimerbusdev, *mptimerbusdev, + *wdtbusdev; Error *err = NULL; int i; + scudev = DEVICE(&s->scu); + qdev_prop_set_uint32(scudev, "num-cpu", s->num_cpu); + object_property_set_bool(OBJECT(&s->scu), true, "realized", &err); + if (err != NULL) { + error_propagate(errp, err); + return; + } + scubusdev = SYS_BUS_DEVICE(&s->scu); + gicdev = DEVICE(&s->gic); qdev_prop_set_uint32(gicdev, "num-cpu", s->num_cpu); qdev_prop_set_uint32(gicdev, "num-irq", s->num_irq); @@ -62,14 +75,14 @@ static void a9mp_priv_realize(DeviceState *dev, Error **errp) /* Pass through inbound GPIO lines to the GIC */ qdev_init_gpio_in(dev, a9mp_priv_set_irq, s->num_irq - 32); - scudev = DEVICE(&s->scu); - qdev_prop_set_uint32(scudev, "num-cpu", s->num_cpu); - object_property_set_bool(OBJECT(&s->scu), true, "realized", &err); + gtimerdev = DEVICE(&s->gtimer); + qdev_prop_set_uint32(gtimerdev, "num-cpu", s->num_cpu); + object_property_set_bool(OBJECT(&s->gtimer), true, "realized", &err); if (err != NULL) { error_propagate(errp, err); return; } - scubusdev = SYS_BUS_DEVICE(&s->scu); + gtimerbusdev = SYS_BUS_DEVICE(&s->gtimer); mptimerdev = DEVICE(&s->mptimer); qdev_prop_set_uint32(mptimerdev, "num-cpu", s->num_cpu); @@ -78,7 +91,7 @@ static void a9mp_priv_realize(DeviceState *dev, Error **errp) error_propagate(errp, err); return; } - timerbusdev = SYS_BUS_DEVICE(&s->mptimer); + mptimerbusdev = SYS_BUS_DEVICE(&s->mptimer); wdtdev = DEVICE(&s->wdt); qdev_prop_set_uint32(wdtdev, "num-cpu", s->num_cpu); @@ -97,30 +110,33 @@ static void a9mp_priv_realize(DeviceState *dev, Error **errp) * 0x0600-0x06ff -- private timers and watchdogs * 0x0700-0x0fff -- nothing * 0x1000-0x1fff -- GIC Distributor - * - * We should implement the global timer but don't currently do so. */ memory_region_add_subregion(&s->container, 0, sysbus_mmio_get_region(scubusdev, 0)); /* GIC CPU interface */ memory_region_add_subregion(&s->container, 0x100, sysbus_mmio_get_region(gicbusdev, 1)); + memory_region_add_subregion(&s->container, 0x200, + sysbus_mmio_get_region(gtimerbusdev, 0)); /* Note that the A9 exposes only the "timer/watchdog for this core" * memory region, not the "timer/watchdog for core X" ones 11MPcore has. */ memory_region_add_subregion(&s->container, 0x600, - sysbus_mmio_get_region(timerbusdev, 0)); + sysbus_mmio_get_region(mptimerbusdev, 0)); memory_region_add_subregion(&s->container, 0x620, sysbus_mmio_get_region(wdtbusdev, 0)); memory_region_add_subregion(&s->container, 0x1000, sysbus_mmio_get_region(gicbusdev, 0)); /* Wire up the interrupt from each watchdog and timer. - * For each core the timer is PPI 29 and the watchdog PPI 30. + * For each core the global timer is PPI 27, the private + * timer is PPI 29 and the watchdog PPI 30. */ for (i = 0; i < s->num_cpu; i++) { int ppibase = (s->num_irq - 32) + i * 32; - sysbus_connect_irq(timerbusdev, i, + sysbus_connect_irq(gtimerbusdev, i, + qdev_get_gpio_in(gicdev, ppibase + 27)); + sysbus_connect_irq(mptimerbusdev, i, qdev_get_gpio_in(gicdev, ppibase + 29)); sysbus_connect_irq(wdtbusdev, i, qdev_get_gpio_in(gicdev, ppibase + 30)); diff --git a/hw/display/qxl-render.c b/hw/display/qxl-render.c index d34b0c4170..84f1367716 100644 --- a/hw/display/qxl-render.c +++ b/hw/display/qxl-render.c @@ -20,6 +20,7 @@ */ #include "qxl.h" +#include "trace.h" static void qxl_blit(PCIQXLDevice *qxl, QXLRect *rect) { diff --git a/hw/display/vmware_vga.c b/hw/display/vmware_vga.c index a6a8cdc2e1..aba292ccde 100644 --- a/hw/display/vmware_vga.c +++ b/hw/display/vmware_vga.c @@ -23,6 +23,7 @@ */ #include "hw/hw.h" #include "hw/loader.h" +#include "trace.h" #include "ui/console.h" #include "hw/pci/pci.h" diff --git a/hw/i386/acpi-build.c b/hw/i386/acpi-build.c index 1f22fb60a4..befc39f253 100644 --- a/hw/i386/acpi-build.c +++ b/hw/i386/acpi-build.c @@ -285,7 +285,8 @@ static inline void build_append_array(GArray *array, GArray *val) g_array_append_vals(array, val->data, val->len); } -static void build_append_nameseg(GArray *array, const char *format, ...) +static void GCC_FMT_ATTR(2, 3) +build_append_nameseg(GArray *array, const char *format, ...) { /* It would be nicer to use g_string_vprintf but it's only there in 2.22 */ char s[] = "XXXX"; @@ -630,7 +631,7 @@ build_append_notify(GArray *device, const char *name, GArray *method = build_alloc_array(); uint8_t op = 0x14; /* MethodOp */ - build_append_nameseg(method, name); + build_append_nameseg(method, "%s", name); build_append_byte(method, 0x02); /* MethodFlags: ArgCount */ for (i = skip; i < count; i++) { GArray *target = build_alloc_array(); diff --git a/hw/i386/pc_piix.c b/hw/i386/pc_piix.c index 2111f0192c..ab562853b8 100644 --- a/hw/i386/pc_piix.c +++ b/hw/i386/pc_piix.c @@ -339,13 +339,24 @@ static void pc_xen_hvm_init(QEMUMachineInitArgs *args) .desc = "Standard PC (i440FX + PIIX, 1996)", \ .hot_add_cpu = pc_hot_add_cpu +#define PC_I440FX_2_0_MACHINE_OPTIONS \ + PC_I440FX_MACHINE_OPTIONS, \ + .default_machine_opts = "firmware=bios-256k.bin" + +static QEMUMachine pc_i440fx_machine_v2_0 = { + PC_I440FX_2_0_MACHINE_OPTIONS, + .name = "pc-i440fx-2.0", + .alias = "pc", + .init = pc_init_pci, + .is_default = 1, +}; + #define PC_I440FX_1_7_MACHINE_OPTIONS PC_I440FX_MACHINE_OPTIONS + static QEMUMachine pc_i440fx_machine_v1_7 = { PC_I440FX_1_7_MACHINE_OPTIONS, .name = "pc-i440fx-1.7", - .alias = "pc", .init = pc_init_pci, - .is_default = 1, }; #define PC_I440FX_1_6_MACHINE_OPTIONS PC_I440FX_MACHINE_OPTIONS @@ -747,6 +758,7 @@ static QEMUMachine xenfv_machine = { static void pc_machine_init(void) { + qemu_register_machine(&pc_i440fx_machine_v2_0); qemu_register_machine(&pc_i440fx_machine_v1_7); qemu_register_machine(&pc_i440fx_machine_v1_6); qemu_register_machine(&pc_i440fx_machine_v1_5); diff --git a/hw/i386/pc_q35.c b/hw/i386/pc_q35.c index 600fc02ebe..97aa84264c 100644 --- a/hw/i386/pc_q35.c +++ b/hw/i386/pc_q35.c @@ -259,12 +259,22 @@ static void pc_q35_init_1_4(QEMUMachineInitArgs *args) .desc = "Standard PC (Q35 + ICH9, 2009)", \ .hot_add_cpu = pc_hot_add_cpu +#define PC_Q35_2_0_MACHINE_OPTIONS \ + PC_Q35_MACHINE_OPTIONS, \ + .default_machine_opts = "firmware=bios-256k.bin" + +static QEMUMachine pc_q35_machine_v2_0 = { + PC_Q35_2_0_MACHINE_OPTIONS, + .name = "pc-q35-2.0", + .alias = "q35", + .init = pc_q35_init, +}; + #define PC_Q35_1_7_MACHINE_OPTIONS PC_Q35_MACHINE_OPTIONS static QEMUMachine pc_q35_machine_v1_7 = { PC_Q35_1_7_MACHINE_OPTIONS, .name = "pc-q35-1.7", - .alias = "q35", .init = pc_q35_init, }; @@ -306,6 +316,7 @@ static QEMUMachine pc_q35_machine_v1_4 = { static void pc_q35_machine_init(void) { + qemu_register_machine(&pc_q35_machine_v2_0); qemu_register_machine(&pc_q35_machine_v1_7); qemu_register_machine(&pc_q35_machine_v1_6); qemu_register_machine(&pc_q35_machine_v1_5); diff --git a/hw/misc/vfio.c b/hw/misc/vfio.c index f7f8a19ee8..9aecaa82bc 100644 --- a/hw/misc/vfio.c +++ b/hw/misc/vfio.c @@ -52,6 +52,8 @@ /* Extra debugging, trap acceleration paths for more logging */ #define VFIO_ALLOW_MMAP 1 #define VFIO_ALLOW_KVM_INTX 1 +#define VFIO_ALLOW_KVM_MSI 1 +#define VFIO_ALLOW_KVM_MSIX 1 struct VFIODevice; @@ -208,6 +210,17 @@ static QLIST_HEAD(, VFIOContainer) static QLIST_HEAD(, VFIOGroup) group_list = QLIST_HEAD_INITIALIZER(group_list); +#ifdef CONFIG_KVM +/* + * We have a single VFIO pseudo device per KVM VM. Once created it lives + * for the life of the VM. Closing the file descriptor only drops our + * reference to it and the device's reference to kvm. Therefore once + * initialized, this file descriptor is only released on QEMU exit and + * we'll re-use it should another vfio device be attached before then. + */ +static int vfio_kvm_device_fd = -1; +#endif + static void vfio_disable_interrupts(VFIODevice *vdev); static uint32_t vfio_pci_read_config(PCIDevice *pdev, uint32_t addr, int len); static void vfio_pci_write_config(PCIDevice *pdev, uint32_t addr, @@ -579,9 +592,21 @@ static void vfio_msi_interrupt(void *opaque) return; } - DPRINTF("%s(%04x:%02x:%02x.%x) vector %d\n", __func__, +#ifdef VFIO_DEBUG + MSIMessage msg; + + if (vdev->interrupt == VFIO_INT_MSIX) { + msg = msi_get_message(&vdev->pdev, nr); + } else if (vdev->interrupt == VFIO_INT_MSI) { + msg = msix_get_message(&vdev->pdev, nr); + } else { + abort(); + } + + DPRINTF("%s(%04x:%02x:%02x.%x) vector %d 0x%"PRIx64"/0x%x\n", __func__, vdev->host.domain, vdev->host.bus, vdev->host.slot, - vdev->host.function, nr); + vdev->host.function, nr, msg.address, msg.data); +#endif if (vdev->interrupt == VFIO_INT_MSIX) { msix_notify(&vdev->pdev, nr); @@ -649,7 +674,8 @@ static int vfio_msix_vector_do_use(PCIDevice *pdev, unsigned int nr, * Attempt to enable route through KVM irqchip, * default to userspace handling if unavailable. */ - vector->virq = msg ? kvm_irqchip_add_msi_route(kvm_state, *msg) : -1; + vector->virq = msg && VFIO_ALLOW_KVM_MSIX ? + kvm_irqchip_add_msi_route(kvm_state, *msg) : -1; if (vector->virq < 0 || kvm_irqchip_add_irqfd_notifier(kvm_state, &vector->interrupt, NULL, vector->virq) < 0) { @@ -816,7 +842,8 @@ retry: * Attempt to enable route through KVM irqchip, * default to userspace handling if unavailable. */ - vector->virq = kvm_irqchip_add_msi_route(kvm_state, vector->msg); + vector->virq = VFIO_ALLOW_KVM_MSI ? + kvm_irqchip_add_msi_route(kvm_state, vector->msg) : -1; if (vector->virq < 0 || kvm_irqchip_add_irqfd_notifier(kvm_state, &vector->interrupt, NULL, vector->virq) < 0) { @@ -878,8 +905,20 @@ static void vfio_disable_msi_common(VFIODevice *vdev) static void vfio_disable_msix(VFIODevice *vdev) { + int i; + msix_unset_vector_notifiers(&vdev->pdev); + /* + * MSI-X will only release vectors if MSI-X is still enabled on the + * device, check through the rest and release it ourselves if necessary. + */ + for (i = 0; i < vdev->nr_vectors; i++) { + if (vdev->msi_vectors[i].use) { + vfio_msix_vector_release(&vdev->pdev, i); + } + } + if (vdev->nr_vectors) { vfio_disable_irqindex(vdev, VFIO_PCI_MSIX_IRQ_INDEX); } @@ -1800,6 +1839,34 @@ static void vfio_probe_nvidia_bar5_window_quirk(VFIODevice *vdev, int nr) vdev->host.function); } +static void vfio_nvidia_88000_quirk_write(void *opaque, hwaddr addr, + uint64_t data, unsigned size) +{ + VFIOQuirk *quirk = opaque; + VFIODevice *vdev = quirk->vdev; + PCIDevice *pdev = &vdev->pdev; + hwaddr base = quirk->data.address_match & TARGET_PAGE_MASK; + + vfio_generic_quirk_write(opaque, addr, data, size); + + /* + * Nvidia seems to acknowledge MSI interrupts by writing 0xff to the + * MSI capability ID register. Both the ID and next register are + * read-only, so we allow writes covering either of those to real hw. + * NB - only fixed for the 0x88000 MMIO window. + */ + if ((pdev->cap_present & QEMU_PCI_CAP_MSI) && + vfio_range_contained(addr, size, pdev->msi_cap, PCI_MSI_FLAGS)) { + vfio_bar_write(&vdev->bars[quirk->data.bar], addr + base, data, size); + } +} + +static const MemoryRegionOps vfio_nvidia_88000_quirk = { + .read = vfio_generic_quirk_read, + .write = vfio_nvidia_88000_quirk_write, + .endianness = DEVICE_LITTLE_ENDIAN, +}; + /* * Finally, BAR0 itself. We want to redirect any accesses to either * 0x1800 or 0x88000 through the PCI config space access functions. @@ -1826,7 +1893,7 @@ static void vfio_probe_nvidia_bar0_88000_quirk(VFIODevice *vdev, int nr) quirk->data.address_mask = PCIE_CONFIG_SPACE_SIZE - 1; quirk->data.bar = nr; - memory_region_init_io(&quirk->mem, OBJECT(vdev), &vfio_generic_quirk, + memory_region_init_io(&quirk->mem, OBJECT(vdev), &vfio_nvidia_88000_quirk, quirk, "vfio-nvidia-bar0-88000-quirk", TARGET_PAGE_ALIGN(quirk->data.address_mask + 1)); memory_region_add_subregion_overlap(&vdev->bars[nr].mem, @@ -3041,6 +3108,59 @@ static void vfio_pci_reset_handler(void *opaque) } } +static void vfio_kvm_device_add_group(VFIOGroup *group) +{ +#ifdef CONFIG_KVM + struct kvm_device_attr attr = { + .group = KVM_DEV_VFIO_GROUP, + .attr = KVM_DEV_VFIO_GROUP_ADD, + .addr = (uint64_t)(unsigned long)&group->fd, + }; + + if (!kvm_enabled()) { + return; + } + + if (vfio_kvm_device_fd < 0) { + struct kvm_create_device cd = { + .type = KVM_DEV_TYPE_VFIO, + }; + + if (kvm_vm_ioctl(kvm_state, KVM_CREATE_DEVICE, &cd)) { + DPRINTF("KVM_CREATE_DEVICE: %m\n"); + return; + } + + vfio_kvm_device_fd = cd.fd; + } + + if (ioctl(vfio_kvm_device_fd, KVM_SET_DEVICE_ATTR, &attr)) { + error_report("Failed to add group %d to KVM VFIO device: %m", + group->groupid); + } +#endif +} + +static void vfio_kvm_device_del_group(VFIOGroup *group) +{ +#ifdef CONFIG_KVM + struct kvm_device_attr attr = { + .group = KVM_DEV_VFIO_GROUP, + .attr = KVM_DEV_VFIO_GROUP_DEL, + .addr = (uint64_t)(unsigned long)&group->fd, + }; + + if (vfio_kvm_device_fd < 0) { + return; + } + + if (ioctl(vfio_kvm_device_fd, KVM_SET_DEVICE_ATTR, &attr)) { + error_report("Failed to remove group %d to KVM VFIO device: %m", + group->groupid); + } +#endif +} + static int vfio_connect_container(VFIOGroup *group) { VFIOContainer *container; @@ -3189,6 +3309,8 @@ static VFIOGroup *vfio_get_group(int groupid) QLIST_INSERT_HEAD(&group_list, group, next); + vfio_kvm_device_add_group(group); + return group; } @@ -3198,6 +3320,7 @@ static void vfio_put_group(VFIOGroup *group) return; } + vfio_kvm_device_del_group(group); vfio_disconnect_container(group); QLIST_REMOVE(group, next); DPRINTF("vfio_put_group: close group->fd\n"); diff --git a/hw/net/cadence_gem.c b/hw/net/cadence_gem.c index 4a355bbbef..92dc2f21fa 100644 --- a/hw/net/cadence_gem.c +++ b/hw/net/cadence_gem.c @@ -222,8 +222,13 @@ #define PHY_REG_INT_ST_ENERGY 0x0010 /***********************************************************************/ -#define GEM_RX_REJECT 1 -#define GEM_RX_ACCEPT 0 +#define GEM_RX_REJECT (-1) +#define GEM_RX_PROMISCUOUS_ACCEPT (-2) +#define GEM_RX_BROADCAST_ACCEPT (-3) +#define GEM_RX_MULTICAST_HASH_ACCEPT (-4) +#define GEM_RX_UNICAST_HASH_ACCEPT (-5) + +#define GEM_RX_SAR_ACCEPT 0 /***********************************************************************/ @@ -236,6 +241,13 @@ #define DESC_0_RX_WRAP 0x00000002 #define DESC_0_RX_OWNERSHIP 0x00000001 +#define R_DESC_1_RX_SAR_SHIFT 25 +#define R_DESC_1_RX_SAR_LENGTH 2 +#define R_DESC_1_RX_SAR_MATCH (1 << 27) +#define R_DESC_1_RX_UNICAST_HASH (1 << 29) +#define R_DESC_1_RX_MULTICAST_HASH (1 << 30) +#define R_DESC_1_RX_BROADCAST (1 << 31) + #define DESC_1_RX_SOF 0x00004000 #define DESC_1_RX_EOF 0x00008000 @@ -315,6 +327,28 @@ static inline void rx_desc_set_length(unsigned *desc, unsigned len) desc[1] |= len; } +static inline void rx_desc_set_broadcast(unsigned *desc) +{ + desc[1] |= R_DESC_1_RX_BROADCAST; +} + +static inline void rx_desc_set_unicast_hash(unsigned *desc) +{ + desc[1] |= R_DESC_1_RX_UNICAST_HASH; +} + +static inline void rx_desc_set_multicast_hash(unsigned *desc) +{ + desc[1] |= R_DESC_1_RX_MULTICAST_HASH; +} + +static inline void rx_desc_set_sar(unsigned *desc, int sar_idx) +{ + desc[1] = deposit32(desc[1], R_DESC_1_RX_SAR_SHIFT, R_DESC_1_RX_SAR_LENGTH, + sar_idx); + desc[1] |= R_DESC_1_RX_SAR_MATCH; +} + #define TYPE_CADENCE_GEM "cadence_gem" #define GEM(obj) OBJECT_CHECK(GemState, (obj), TYPE_CADENCE_GEM) @@ -346,6 +380,11 @@ typedef struct GemState { uint32_t rx_desc_addr; uint32_t tx_desc_addr; + uint8_t can_rx_state; /* Debug only */ + + unsigned rx_desc[2]; + + bool sar_active[4]; } GemState; /* The broadcast MAC address: 0xFFFFFFFFFFFF */ @@ -415,13 +454,28 @@ static int gem_can_receive(NetClientState *nc) s = qemu_get_nic_opaque(nc); - DB_PRINT("\n"); - /* Do nothing if receive is not enabled. */ if (!(s->regs[GEM_NWCTRL] & GEM_NWCTRL_RXENA)) { + if (s->can_rx_state != 1) { + s->can_rx_state = 1; + DB_PRINT("can't receive - no enable\n"); + } return 0; } + if (rx_desc_get_ownership(s->rx_desc) == 1) { + if (s->can_rx_state != 2) { + s->can_rx_state = 2; + DB_PRINT("can't receive - busy buffer descriptor 0x%x\n", + s->rx_desc_addr); + } + return 0; + } + + if (s->can_rx_state != 0) { + s->can_rx_state = 0; + DB_PRINT("can receive 0x%x\n", s->rx_desc_addr); + } return 1; } @@ -527,7 +581,10 @@ static unsigned calc_mac_hash(const uint8_t *mac) * Accept or reject this destination address? * Returns: * GEM_RX_REJECT: reject - * GEM_RX_ACCEPT: accept + * >= 0: Specific address accept (which matched SAR is returned) + * others for various other modes of accept: + * GEM_RM_PROMISCUOUS_ACCEPT, GEM_RX_BROADCAST_ACCEPT, + * GEM_RX_MULTICAST_HASH_ACCEPT or GEM_RX_UNICAST_HASH_ACCEPT */ static int gem_mac_address_filter(GemState *s, const uint8_t *packet) { @@ -536,7 +593,7 @@ static int gem_mac_address_filter(GemState *s, const uint8_t *packet) /* Promiscuous mode? */ if (s->regs[GEM_NWCFG] & GEM_NWCFG_PROMISC) { - return GEM_RX_ACCEPT; + return GEM_RX_PROMISCUOUS_ACCEPT; } if (!memcmp(packet, broadcast_addr, 6)) { @@ -544,7 +601,7 @@ static int gem_mac_address_filter(GemState *s, const uint8_t *packet) if (s->regs[GEM_NWCFG] & GEM_NWCFG_BCAST_REJ) { return GEM_RX_REJECT; } - return GEM_RX_ACCEPT; + return GEM_RX_BROADCAST_ACCEPT; } /* Accept packets -w- hash match? */ @@ -555,53 +612,67 @@ static int gem_mac_address_filter(GemState *s, const uint8_t *packet) hash_index = calc_mac_hash(packet); if (hash_index < 32) { if (s->regs[GEM_HASHLO] & (1<<hash_index)) { - return GEM_RX_ACCEPT; + return packet[0] == 0x01 ? GEM_RX_MULTICAST_HASH_ACCEPT : + GEM_RX_UNICAST_HASH_ACCEPT; } } else { hash_index -= 32; if (s->regs[GEM_HASHHI] & (1<<hash_index)) { - return GEM_RX_ACCEPT; + return packet[0] == 0x01 ? GEM_RX_MULTICAST_HASH_ACCEPT : + GEM_RX_UNICAST_HASH_ACCEPT; } } } /* Check all 4 specific addresses */ gem_spaddr = (uint8_t *)&(s->regs[GEM_SPADDR1LO]); - for (i = 0; i < 4; i++) { - if (!memcmp(packet, gem_spaddr, 6)) { - return GEM_RX_ACCEPT; + for (i = 3; i >= 0; i--) { + if (s->sar_active[i] && !memcmp(packet, gem_spaddr + 8 * i, 6)) { + return GEM_RX_SAR_ACCEPT + i; } - - gem_spaddr += 8; } /* No address match; reject the packet */ return GEM_RX_REJECT; } +static void gem_get_rx_desc(GemState *s) +{ + DB_PRINT("read descriptor 0x%x\n", (unsigned)s->rx_desc_addr); + /* read current descriptor */ + cpu_physical_memory_read(s->rx_desc_addr, + (uint8_t *)s->rx_desc, sizeof(s->rx_desc)); + + /* Descriptor owned by software ? */ + if (rx_desc_get_ownership(s->rx_desc) == 1) { + DB_PRINT("descriptor 0x%x owned by sw.\n", + (unsigned)s->rx_desc_addr); + s->regs[GEM_RXSTATUS] |= GEM_RXSTATUS_NOBUF; + s->regs[GEM_ISR] |= GEM_INT_RXUSED & ~(s->regs[GEM_IMR]); + /* Handle interrupt consequences */ + gem_update_int_status(s); + } +} + /* * gem_receive: * Fit a packet handed to us by QEMU into the receive descriptor ring. */ static ssize_t gem_receive(NetClientState *nc, const uint8_t *buf, size_t size) { - unsigned desc[2]; - hwaddr packet_desc_addr, last_desc_addr; GemState *s; unsigned rxbufsize, bytes_to_copy; unsigned rxbuf_offset; uint8_t rxbuf[2048]; uint8_t *rxbuf_ptr; + bool first_desc = true; + int maf; s = qemu_get_nic_opaque(nc); - /* Do nothing if receive is not enabled. */ - if (!gem_can_receive(nc)) { - return -1; - } - /* Is this destination MAC address "for us" ? */ - if (gem_mac_address_filter(s, buf) == GEM_RX_REJECT) { + maf = gem_mac_address_filter(s, buf); + if (maf == GEM_RX_REJECT) { return -1; } @@ -633,6 +704,14 @@ static ssize_t gem_receive(NetClientState *nc, const uint8_t *buf, size_t size) GEM_DMACFG_RBUFSZ_S) * GEM_DMACFG_RBUFSZ_MUL; bytes_to_copy = size; + /* Pad to minimum length. Assume FCS field is stripped, logic + * below will increment it to the real minimum of 64 when + * not FCS stripping + */ + if (size < 60) { + size = 60; + } + /* Strip of FCS field ? (usually yes) */ if (s->regs[GEM_NWCFG] & GEM_NWCFG_STRIP_FCS) { rxbuf_ptr = (void *)buf; @@ -659,95 +738,71 @@ static ssize_t gem_receive(NetClientState *nc, const uint8_t *buf, size_t size) size += 4; } - /* Pad to minimum length */ - if (size < 64) { - size = 64; - } - DB_PRINT("config bufsize: %d packet size: %ld\n", rxbufsize, size); - packet_desc_addr = s->rx_desc_addr; - while (1) { - DB_PRINT("read descriptor 0x%x\n", (unsigned)packet_desc_addr); - /* read current descriptor */ - cpu_physical_memory_read(packet_desc_addr, - (uint8_t *)&desc[0], sizeof(desc)); - - /* Descriptor owned by software ? */ - if (rx_desc_get_ownership(desc) == 1) { - DB_PRINT("descriptor 0x%x owned by sw.\n", - (unsigned)packet_desc_addr); - s->regs[GEM_RXSTATUS] |= GEM_RXSTATUS_NOBUF; - s->regs[GEM_ISR] |= GEM_INT_RXUSED & ~(s->regs[GEM_IMR]); - /* Handle interrupt consequences */ - gem_update_int_status(s); + while (bytes_to_copy) { + /* Do nothing if receive is not enabled. */ + if (!gem_can_receive(nc)) { + assert(!first_desc); return -1; } DB_PRINT("copy %d bytes to 0x%x\n", MIN(bytes_to_copy, rxbufsize), - rx_desc_get_buffer(desc)); - - /* - * Let's have QEMU lend a helping hand. - */ - if (rx_desc_get_buffer(desc) == 0) { - DB_PRINT("Invalid RX buffer (NULL) for descriptor 0x%x\n", - (unsigned)packet_desc_addr); - break; - } + rx_desc_get_buffer(s->rx_desc)); /* Copy packet data to emulated DMA buffer */ - cpu_physical_memory_write(rx_desc_get_buffer(desc) + rxbuf_offset, + cpu_physical_memory_write(rx_desc_get_buffer(s->rx_desc) + rxbuf_offset, rxbuf_ptr, MIN(bytes_to_copy, rxbufsize)); - bytes_to_copy -= MIN(bytes_to_copy, rxbufsize); rxbuf_ptr += MIN(bytes_to_copy, rxbufsize); + bytes_to_copy -= MIN(bytes_to_copy, rxbufsize); + + /* Update the descriptor. */ + if (first_desc) { + rx_desc_set_sof(s->rx_desc); + first_desc = false; + } if (bytes_to_copy == 0) { + rx_desc_set_eof(s->rx_desc); + rx_desc_set_length(s->rx_desc, size); + } + rx_desc_set_ownership(s->rx_desc); + + switch (maf) { + case GEM_RX_PROMISCUOUS_ACCEPT: + break; + case GEM_RX_BROADCAST_ACCEPT: + rx_desc_set_broadcast(s->rx_desc); + break; + case GEM_RX_UNICAST_HASH_ACCEPT: + rx_desc_set_unicast_hash(s->rx_desc); + break; + case GEM_RX_MULTICAST_HASH_ACCEPT: + rx_desc_set_multicast_hash(s->rx_desc); break; + case GEM_RX_REJECT: + abort(); + default: /* SAR */ + rx_desc_set_sar(s->rx_desc, maf); } + /* Descriptor write-back. */ + cpu_physical_memory_write(s->rx_desc_addr, + (uint8_t *)s->rx_desc, sizeof(s->rx_desc)); + /* Next descriptor */ - if (rx_desc_get_wrap(desc)) { - packet_desc_addr = s->regs[GEM_RXQBASE]; + if (rx_desc_get_wrap(s->rx_desc)) { + DB_PRINT("wrapping RX descriptor list\n"); + s->rx_desc_addr = s->regs[GEM_RXQBASE]; } else { - packet_desc_addr += 8; + DB_PRINT("incrementing RX descriptor list\n"); + s->rx_desc_addr += 8; } + gem_get_rx_desc(s); } - DB_PRINT("set length: %ld, EOF on descriptor 0x%x\n", size, - (unsigned)packet_desc_addr); - - /* Update last descriptor with EOF and total length */ - rx_desc_set_eof(desc); - rx_desc_set_length(desc, size); - cpu_physical_memory_write(packet_desc_addr, - (uint8_t *)&desc[0], sizeof(desc)); - - /* Advance RX packet descriptor Q */ - last_desc_addr = packet_desc_addr; - packet_desc_addr = s->rx_desc_addr; - s->rx_desc_addr = last_desc_addr; - if (rx_desc_get_wrap(desc)) { - s->rx_desc_addr = s->regs[GEM_RXQBASE]; - DB_PRINT("wrapping RX descriptor list\n"); - } else { - DB_PRINT("incrementing RX descriptor list\n"); - s->rx_desc_addr += 8; - } - - DB_PRINT("set SOF, OWN on descriptor 0x%08x\n", (unsigned)packet_desc_addr); - /* Count it */ gem_receive_updatestats(s, buf, size); - /* Update first descriptor (which could also be the last) */ - /* read descriptor */ - cpu_physical_memory_read(packet_desc_addr, - (uint8_t *)&desc[0], sizeof(desc)); - rx_desc_set_sof(desc); - rx_desc_set_ownership(desc); - cpu_physical_memory_write(packet_desc_addr, - (uint8_t *)&desc[0], sizeof(desc)); - s->regs[GEM_RXSTATUS] |= GEM_RXSTATUS_FRMRCVD; s->regs[GEM_ISR] |= GEM_INT_RXCMPL & ~(s->regs[GEM_IMR]); @@ -893,7 +948,7 @@ static void gem_transmit(GemState *s) gem_transmit_updatestats(s, tx_packet, total_bytes); /* Send the packet somewhere */ - if (s->phy_loop) { + if (s->phy_loop || (s->regs[GEM_NWCTRL] & GEM_NWCTRL_LOCALLOOP)) { gem_receive(qemu_get_queue(s->nic), tx_packet, total_bytes); } else { qemu_send_packet(qemu_get_queue(s->nic), tx_packet, @@ -949,6 +1004,7 @@ static void gem_phy_reset(GemState *s) static void gem_reset(DeviceState *d) { + int i; GemState *s = GEM(d); DB_PRINT("\n"); @@ -968,6 +1024,10 @@ static void gem_reset(DeviceState *d) s->regs[GEM_DESCONF5] = 0x002f2145; s->regs[GEM_DESCONF6] = 0x00000200; + for (i = 0; i < 4; i++) { + s->sar_active[i] = false; + } + gem_phy_reset(s); gem_update_int_status(s); @@ -1069,19 +1129,21 @@ static void gem_write(void *opaque, hwaddr offset, uint64_t val, /* Squash bits which are read only in write value */ val &= ~(s->regs_ro[offset]); - /* Preserve (only) bits which are read only in register */ - readonly = s->regs[offset]; - readonly &= s->regs_ro[offset]; - - /* Squash bits which are write 1 to clear */ - val &= ~(s->regs_w1c[offset] & val); + /* Preserve (only) bits which are read only and wtc in register */ + readonly = s->regs[offset] & (s->regs_ro[offset] | s->regs_w1c[offset]); /* Copy register write to backing store */ - s->regs[offset] = val | readonly; + s->regs[offset] = (val & ~s->regs_w1c[offset]) | readonly; + + /* do w1c */ + s->regs[offset] &= ~(s->regs_w1c[offset] & val); /* Handle register write side effects */ switch (offset) { case GEM_NWCTRL: + if (val & GEM_NWCTRL_RXENA) { + gem_get_rx_desc(s); + } if (val & GEM_NWCTRL_TXSTART) { gem_transmit(s); } @@ -1089,7 +1151,7 @@ static void gem_write(void *opaque, hwaddr offset, uint64_t val, /* Reset to start of Q when transmit disabled. */ s->tx_desc_addr = s->regs[GEM_TXQBASE]; } - if (val & GEM_NWCTRL_RXENA) { + if (gem_can_receive(qemu_get_queue(s->nic))) { qemu_flush_queued_packets(qemu_get_queue(s->nic)); } break; @@ -1114,6 +1176,18 @@ static void gem_write(void *opaque, hwaddr offset, uint64_t val, s->regs[GEM_IMR] |= val; gem_update_int_status(s); break; + case GEM_SPADDR1LO: + case GEM_SPADDR2LO: + case GEM_SPADDR3LO: + case GEM_SPADDR4LO: + s->sar_active[(offset - GEM_SPADDR1LO) / 2] = false; + break; + case GEM_SPADDR1HI: + case GEM_SPADDR2HI: + case GEM_SPADDR3HI: + case GEM_SPADDR4HI: + s->sar_active[(offset - GEM_SPADDR1HI) / 2] = true; + break; case GEM_PHYMNTNC: if (val & GEM_PHYMNTNC_OP_W) { uint32_t phy_addr, reg_num; @@ -1181,15 +1255,17 @@ static int gem_init(SysBusDevice *sbd) static const VMStateDescription vmstate_cadence_gem = { .name = "cadence_gem", - .version_id = 1, - .minimum_version_id = 1, - .minimum_version_id_old = 1, + .version_id = 2, + .minimum_version_id = 2, + .minimum_version_id_old = 2, .fields = (VMStateField[]) { VMSTATE_UINT32_ARRAY(regs, GemState, GEM_MAXREG), VMSTATE_UINT16_ARRAY(phy_regs, GemState, 32), VMSTATE_UINT8(phy_loop, GemState), VMSTATE_UINT32(rx_desc_addr, GemState), VMSTATE_UINT32(tx_desc_addr, GemState), + VMSTATE_BOOL_ARRAY(sar_active, GemState, 4), + VMSTATE_END_OF_LIST(), } }; diff --git a/hw/net/virtio-net.c b/hw/net/virtio-net.c index b75c753305..d312b9c83c 100644 --- a/hw/net/virtio-net.c +++ b/hw/net/virtio-net.c @@ -610,11 +610,11 @@ static int virtio_net_handle_mac(VirtIONet *n, uint8_t cmd, return VIRTIO_NET_ERR; } - n->mac_table.in_use = 0; - n->mac_table.first_multi = 0; - n->mac_table.uni_overflow = 0; - n->mac_table.multi_overflow = 0; - memset(n->mac_table.macs, 0, MAC_TABLE_ENTRIES * ETH_ALEN); + int in_use = 0; + int first_multi = 0; + uint8_t uni_overflow = 0; + uint8_t multi_overflow = 0; + uint8_t *macs = g_malloc0(MAC_TABLE_ENTRIES * ETH_ALEN); s = iov_to_buf(iov, iov_cnt, 0, &mac_data.entries, sizeof(mac_data.entries)); @@ -629,19 +629,19 @@ static int virtio_net_handle_mac(VirtIONet *n, uint8_t cmd, } if (mac_data.entries <= MAC_TABLE_ENTRIES) { - s = iov_to_buf(iov, iov_cnt, 0, n->mac_table.macs, + s = iov_to_buf(iov, iov_cnt, 0, macs, mac_data.entries * ETH_ALEN); if (s != mac_data.entries * ETH_ALEN) { goto error; } - n->mac_table.in_use += mac_data.entries; + in_use += mac_data.entries; } else { - n->mac_table.uni_overflow = 1; + uni_overflow = 1; } iov_discard_front(&iov, &iov_cnt, mac_data.entries * ETH_ALEN); - n->mac_table.first_multi = n->mac_table.in_use; + first_multi = in_use; s = iov_to_buf(iov, iov_cnt, 0, &mac_data.entries, sizeof(mac_data.entries)); @@ -656,24 +656,29 @@ static int virtio_net_handle_mac(VirtIONet *n, uint8_t cmd, goto error; } - if (n->mac_table.in_use + mac_data.entries <= MAC_TABLE_ENTRIES) { - s = iov_to_buf(iov, iov_cnt, 0, - &n->mac_table.macs[n->mac_table.in_use * ETH_ALEN], + if (in_use + mac_data.entries <= MAC_TABLE_ENTRIES) { + s = iov_to_buf(iov, iov_cnt, 0, &macs[in_use * ETH_ALEN], mac_data.entries * ETH_ALEN); if (s != mac_data.entries * ETH_ALEN) { goto error; } - n->mac_table.in_use += mac_data.entries; + in_use += mac_data.entries; } else { - n->mac_table.multi_overflow = 1; + multi_overflow = 1; } + n->mac_table.in_use = in_use; + n->mac_table.first_multi = first_multi; + n->mac_table.uni_overflow = uni_overflow; + n->mac_table.multi_overflow = multi_overflow; + memcpy(n->mac_table.macs, macs, MAC_TABLE_ENTRIES * ETH_ALEN); + g_free(macs); rxfilter_notify(nc); return VIRTIO_NET_OK; error: - rxfilter_notify(nc); + g_free(macs); return VIRTIO_NET_ERR; } @@ -1428,7 +1433,7 @@ static NetClientInfo net_virtio_info = { .size = sizeof(NICState), .can_receive = virtio_net_can_receive, .receive = virtio_net_receive, - .cleanup = virtio_net_cleanup, + .cleanup = virtio_net_cleanup, .link_status_changed = virtio_net_set_link_status, .query_rx_filter = virtio_net_query_rxfilter, }; diff --git a/hw/nvram/eeprom93xx.c b/hw/nvram/eeprom93xx.c index 08f4df586c..a98f924b81 100644 --- a/hw/nvram/eeprom93xx.c +++ b/hw/nvram/eeprom93xx.c @@ -126,7 +126,7 @@ static const VMStateDescription vmstate_eeprom = { .version_id = EEPROM_VERSION, .minimum_version_id = OLD_EEPROM_VERSION, .minimum_version_id_old = OLD_EEPROM_VERSION, - .fields = (VMStateField []) { + .fields = (VMStateField[]) { VMSTATE_UINT8(tick, eeprom_t), VMSTATE_UINT8(address, eeprom_t), VMSTATE_UINT8(command, eeprom_t), @@ -157,13 +157,13 @@ void eeprom93xx_write(eeprom_t *eeprom, int eecs, int eesk, int eedi) logout("CS=%u SK=%u DI=%u DO=%u, tick = %u\n", eecs, eesk, eedi, eedo, tick); - if (! eeprom->eecs && eecs) { + if (!eeprom->eecs && eecs) { /* Start chip select cycle. */ logout("Cycle start, waiting for 1st start bit (0)\n"); tick = 0; command = 0x0; address = 0x0; - } else if (eeprom->eecs && ! eecs) { + } else if (eeprom->eecs && !eecs) { /* End chip select cycle. This triggers write / erase. */ if (eeprom->writable) { uint8_t subcommand = address >> (eeprom->addrbits - 2); @@ -189,7 +189,7 @@ void eeprom93xx_write(eeprom_t *eeprom, int eecs, int eesk, int eedi) } /* Output DO is tristate, read results in 1. */ eedo = 1; - } else if (eecs && ! eeprom->eesk && eesk) { + } else if (eecs && !eeprom->eesk && eesk) { /* Raising edge of clock shifts data in. */ if (tick == 0) { /* Wait for 1st start bit. */ @@ -230,20 +230,20 @@ void eeprom93xx_write(eeprom_t *eeprom, int eecs, int eesk, int eedi) if (command == 0) { /* Command code in upper 2 bits of address. */ switch (address >> (eeprom->addrbits - 2)) { - case 0: - logout("write disable command\n"); - eeprom->writable = 0; - break; - case 1: - logout("write all command\n"); - break; - case 2: - logout("erase all command\n"); - break; - case 3: - logout("write enable command\n"); - eeprom->writable = 1; - break; + case 0: + logout("write disable command\n"); + eeprom->writable = 0; + break; + case 1: + logout("write all command\n"); + break; + case 2: + logout("erase all command\n"); + break; + case 3: + logout("write enable command\n"); + eeprom->writable = 1; + break; } } else { /* Read, write or erase word. */ @@ -276,7 +276,7 @@ uint16_t eeprom93xx_read(eeprom_t *eeprom) { /* Return status of pin DO (0 or 1). */ logout("CS=%u DO=%u\n", eeprom->eecs, eeprom->eedo); - return (eeprom->eedo); + return eeprom->eedo; } #if 0 @@ -296,18 +296,18 @@ eeprom_t *eeprom93xx_new(DeviceState *dev, uint16_t nwords) uint8_t addrbits; switch (nwords) { - case 16: - case 64: - addrbits = 6; - break; - case 128: - case 256: - addrbits = 8; - break; - default: - assert(!"Unsupported EEPROM size, fallback to 64 words!"); - nwords = 64; - addrbits = 6; + case 16: + case 64: + addrbits = 6; + break; + case 128: + case 256: + addrbits = 8; + break; + default: + assert(!"Unsupported EEPROM size, fallback to 64 words!"); + nwords = 64; + addrbits = 6; } eeprom = (eeprom_t *)g_malloc0(sizeof(*eeprom) + nwords * 2); diff --git a/hw/ppc/mac.h b/hw/ppc/mac.h index 1e578dd59d..c1faf9ce27 100644 --- a/hw/ppc/mac.h +++ b/hw/ppc/mac.h @@ -34,7 +34,6 @@ #define MAX_CPUS 1 #define BIOS_SIZE (1024 * 1024) -#define BIOS_FILENAME "ppc_rom.bin" #define NVRAM_SIZE 0x2000 #define PROM_FILENAME "openbios-ppc" #define PROM_ADDR 0xfff00000 diff --git a/hw/ppc/ppc.c b/hw/ppc/ppc.c index bf2d3d4b35..114be64480 100644 --- a/hw/ppc/ppc.c +++ b/hw/ppc/ppc.c @@ -684,7 +684,7 @@ static inline void cpu_ppc_hdecr_excp(PowerPCCPU *cpu) } static void __cpu_ppc_store_decr(PowerPCCPU *cpu, uint64_t *nextp, - struct QEMUTimer *timer, + QEMUTimer *timer, void (*raise_excp)(PowerPCCPU *), uint32_t decr, uint32_t value, int is_excp) @@ -856,9 +856,9 @@ typedef struct ppc40x_timer_t ppc40x_timer_t; struct ppc40x_timer_t { uint64_t pit_reload; /* PIT auto-reload value */ uint64_t fit_next; /* Tick for next FIT interrupt */ - struct QEMUTimer *fit_timer; + QEMUTimer *fit_timer; uint64_t wdt_next; /* Tick for next WDT interrupt */ - struct QEMUTimer *wdt_timer; + QEMUTimer *wdt_timer; /* 405 have the PIT, 440 have a DECR. */ unsigned int decr_excp; diff --git a/hw/ppc/ppc405_uc.c b/hw/ppc/ppc405_uc.c index 6d6a7f1203..8109f92200 100644 --- a/hw/ppc/ppc405_uc.c +++ b/hw/ppc/ppc405_uc.c @@ -1234,7 +1234,7 @@ struct ppc4xx_gpt_t { MemoryRegion iomem; int64_t tb_offset; uint32_t tb_freq; - struct QEMUTimer *timer; + QEMUTimer *timer; qemu_irq irqs[5]; uint32_t oe; uint32_t ol; diff --git a/hw/ppc/ppc_booke.c b/hw/ppc/ppc_booke.c index b421620708..d8399602d6 100644 --- a/hw/ppc/ppc_booke.c +++ b/hw/ppc/ppc_booke.c @@ -64,10 +64,10 @@ typedef struct booke_timer_t booke_timer_t; struct booke_timer_t { uint64_t fit_next; - struct QEMUTimer *fit_timer; + QEMUTimer *fit_timer; uint64_t wdt_next; - struct QEMUTimer *wdt_timer; + QEMUTimer *wdt_timer; uint32_t flags; }; diff --git a/hw/scsi/scsi-bus.c b/hw/scsi/scsi-bus.c index 2d6ce4d6bb..3496c0bbd8 100644 --- a/hw/scsi/scsi-bus.c +++ b/hw/scsi/scsi-bus.c @@ -1305,6 +1305,11 @@ const struct SCSISense sense_code_ILLEGAL_REQ_REMOVAL_PREVENTED = { .key = ILLEGAL_REQUEST, .asc = 0x53, .ascq = 0x02 }; +/* Illegal request, Invalid Transfer Tag */ +const struct SCSISense sense_code_INVALID_TAG = { + .key = ILLEGAL_REQUEST, .asc = 0x4b, .ascq = 0x01 +}; + /* Command aborted, I/O process terminated */ const struct SCSISense sense_code_IO_ERROR = { .key = ABORTED_COMMAND, .asc = 0x00, .ascq = 0x06 @@ -1320,6 +1325,11 @@ const struct SCSISense sense_code_LUN_FAILURE = { .key = ABORTED_COMMAND, .asc = 0x3e, .ascq = 0x01 }; +/* Command aborted, Overlapped Commands Attempted */ +const struct SCSISense sense_code_OVERLAPPED_COMMANDS = { + .key = ABORTED_COMMAND, .asc = 0x4e, .ascq = 0x00 +}; + /* Unit attention, Capacity data has changed */ const struct SCSISense sense_code_CAPACITY_CHANGED = { .key = UNIT_ATTENTION, .asc = 0x2a, .ascq = 0x09 diff --git a/hw/timer/Makefile.objs b/hw/timer/Makefile.objs index eca590570e..3ae091c95e 100644 --- a/hw/timer/Makefile.objs +++ b/hw/timer/Makefile.objs @@ -1,5 +1,6 @@ common-obj-$(CONFIG_ARM_TIMER) += arm_timer.o common-obj-$(CONFIG_ARM_MPTIMER) += arm_mptimer.o +common-obj-$(CONFIG_A9_GTIMER) += a9gtimer.o common-obj-$(CONFIG_CADENCE) += cadence_ttc.o common-obj-$(CONFIG_DS1338) += ds1338.o common-obj-$(CONFIG_HPET) += hpet.o diff --git a/hw/timer/a9gtimer.c b/hw/timer/a9gtimer.c new file mode 100644 index 0000000000..a0656d58a1 --- /dev/null +++ b/hw/timer/a9gtimer.c @@ -0,0 +1,369 @@ +/* + * Global peripheral timer block for ARM A9MP + * + * (C) 2013 Xilinx Inc. + * + * Written by François LEGAL + * Written by Peter Crosthwaite <peter.crosthwaite@xilinx.com> + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, see <http://www.gnu.org/licenses/>. + */ + +#include "hw/timer/a9gtimer.h" +#include "qemu/timer.h" +#include "qemu/bitops.h" +#include "qemu/log.h" + +#ifndef A9_GTIMER_ERR_DEBUG +#define A9_GTIMER_ERR_DEBUG 0 +#endif + +#define DB_PRINT_L(level, ...) do { \ + if (A9_GTIMER_ERR_DEBUG > (level)) { \ + fprintf(stderr, ": %s: ", __func__); \ + fprintf(stderr, ## __VA_ARGS__); \ + } \ +} while (0); + +#define DB_PRINT(...) DB_PRINT_L(0, ## __VA_ARGS__) + +static inline int a9_gtimer_get_current_cpu(A9GTimerState *s) +{ + if (current_cpu->cpu_index >= s->num_cpu) { + hw_error("a9gtimer: num-cpu %d but this cpu is %d!\n", + s->num_cpu, current_cpu->cpu_index); + } + return current_cpu->cpu_index; +} + +static inline uint64_t a9_gtimer_get_conv(A9GTimerState *s) +{ + uint64_t prescale = extract32(s->control, R_CONTROL_PRESCALER_SHIFT, + R_CONTROL_PRESCALER_LEN); + + return (prescale + 1) * 10; +} + +static A9GTimerUpdate a9_gtimer_get_update(A9GTimerState *s) +{ + A9GTimerUpdate ret; + + ret.now = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL); + ret.new = s->ref_counter + + (ret.now - s->cpu_ref_time) / a9_gtimer_get_conv(s); + return ret; +} + +static void a9_gtimer_update(A9GTimerState *s, bool sync) +{ + + A9GTimerUpdate update = a9_gtimer_get_update(s); + int i; + int64_t next_cdiff = 0; + + for (i = 0; i < s->num_cpu; ++i) { + A9GTimerPerCPU *gtb = &s->per_cpu[i]; + int64_t cdiff = 0; + + if ((s->control & R_CONTROL_TIMER_ENABLE) && + (gtb->control & R_CONTROL_COMP_ENABLE)) { + /* R2p0+, where the compare function is >= */ + while (gtb->compare < update.new) { + DB_PRINT("Compare event happened for CPU %d\n", i); + gtb->status = 1; + if (gtb->control & R_CONTROL_AUTO_INCREMENT) { + DB_PRINT("Auto incrementing timer compare by %" PRId32 "\n", + gtb->inc); + gtb->compare += gtb->inc; + } else { + break; + } + } + cdiff = (int64_t)gtb->compare - (int64_t)update.new + 1; + if (cdiff > 0 && (cdiff < next_cdiff || !next_cdiff)) { + next_cdiff = cdiff; + } + } + + qemu_set_irq(gtb->irq, + gtb->status && (gtb->control & R_CONTROL_IRQ_ENABLE)); + } + + timer_del(s->timer); + if (next_cdiff) { + DB_PRINT("scheduling qemu_timer to fire again in %" + PRIx64 " cycles\n", next_cdiff); + timer_mod(s->timer, update.now + next_cdiff * a9_gtimer_get_conv(s)); + } + + if (s->control & R_CONTROL_TIMER_ENABLE) { + s->counter = update.new; + } + + if (sync) { + s->cpu_ref_time = update.now; + s->ref_counter = s->counter; + } +} + +static void a9_gtimer_update_no_sync(void *opaque) +{ + A9GTimerState *s = A9_GTIMER(opaque); + + return a9_gtimer_update(s, false); +} + +static uint64_t a9_gtimer_read(void *opaque, hwaddr addr, unsigned size) +{ + A9GTimerPerCPU *gtb = (A9GTimerPerCPU *)opaque; + A9GTimerState *s = gtb->parent; + A9GTimerUpdate update; + uint64_t ret = 0; + int shift = 0; + + switch (addr) { + case R_COUNTER_HI: + shift = 32; + /* fallthrough */ + case R_COUNTER_LO: + update = a9_gtimer_get_update(s); + ret = extract64(update.new, shift, 32); + break; + case R_CONTROL: + ret = s->control | gtb->control; + break; + case R_INTERRUPT_STATUS: + ret = gtb->status; + break; + case R_COMPARATOR_HI: + shift = 32; + /* fallthrough */ + case R_COMPARATOR_LO: + ret = extract64(gtb->compare, shift, 32); + break; + case R_AUTO_INCREMENT: + ret = gtb->inc; + break; + default: + qemu_log_mask(LOG_GUEST_ERROR, "bad a9gtimer register: %x\n", + (unsigned)addr); + return 0; + } + + DB_PRINT("addr:%#x data:%#08" PRIx64 "\n", (unsigned)addr, ret); + return ret; +} + +static void a9_gtimer_write(void *opaque, hwaddr addr, uint64_t value, + unsigned size) +{ + A9GTimerPerCPU *gtb = (A9GTimerPerCPU *)opaque; + A9GTimerState *s = gtb->parent; + int shift = 0; + + DB_PRINT("addr:%#x data:%#08" PRIx64 "\n", (unsigned)addr, value); + + switch (addr) { + case R_COUNTER_HI: + shift = 32; + /* fallthrough */ + case R_COUNTER_LO: + /* + * Keep it simple - ARM docco explicitly says to disable timer before + * modding it, so dont bother trying to do all the difficult on the fly + * timer modifications - (if they even work in real hardware??). + */ + if (s->control & R_CONTROL_TIMER_ENABLE) { + qemu_log_mask(LOG_GUEST_ERROR, "Cannot mod running ARM gtimer\n"); + return; + } + s->counter = deposit64(s->counter, shift, 32, value); + return; + case R_CONTROL: + a9_gtimer_update(s, (value ^ s->control) & R_CONTROL_NEEDS_SYNC); + gtb->control = value & R_CONTROL_BANKED; + s->control = value & ~R_CONTROL_BANKED; + break; + case R_INTERRUPT_STATUS: + a9_gtimer_update(s, false); + gtb->status &= ~value; + break; + case R_COMPARATOR_HI: + shift = 32; + /* fallthrough */ + case R_COMPARATOR_LO: + a9_gtimer_update(s, false); + gtb->compare = deposit64(gtb->compare, shift, 32, value); + break; + case R_AUTO_INCREMENT: + gtb->inc = value; + return; + default: + return; + } + + a9_gtimer_update(s, false); +} + +/* Wrapper functions to implement the "read global timer for + * the current CPU" memory regions. + */ +static uint64_t a9_gtimer_this_read(void *opaque, hwaddr addr, + unsigned size) +{ + A9GTimerState *s = A9_GTIMER(opaque); + int id = a9_gtimer_get_current_cpu(s); + + /* no \n so concatenates with message from read fn */ + DB_PRINT("CPU:%d:", id); + + return a9_gtimer_read(&s->per_cpu[id], addr, size); +} + +static void a9_gtimer_this_write(void *opaque, hwaddr addr, + uint64_t value, unsigned size) +{ + A9GTimerState *s = A9_GTIMER(opaque); + int id = a9_gtimer_get_current_cpu(s); + + /* no \n so concatenates with message from write fn */ + DB_PRINT("CPU:%d:", id); + + a9_gtimer_write(&s->per_cpu[id], addr, value, size); +} + +static const MemoryRegionOps a9_gtimer_this_ops = { + .read = a9_gtimer_this_read, + .write = a9_gtimer_this_write, + .valid = { + .min_access_size = 4, + .max_access_size = 4, + }, + .endianness = DEVICE_NATIVE_ENDIAN, +}; + +static const MemoryRegionOps a9_gtimer_ops = { + .read = a9_gtimer_read, + .write = a9_gtimer_write, + .valid = { + .min_access_size = 4, + .max_access_size = 4, + }, + .endianness = DEVICE_NATIVE_ENDIAN, +}; + +static void a9_gtimer_reset(DeviceState *dev) +{ + A9GTimerState *s = A9_GTIMER(dev); + int i; + + s->counter = 0; + s->control = 0; + + for (i = 0; i < s->num_cpu; i++) { + A9GTimerPerCPU *gtb = &s->per_cpu[i]; + + gtb->control = 0; + gtb->status = 0; + gtb->compare = 0; + gtb->inc = 0; + } + a9_gtimer_update(s, false); +} + +static void a9_gtimer_realize(DeviceState *dev, Error **errp) +{ + A9GTimerState *s = A9_GTIMER(dev); + SysBusDevice *sbd = SYS_BUS_DEVICE(dev); + int i; + + if (s->num_cpu < 1 || s->num_cpu > A9_GTIMER_MAX_CPUS) { + error_setg(errp, "%s: num-cpu must be between 1 and %d\n", + __func__, A9_GTIMER_MAX_CPUS); + return; + } + + memory_region_init_io(&s->iomem, OBJECT(dev), &a9_gtimer_this_ops, s, + "a9gtimer shared", 0x20); + sysbus_init_mmio(sbd, &s->iomem); + s->timer = timer_new_ns(QEMU_CLOCK_VIRTUAL, a9_gtimer_update_no_sync, s); + + for (i = 0; i < s->num_cpu; i++) { + A9GTimerPerCPU *gtb = &s->per_cpu[i]; + + gtb->parent = s; + sysbus_init_irq(sbd, >b->irq); + memory_region_init_io(>b->iomem, OBJECT(dev), &a9_gtimer_ops, gtb, + "a9gtimer per cpu", 0x20); + sysbus_init_mmio(sbd, >b->iomem); + } +} + +static const VMStateDescription vmstate_a9_gtimer_per_cpu = { + .name = "arm.cortex-a9-global-timer.percpu", + .version_id = 1, + .minimum_version_id = 1, + .fields = (VMStateField[]) { + VMSTATE_UINT32(control, A9GTimerPerCPU), + VMSTATE_UINT64(compare, A9GTimerPerCPU), + VMSTATE_UINT32(status, A9GTimerPerCPU), + VMSTATE_UINT32(inc, A9GTimerPerCPU), + VMSTATE_END_OF_LIST() + } +}; + +static const VMStateDescription vmstate_a9_gtimer = { + .name = "arm.cortex-a9-global-timer", + .version_id = 1, + .minimum_version_id = 1, + .fields = (VMStateField[]) { + VMSTATE_TIMER(timer, A9GTimerState), + VMSTATE_UINT64(counter, A9GTimerState), + VMSTATE_UINT64(ref_counter, A9GTimerState), + VMSTATE_UINT64(cpu_ref_time, A9GTimerState), + VMSTATE_STRUCT_VARRAY_UINT32(per_cpu, A9GTimerState, num_cpu, + 1, vmstate_a9_gtimer_per_cpu, + A9GTimerPerCPU), + VMSTATE_END_OF_LIST() + } +}; + +static Property a9_gtimer_properties[] = { + DEFINE_PROP_UINT32("num-cpu", A9GTimerState, num_cpu, 0), + DEFINE_PROP_END_OF_LIST() +}; + +static void a9_gtimer_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + + dc->realize = a9_gtimer_realize; + dc->vmsd = &vmstate_a9_gtimer; + dc->reset = a9_gtimer_reset; + dc->props = a9_gtimer_properties; +} + +static const TypeInfo a9_gtimer_info = { + .name = TYPE_A9_GTIMER, + .parent = TYPE_SYS_BUS_DEVICE, + .instance_size = sizeof(A9GTimerState), + .class_init = a9_gtimer_class_init, +}; + +static void a9_gtimer_register_types(void) +{ + type_register_static(&a9_gtimer_info); +} + +type_init(a9_gtimer_register_types) diff --git a/hw/timer/m48t59.c b/hw/timer/m48t59.c index d3d78ec5a8..be0592b53d 100644 --- a/hw/timer/m48t59.c +++ b/hw/timer/m48t59.c @@ -61,8 +61,8 @@ struct M48t59State { time_t stop_time; /* Alarm & watchdog */ struct tm alarm; - struct QEMUTimer *alrm_timer; - struct QEMUTimer *wd_timer; + QEMUTimer *alrm_timer; + QEMUTimer *wd_timer; /* NVRAM storage */ uint8_t *buffer; /* Model parameters */ diff --git a/hw/usb/bus.c b/hw/usb/bus.c index ca329bef29..09848c6320 100644 --- a/hw/usb/bus.c +++ b/hw/usb/bus.c @@ -203,6 +203,24 @@ void usb_device_ep_stopped(USBDevice *dev, USBEndpoint *ep) } } +int usb_device_alloc_streams(USBDevice *dev, USBEndpoint **eps, int nr_eps, + int streams) +{ + USBDeviceClass *klass = USB_DEVICE_GET_CLASS(dev); + if (klass->alloc_streams) { + return klass->alloc_streams(dev, eps, nr_eps, streams); + } + return 0; +} + +void usb_device_free_streams(USBDevice *dev, USBEndpoint **eps, int nr_eps) +{ + USBDeviceClass *klass = USB_DEVICE_GET_CLASS(dev); + if (klass->free_streams) { + klass->free_streams(dev, eps, nr_eps); + } +} + static int usb_qdev_init(DeviceState *qdev) { USBDevice *dev = USB_DEVICE(qdev); diff --git a/hw/usb/core.c b/hw/usb/core.c index cf59a1abcf..67ba7d6018 100644 --- a/hw/usb/core.c +++ b/hw/usb/core.c @@ -623,6 +623,7 @@ void usb_ep_reset(USBDevice *dev) dev->ep_ctl.type = USB_ENDPOINT_XFER_CONTROL; dev->ep_ctl.ifnum = 0; dev->ep_ctl.max_packet_size = 64; + dev->ep_ctl.max_streams = 0; dev->ep_ctl.dev = dev; dev->ep_ctl.pipeline = false; for (ep = 0; ep < USB_MAX_ENDPOINTS; ep++) { @@ -636,6 +637,8 @@ void usb_ep_reset(USBDevice *dev) dev->ep_out[ep].ifnum = USB_INTERFACE_INVALID; dev->ep_in[ep].max_packet_size = 0; dev->ep_out[ep].max_packet_size = 0; + dev->ep_in[ep].max_streams = 0; + dev->ep_out[ep].max_streams = 0; dev->ep_in[ep].dev = dev; dev->ep_out[ep].dev = dev; dev->ep_in[ep].pipeline = false; @@ -764,6 +767,25 @@ int usb_ep_get_max_packet_size(USBDevice *dev, int pid, int ep) return uep->max_packet_size; } +void usb_ep_set_max_streams(USBDevice *dev, int pid, int ep, uint8_t raw) +{ + struct USBEndpoint *uep = usb_ep_get(dev, pid, ep); + int MaxStreams; + + MaxStreams = raw & 0x1f; + if (MaxStreams) { + uep->max_streams = 1 << MaxStreams; + } else { + uep->max_streams = 0; + } +} + +int usb_ep_get_max_streams(USBDevice *dev, int pid, int ep) +{ + struct USBEndpoint *uep = usb_ep_get(dev, pid, ep); + return uep->max_streams; +} + void usb_ep_set_pipeline(USBDevice *dev, int pid, int ep, bool enabled) { struct USBEndpoint *uep = usb_ep_get(dev, pid, ep); diff --git a/hw/usb/desc.c b/hw/usb/desc.c index bf6c522682..f18a043500 100644 --- a/hw/usb/desc.c +++ b/hw/usb/desc.c @@ -6,16 +6,6 @@ /* ------------------------------------------------------------------ */ -static uint8_t usb_lo(uint16_t val) -{ - return val & 0xff; -} - -static uint8_t usb_hi(uint16_t val) -{ - return (val >> 8) & 0xff; -} - int usb_desc_device(const USBDescID *id, const USBDescDevice *dev, uint8_t *dest, size_t len) { @@ -385,6 +375,8 @@ static void usb_desc_ep_init(USBDevice *dev) usb_ep_set_ifnum(dev, pid, ep, iface->bInterfaceNumber); usb_ep_set_max_packet_size(dev, pid, ep, iface->eps[e].wMaxPacketSize); + usb_ep_set_max_streams(dev, pid, ep, + iface->eps[e].bmAttributes_super); } } } diff --git a/hw/usb/desc.h b/hw/usb/desc.h index ddd3e7485c..81327b0e74 100644 --- a/hw/usb/desc.h +++ b/hw/usb/desc.h @@ -194,6 +194,17 @@ struct USBDesc { #define USB_DESC_FLAG_SUPER (1 << 1) +/* little helpers */ +static inline uint8_t usb_lo(uint16_t val) +{ + return val & 0xff; +} + +static inline uint8_t usb_hi(uint16_t val) +{ + return (val >> 8) & 0xff; +} + /* generate usb packages from structs */ int usb_desc_device(const USBDescID *id, const USBDescDevice *dev, uint8_t *dest, size_t len); diff --git a/hw/usb/dev-hid.c b/hw/usb/dev-hid.c index 59567200ae..5e667f0199 100644 --- a/hw/usb/dev-hid.c +++ b/hw/usb/dev-hid.c @@ -236,7 +236,7 @@ static const USBDescDevice desc_device_tablet2 = { .bNumInterfaces = 1, .bConfigurationValue = 1, .iConfiguration = STR_CONFIG_TABLET, - .bmAttributes = 0x80, + .bmAttributes = 0xa0, .bMaxPower = 50, .nif = 1, .ifs = &desc_iface_tablet2, diff --git a/hw/usb/dev-uas.c b/hw/usb/dev-uas.c index 70ed2d1dbd..997b715952 100644 --- a/hw/usb/dev-uas.c +++ b/hw/usb/dev-uas.c @@ -55,7 +55,7 @@ typedef struct { uint8_t id; uint8_t reserved; uint16_t tag; -} QEMU_PACKED uas_ui_header; +} QEMU_PACKED uas_iu_header; typedef struct { uint8_t prio_taskattr; /* 6:3 priority, 2:0 task attribute */ @@ -65,7 +65,7 @@ typedef struct { uint64_t lun; uint8_t cdb[16]; uint8_t add_cdb[]; -} QEMU_PACKED uas_ui_command; +} QEMU_PACKED uas_iu_command; typedef struct { uint16_t status_qualifier; @@ -73,29 +73,29 @@ typedef struct { uint8_t reserved[7]; uint16_t sense_length; uint8_t sense_data[18]; -} QEMU_PACKED uas_ui_sense; +} QEMU_PACKED uas_iu_sense; typedef struct { - uint16_t add_response_info; + uint8_t add_response_info[3]; uint8_t response_code; -} QEMU_PACKED uas_ui_response; +} QEMU_PACKED uas_iu_response; typedef struct { uint8_t function; uint8_t reserved; uint16_t task_tag; uint64_t lun; -} QEMU_PACKED uas_ui_task_mgmt; +} QEMU_PACKED uas_iu_task_mgmt; typedef struct { - uas_ui_header hdr; + uas_iu_header hdr; union { - uas_ui_command command; - uas_ui_sense sense; - uas_ui_task_mgmt task; - uas_ui_response response; + uas_iu_command command; + uas_iu_sense sense; + uas_iu_task_mgmt task; + uas_iu_response response; }; -} QEMU_PACKED uas_ui; +} QEMU_PACKED uas_iu; /* --------------------------------------------------------------------- */ @@ -122,8 +122,8 @@ struct UASDevice { UASRequest *dataout2; /* usb 3.0 only */ - USBPacket *data3[UAS_MAX_STREAMS]; - USBPacket *status3[UAS_MAX_STREAMS]; + USBPacket *data3[UAS_MAX_STREAMS + 1]; + USBPacket *status3[UAS_MAX_STREAMS + 1]; }; struct UASRequest { @@ -145,7 +145,7 @@ struct UASRequest { struct UASStatus { uint32_t stream; - uas_ui status; + uas_iu status; uint32_t length; QTAILQ_ENTRY(UASStatus) next; }; @@ -338,7 +338,7 @@ static UASStatus *usb_uas_alloc_status(UASDevice *uas, uint8_t id, uint16_t tag) st->status.hdr.id = id; st->status.hdr.tag = cpu_to_be16(tag); - st->length = sizeof(uas_ui_header); + st->length = sizeof(uas_iu_header); if (uas_using_streams(uas)) { st->stream = tag; } @@ -392,15 +392,13 @@ static void usb_uas_queue_status(UASDevice *uas, UASStatus *st, int length) } } -static void usb_uas_queue_response(UASDevice *uas, uint16_t tag, - uint8_t code, uint16_t add_info) +static void usb_uas_queue_response(UASDevice *uas, uint16_t tag, uint8_t code) { UASStatus *st = usb_uas_alloc_status(uas, UAS_UI_RESPONSE, tag); trace_usb_uas_response(uas->dev.addr, tag, code); st->status.response.response_code = code; - st->status.response.add_response_info = cpu_to_be16(add_info); - usb_uas_queue_status(uas, st, sizeof(uas_ui_response)); + usb_uas_queue_status(uas, st, sizeof(uas_iu_response)); } static void usb_uas_queue_sense(UASRequest *req, uint8_t status) @@ -416,10 +414,28 @@ static void usb_uas_queue_sense(UASRequest *req, uint8_t status) sizeof(st->status.sense.sense_data)); st->status.sense.sense_length = cpu_to_be16(slen); } - len = sizeof(uas_ui_sense) - sizeof(st->status.sense.sense_data) + slen; + len = sizeof(uas_iu_sense) - sizeof(st->status.sense.sense_data) + slen; usb_uas_queue_status(req->uas, st, len); } +static void usb_uas_queue_fake_sense(UASDevice *uas, uint16_t tag, + struct SCSISense sense) +{ + UASStatus *st = usb_uas_alloc_status(uas, UAS_UI_SENSE, tag); + int len, slen = 0; + + st->status.sense.status = CHECK_CONDITION; + st->status.sense.status_qualifier = cpu_to_be16(0); + st->status.sense.sense_data[0] = 0x70; + st->status.sense.sense_data[2] = sense.key; + st->status.sense.sense_data[7] = 10; + st->status.sense.sense_data[12] = sense.asc; + st->status.sense.sense_data[13] = sense.ascq; + slen = 18; + len = sizeof(uas_iu_sense) - sizeof(st->status.sense.sense_data) + slen; + usb_uas_queue_status(uas, st, len); +} + static void usb_uas_queue_read_ready(UASRequest *req) { UASStatus *st = usb_uas_alloc_status(req->uas, UAS_UI_READ_READY, @@ -518,14 +534,14 @@ static void usb_uas_start_next_transfer(UASDevice *uas) } } -static UASRequest *usb_uas_alloc_request(UASDevice *uas, uas_ui *ui) +static UASRequest *usb_uas_alloc_request(UASDevice *uas, uas_iu *iu) { UASRequest *req; req = g_new0(UASRequest, 1); req->uas = uas; - req->tag = be16_to_cpu(ui->hdr.tag); - req->lun = be64_to_cpu(ui->command.lun); + req->tag = be16_to_cpu(iu->hdr.tag); + req->lun = be64_to_cpu(iu->command.lun); req->dev = usb_uas_get_dev(req->uas, req->lun); return req; } @@ -648,7 +664,7 @@ static void usb_uas_cancel_io(USBDevice *dev, USBPacket *p) return; } if (uas_using_streams(uas)) { - for (i = 0; i < UAS_MAX_STREAMS; i++) { + for (i = 0; i <= UAS_MAX_STREAMS; i++) { if (uas->status3[i] == p) { uas->status3[i] = NULL; return; @@ -668,16 +684,20 @@ static void usb_uas_cancel_io(USBDevice *dev, USBPacket *p) assert(!"canceled usb packet not found"); } -static void usb_uas_command(UASDevice *uas, uas_ui *ui) +static void usb_uas_command(UASDevice *uas, uas_iu *iu) { UASRequest *req; uint32_t len; + uint16_t tag = be16_to_cpu(iu->hdr.tag); - req = usb_uas_find_request(uas, be16_to_cpu(ui->hdr.tag)); + if (uas_using_streams(uas) && tag > UAS_MAX_STREAMS) { + goto invalid_tag; + } + req = usb_uas_find_request(uas, tag); if (req) { goto overlapped_tag; } - req = usb_uas_alloc_request(uas, ui); + req = usb_uas_alloc_request(uas, iu); if (req->dev == NULL) { goto bad_target; } @@ -694,7 +714,7 @@ static void usb_uas_command(UASDevice *uas, uas_ui *ui) req->req = scsi_req_new(req->dev, req->tag, usb_uas_get_lun(req->lun), - ui->command.cdb, req); + iu->command.cdb, req); if (uas->requestlog) { scsi_req_print(req->req); } @@ -705,105 +725,97 @@ static void usb_uas_command(UASDevice *uas, uas_ui *ui) } return; +invalid_tag: + usb_uas_queue_fake_sense(uas, tag, sense_code_INVALID_TAG); + return; + overlapped_tag: - usb_uas_queue_response(uas, req->tag, UAS_RC_OVERLAPPED_TAG, 0); + usb_uas_queue_fake_sense(uas, tag, sense_code_OVERLAPPED_COMMANDS); return; bad_target: - /* - * FIXME: Seems to upset linux, is this wrong? - * NOTE: Happens only with no scsi devices at the bus, not sure - * this is a valid UAS setup in the first place. - */ - usb_uas_queue_response(uas, req->tag, UAS_RC_INVALID_INFO_UNIT, 0); + usb_uas_queue_fake_sense(uas, tag, sense_code_LUN_NOT_SUPPORTED); g_free(req); } -static void usb_uas_task(UASDevice *uas, uas_ui *ui) +static void usb_uas_task(UASDevice *uas, uas_iu *iu) { - uint16_t tag = be16_to_cpu(ui->hdr.tag); - uint64_t lun64 = be64_to_cpu(ui->task.lun); + uint16_t tag = be16_to_cpu(iu->hdr.tag); + uint64_t lun64 = be64_to_cpu(iu->task.lun); SCSIDevice *dev = usb_uas_get_dev(uas, lun64); int lun = usb_uas_get_lun(lun64); UASRequest *req; uint16_t task_tag; - req = usb_uas_find_request(uas, be16_to_cpu(ui->hdr.tag)); + if (uas_using_streams(uas) && tag > UAS_MAX_STREAMS) { + goto invalid_tag; + } + req = usb_uas_find_request(uas, be16_to_cpu(iu->hdr.tag)); if (req) { goto overlapped_tag; } + if (dev == NULL) { + goto incorrect_lun; + } - switch (ui->task.function) { + switch (iu->task.function) { case UAS_TMF_ABORT_TASK: - task_tag = be16_to_cpu(ui->task.task_tag); + task_tag = be16_to_cpu(iu->task.task_tag); trace_usb_uas_tmf_abort_task(uas->dev.addr, tag, task_tag); - if (dev == NULL) { - goto bad_target; - } - if (dev->lun != lun) { - goto incorrect_lun; - } req = usb_uas_find_request(uas, task_tag); if (req && req->dev == dev) { scsi_req_cancel(req->req); } - usb_uas_queue_response(uas, tag, UAS_RC_TMF_COMPLETE, 0); + usb_uas_queue_response(uas, tag, UAS_RC_TMF_COMPLETE); break; case UAS_TMF_LOGICAL_UNIT_RESET: trace_usb_uas_tmf_logical_unit_reset(uas->dev.addr, tag, lun); - if (dev == NULL) { - goto bad_target; - } - if (dev->lun != lun) { - goto incorrect_lun; - } qdev_reset_all(&dev->qdev); - usb_uas_queue_response(uas, tag, UAS_RC_TMF_COMPLETE, 0); + usb_uas_queue_response(uas, tag, UAS_RC_TMF_COMPLETE); break; default: - trace_usb_uas_tmf_unsupported(uas->dev.addr, tag, ui->task.function); - usb_uas_queue_response(uas, tag, UAS_RC_TMF_NOT_SUPPORTED, 0); + trace_usb_uas_tmf_unsupported(uas->dev.addr, tag, iu->task.function); + usb_uas_queue_response(uas, tag, UAS_RC_TMF_NOT_SUPPORTED); break; } return; -overlapped_tag: - usb_uas_queue_response(uas, req->tag, UAS_RC_OVERLAPPED_TAG, 0); +invalid_tag: + usb_uas_queue_response(uas, tag, UAS_RC_INVALID_INFO_UNIT); return; -bad_target: - /* FIXME: correct? [see long comment in usb_uas_command()] */ - usb_uas_queue_response(uas, tag, UAS_RC_INVALID_INFO_UNIT, 0); +overlapped_tag: + usb_uas_queue_response(uas, req->tag, UAS_RC_OVERLAPPED_TAG); return; incorrect_lun: - usb_uas_queue_response(uas, tag, UAS_RC_INCORRECT_LUN, 0); + usb_uas_queue_response(uas, tag, UAS_RC_INCORRECT_LUN); } static void usb_uas_handle_data(USBDevice *dev, USBPacket *p) { UASDevice *uas = DO_UPCAST(UASDevice, dev, dev); - uas_ui ui; + uas_iu iu; UASStatus *st; UASRequest *req; int length; switch (p->ep->nr) { case UAS_PIPE_ID_COMMAND: - length = MIN(sizeof(ui), p->iov.size); - usb_packet_copy(p, &ui, length); - switch (ui.hdr.id) { + length = MIN(sizeof(iu), p->iov.size); + usb_packet_copy(p, &iu, length); + switch (iu.hdr.id) { case UAS_UI_COMMAND: - usb_uas_command(uas, &ui); + usb_uas_command(uas, &iu); break; case UAS_UI_TASK_MGMT: - usb_uas_task(uas, &ui); + usb_uas_task(uas, &iu); break; default: - fprintf(stderr, "%s: unknown command ui: id 0x%x\n", - __func__, ui.hdr.id); + fprintf(stderr, "%s: unknown command iu: id 0x%x\n", + __func__, iu.hdr.id); p->status = USB_RET_STALL; break; } diff --git a/hw/usb/hcd-ehci.c b/hw/usb/hcd-ehci.c index 22bdbf4a7d..355bbd6bed 100644 --- a/hw/usb/hcd-ehci.c +++ b/hw/usb/hcd-ehci.c @@ -28,6 +28,7 @@ */ #include "hw/usb/hcd-ehci.h" +#include "trace.h" /* Capability Registers Base Address - section 2.2 */ #define CAPLENGTH 0x0000 /* 1-byte, 0x0001 reserved */ @@ -826,9 +827,9 @@ static void ehci_child_detach(USBPort *port, USBDevice *child) static void ehci_wakeup(USBPort *port) { EHCIState *s = port->opaque; - uint32_t portsc = s->portsc[port->index]; + uint32_t *portsc = &s->portsc[port->index]; - if (portsc & PORTSC_POWNER) { + if (*portsc & PORTSC_POWNER) { USBPort *companion = s->companion_ports[port->index]; if (companion->ops->wakeup) { companion->ops->wakeup(companion); @@ -836,6 +837,12 @@ static void ehci_wakeup(USBPort *port) return; } + if (*portsc & PORTSC_SUSPEND) { + trace_usb_ehci_port_wakeup(port->index); + *portsc |= PORTSC_FPRES; + ehci_raise_irq(s, USBSTS_PCD); + } + qemu_bh_schedule(s->async_bh); } @@ -1067,6 +1074,14 @@ static void ehci_port_write(void *ptr, hwaddr addr, } } + if ((val & PORTSC_SUSPEND) && !(*portsc & PORTSC_SUSPEND)) { + trace_usb_ehci_port_suspend(port); + } + if (!(val & PORTSC_FPRES) && (*portsc & PORTSC_FPRES)) { + trace_usb_ehci_port_resume(port); + val &= ~PORTSC_SUSPEND; + } + *portsc &= ~PORTSC_RO_MASK; *portsc |= val; trace_usb_ehci_portsc_change(addr + s->portscbase, addr >> 2, *portsc, old); diff --git a/hw/usb/hcd-ehci.h b/hw/usb/hcd-ehci.h index 065c9fa741..1ad4b96cce 100644 --- a/hw/usb/hcd-ehci.h +++ b/hw/usb/hcd-ehci.h @@ -21,7 +21,6 @@ #include "qemu/timer.h" #include "hw/usb.h" #include "monitor/monitor.h" -#include "trace.h" #include "sysemu/dma.h" #include "sysemu/sysemu.h" #include "hw/pci/pci.h" diff --git a/hw/usb/hcd-xhci.c b/hw/usb/hcd-xhci.c index 835f65ed81..bafe08590b 100644 --- a/hw/usb/hcd-xhci.c +++ b/hw/usb/hcd-xhci.c @@ -1150,6 +1150,111 @@ static void xhci_free_streams(XHCIEPContext *epctx) epctx->nr_pstreams = 0; } +static int xhci_epmask_to_eps_with_streams(XHCIState *xhci, + unsigned int slotid, + uint32_t epmask, + XHCIEPContext **epctxs, + USBEndpoint **eps) +{ + XHCISlot *slot; + XHCIEPContext *epctx; + USBEndpoint *ep; + int i, j; + + assert(slotid >= 1 && slotid <= xhci->numslots); + + slot = &xhci->slots[slotid - 1]; + + for (i = 2, j = 0; i <= 31; i++) { + if (!(epmask & (1 << i))) { + continue; + } + + epctx = slot->eps[i - 1]; + ep = xhci_epid_to_usbep(xhci, slotid, i); + if (!epctx || !epctx->nr_pstreams || !ep) { + continue; + } + + if (epctxs) { + epctxs[j] = epctx; + } + eps[j++] = ep; + } + return j; +} + +static void xhci_free_device_streams(XHCIState *xhci, unsigned int slotid, + uint32_t epmask) +{ + USBEndpoint *eps[30]; + int nr_eps; + + nr_eps = xhci_epmask_to_eps_with_streams(xhci, slotid, epmask, NULL, eps); + if (nr_eps) { + usb_device_free_streams(eps[0]->dev, eps, nr_eps); + } +} + +static TRBCCode xhci_alloc_device_streams(XHCIState *xhci, unsigned int slotid, + uint32_t epmask) +{ + XHCIEPContext *epctxs[30]; + USBEndpoint *eps[30]; + int i, r, nr_eps, req_nr_streams, dev_max_streams; + + nr_eps = xhci_epmask_to_eps_with_streams(xhci, slotid, epmask, epctxs, + eps); + if (nr_eps == 0) { + return CC_SUCCESS; + } + + req_nr_streams = epctxs[0]->nr_pstreams; + dev_max_streams = eps[0]->max_streams; + + for (i = 1; i < nr_eps; i++) { + /* + * HdG: I don't expect these to ever trigger, but if they do we need + * to come up with another solution, ie group identical endpoints + * together and make an usb_device_alloc_streams call per group. + */ + if (epctxs[i]->nr_pstreams != req_nr_streams) { + FIXME("guest streams config not identical for all eps"); + return CC_RESOURCE_ERROR; + } + if (eps[i]->max_streams != dev_max_streams) { + FIXME("device streams config not identical for all eps"); + return CC_RESOURCE_ERROR; + } + } + + /* + * max-streams in both the device descriptor and in the controller is a + * power of 2. But stream id 0 is reserved, so if a device can do up to 4 + * streams the guest will ask for 5 rounded up to the next power of 2 which + * becomes 8. For emulated devices usb_device_alloc_streams is a nop. + * + * For redirected devices however this is an issue, as there we must ask + * the real xhci controller to alloc streams, and the host driver for the + * real xhci controller will likely disallow allocating more streams then + * the device can handle. + * + * So we limit the requested nr_streams to the maximum number the device + * can handle. + */ + if (req_nr_streams > dev_max_streams) { + req_nr_streams = dev_max_streams; + } + + r = usb_device_alloc_streams(eps[0]->dev, eps, nr_eps, req_nr_streams); + if (r != 0) { + fprintf(stderr, "xhci: alloc streams failed\n"); + return CC_RESOURCE_ERROR; + } + + return CC_SUCCESS; +} + static XHCIStreamContext *xhci_find_stream(XHCIEPContext *epctx, unsigned int streamid, uint32_t *cc_error) @@ -1495,7 +1600,8 @@ static TRBCCode xhci_reset_ep(XHCIState *xhci, unsigned int slotid, } if (!xhci->slots[slotid-1].uport || - !xhci->slots[slotid-1].uport->dev) { + !xhci->slots[slotid-1].uport->dev || + !xhci->slots[slotid-1].uport->dev->attached) { return CC_USB_TRANSACTION_ERROR; } @@ -1982,6 +2088,14 @@ static void xhci_kick_ep(XHCIState *xhci, unsigned int slotid, return; } + /* If the device has been detached, but the guest has not noticed this + yet the 2 above checks will succeed, but we must NOT continue */ + if (!xhci->slots[slotid - 1].uport || + !xhci->slots[slotid - 1].uport->dev || + !xhci->slots[slotid - 1].uport->dev->attached) { + return; + } + if (epctx->retry) { XHCITransfer *xfer = epctx->retry; @@ -2206,7 +2320,7 @@ static TRBCCode xhci_address_slot(XHCIState *xhci, unsigned int slotid, trace_usb_xhci_slot_address(slotid, uport->path); dev = uport->dev; - if (!dev) { + if (!dev || !dev->attached) { fprintf(stderr, "xhci: port %s not connected\n", uport->path); return CC_USB_TRANSACTION_ERROR; } @@ -2313,6 +2427,8 @@ static TRBCCode xhci_configure_slot(XHCIState *xhci, unsigned int slotid, return CC_CONTEXT_STATE_ERROR; } + xhci_free_device_streams(xhci, slotid, ictl_ctx[0] | ictl_ctx[1]); + for (i = 2; i <= 31; i++) { if (ictl_ctx[0] & (1<<i)) { xhci_disable_ep(xhci, slotid, i); @@ -2334,6 +2450,16 @@ static TRBCCode xhci_configure_slot(XHCIState *xhci, unsigned int slotid, } } + res = xhci_alloc_device_streams(xhci, slotid, ictl_ctx[1]); + if (res != CC_SUCCESS) { + for (i = 2; i <= 31; i++) { + if (ictl_ctx[1] & (1 << i)) { + xhci_disable_ep(xhci, slotid, i); + } + } + return res; + } + slot_ctx[3] &= ~(SLOT_STATE_MASK << SLOT_STATE_SHIFT); slot_ctx[3] |= SLOT_CONFIGURED << SLOT_STATE_SHIFT; slot_ctx[0] &= ~(SLOT_CONTEXT_ENTRIES_MASK << SLOT_CONTEXT_ENTRIES_SHIFT); @@ -3016,6 +3142,14 @@ static void xhci_oper_write(void *ptr, hwaddr reg, } else if (!(val & USBCMD_RS) && (xhci->usbcmd & USBCMD_RS)) { xhci_stop(xhci); } + if (val & USBCMD_CSS) { + /* save state */ + xhci->usbsts &= ~USBSTS_SRE; + } + if (val & USBCMD_CRS) { + /* restore state */ + xhci->usbsts |= USBSTS_SRE; + } xhci->usbcmd = val & 0xc0f; xhci_mfwrap_update(xhci); if (val & USBCMD_HCRST) { diff --git a/hw/xen/xen_pt.c b/hw/xen/xen_pt.c index ca2d460785..d58cb616b1 100644 --- a/hw/xen/xen_pt.c +++ b/hw/xen/xen_pt.c @@ -570,7 +570,8 @@ static void xen_pt_region_update(XenPCIPassthroughState *s, if (args.rc) { XEN_PT_WARN(d, "Region: %d (addr: %#"FMT_PCIBUS ", len: %#"FMT_PCIBUS") is overlapped.\n", - bar, sec->offset_within_address_space, sec->size); + bar, sec->offset_within_address_space, + int128_get64(sec->size)); } if (d->io_regions[bar].type & PCI_BASE_ADDRESS_SPACE_IO) { diff --git a/hw/xen/xen_pvdevice.c b/hw/xen/xen_pvdevice.c index 1132c8934f..c2189473ba 100644 --- a/hw/xen/xen_pvdevice.c +++ b/hw/xen/xen_pvdevice.c @@ -74,6 +74,10 @@ static int xen_pv_init(PCIDevice *pci_dev) XenPVDevice *d = XEN_PV_DEVICE(pci_dev); uint8_t *pci_conf; + /* device-id property must always be supplied */ + if (d->device_id == 0xffff) + return -1; + pci_conf = pci_dev->config; pci_set_word(pci_conf + PCI_VENDOR_ID, d->vendor_id); @@ -99,7 +103,7 @@ static int xen_pv_init(PCIDevice *pci_dev) static Property xen_pv_props[] = { DEFINE_PROP_UINT16("vendor-id", XenPVDevice, vendor_id, PCI_VENDOR_ID_XEN), - DEFINE_PROP_UINT16("device-id", XenPVDevice, device_id, PCI_DEVICE_ID_XEN_PVDEVICE), + DEFINE_PROP_UINT16("device-id", XenPVDevice, device_id, 0xffff), DEFINE_PROP_UINT8("revision", XenPVDevice, revision, 0x01), DEFINE_PROP_UINT32("size", XenPVDevice, size, 0x400000), DEFINE_PROP_END_OF_LIST() diff --git a/include/elf.h b/include/elf.h index b818091c7b..667af6fc63 100644 --- a/include/elf.h +++ b/include/elf.h @@ -411,6 +411,65 @@ typedef struct { #define R_SPARC_5 44 #define R_SPARC_6 45 +/* Bits present in AT_HWCAP for ARM. */ + +#define HWCAP_ARM_SWP (1 << 0) +#define HWCAP_ARM_HALF (1 << 1) +#define HWCAP_ARM_THUMB (1 << 2) +#define HWCAP_ARM_26BIT (1 << 3) +#define HWCAP_ARM_FAST_MULT (1 << 4) +#define HWCAP_ARM_FPA (1 << 5) +#define HWCAP_ARM_VFP (1 << 6) +#define HWCAP_ARM_EDSP (1 << 7) +#define HWCAP_ARM_JAVA (1 << 8) +#define HWCAP_ARM_IWMMXT (1 << 9) +#define HWCAP_ARM_CRUNCH (1 << 10) +#define HWCAP_ARM_THUMBEE (1 << 11) +#define HWCAP_ARM_NEON (1 << 12) +#define HWCAP_ARM_VFPv3 (1 << 13) +#define HWCAP_ARM_VFPv3D16 (1 << 14) /* also set for VFPv4-D16 */ +#define HWCAP_ARM_TLS (1 << 15) +#define HWCAP_ARM_VFPv4 (1 << 16) +#define HWCAP_ARM_IDIVA (1 << 17) +#define HWCAP_ARM_IDIVT (1 << 18) +#define HWCAP_IDIV (HWCAP_IDIVA | HWCAP_IDIVT) +#define HWCAP_VFPD32 (1 << 19) /* set if VFP has 32 regs */ +#define HWCAP_LPAE (1 << 20) + +/* Bits present in AT_HWCAP for PowerPC. */ + +#define PPC_FEATURE_32 0x80000000 +#define PPC_FEATURE_64 0x40000000 +#define PPC_FEATURE_601_INSTR 0x20000000 +#define PPC_FEATURE_HAS_ALTIVEC 0x10000000 +#define PPC_FEATURE_HAS_FPU 0x08000000 +#define PPC_FEATURE_HAS_MMU 0x04000000 +#define PPC_FEATURE_HAS_4xxMAC 0x02000000 +#define PPC_FEATURE_UNIFIED_CACHE 0x01000000 +#define PPC_FEATURE_HAS_SPE 0x00800000 +#define PPC_FEATURE_HAS_EFP_SINGLE 0x00400000 +#define PPC_FEATURE_HAS_EFP_DOUBLE 0x00200000 +#define PPC_FEATURE_NO_TB 0x00100000 +#define PPC_FEATURE_POWER4 0x00080000 +#define PPC_FEATURE_POWER5 0x00040000 +#define PPC_FEATURE_POWER5_PLUS 0x00020000 +#define PPC_FEATURE_CELL 0x00010000 +#define PPC_FEATURE_BOOKE 0x00008000 +#define PPC_FEATURE_SMT 0x00004000 +#define PPC_FEATURE_ICACHE_SNOOP 0x00002000 +#define PPC_FEATURE_ARCH_2_05 0x00001000 +#define PPC_FEATURE_PA6T 0x00000800 +#define PPC_FEATURE_HAS_DFP 0x00000400 +#define PPC_FEATURE_POWER6_EXT 0x00000200 +#define PPC_FEATURE_ARCH_2_06 0x00000100 +#define PPC_FEATURE_HAS_VSX 0x00000080 + +#define PPC_FEATURE_PSERIES_PERFMON_COMPAT \ + 0x00000040 + +#define PPC_FEATURE_TRUE_LE 0x00000002 +#define PPC_FEATURE_PPC_LE 0x00000001 + /* Bits present in AT_HWCAP, primarily for Sparc32. */ #define HWCAP_SPARC_FLUSH 1 /* CPU supports flush instruction. */ @@ -420,6 +479,20 @@ typedef struct { #define HWCAP_SPARC_V9 16 #define HWCAP_SPARC_ULTRA3 32 +/* Bits present in AT_HWCAP for s390. */ + +#define HWCAP_S390_ESAN3 1 +#define HWCAP_S390_ZARCH 2 +#define HWCAP_S390_STFLE 4 +#define HWCAP_S390_MSA 8 +#define HWCAP_S390_LDISP 16 +#define HWCAP_S390_EIMM 32 +#define HWCAP_S390_DFP 64 +#define HWCAP_S390_HPAGE 128 +#define HWCAP_S390_ETF3EH 256 +#define HWCAP_S390_HIGH_GPRS 512 +#define HWCAP_S390_TE 1024 + /* * 68k ELF relocation types */ diff --git a/include/fpu/softfloat.h b/include/fpu/softfloat.h index f3927e2419..2365274daa 100644 --- a/include/fpu/softfloat.h +++ b/include/fpu/softfloat.h @@ -302,6 +302,8 @@ int float32_compare( float32, float32 STATUS_PARAM ); int float32_compare_quiet( float32, float32 STATUS_PARAM ); float32 float32_min(float32, float32 STATUS_PARAM); float32 float32_max(float32, float32 STATUS_PARAM); +float32 float32_minnum(float32, float32 STATUS_PARAM); +float32 float32_maxnum(float32, float32 STATUS_PARAM); int float32_is_quiet_nan( float32 ); int float32_is_signaling_nan( float32 ); float32 float32_maybe_silence_nan( float32 ); @@ -408,6 +410,8 @@ int float64_compare( float64, float64 STATUS_PARAM ); int float64_compare_quiet( float64, float64 STATUS_PARAM ); float64 float64_min(float64, float64 STATUS_PARAM); float64 float64_max(float64, float64 STATUS_PARAM); +float64 float64_minnum(float64, float64 STATUS_PARAM); +float64 float64_maxnum(float64, float64 STATUS_PARAM); int float64_is_quiet_nan( float64 a ); int float64_is_signaling_nan( float64 ); float64 float64_maybe_silence_nan( float64 ); diff --git a/include/hw/arm/arm.h b/include/hw/arm/arm.h index ecbbba871e..cbbf4ca4cb 100644 --- a/include/hw/arm/arm.h +++ b/include/hw/arm/arm.h @@ -50,6 +50,13 @@ struct arm_boot_info { const struct arm_boot_info *info); void (*secondary_cpu_reset_hook)(ARMCPU *cpu, const struct arm_boot_info *info); + /* if a board is able to create a dtb without a dtb file then it + * sets get_dtb. This will only be used if no dtb file is provided + * by the user. On success, sets *size to the length of the created + * dtb, and returns a pointer to it. (The caller must free this memory + * with g_free() when it has finished with it.) On failure, returns NULL. + */ + void *(*get_dtb)(const struct arm_boot_info *info, int *size); /* if a board needs to be able to modify a device tree provided by * the user it should implement this hook. */ diff --git a/include/hw/char/serial.h b/include/hw/char/serial.h index 85f58acd51..f431764bf5 100644 --- a/include/hw/char/serial.h +++ b/include/hw/char/serial.h @@ -65,13 +65,13 @@ struct SerialState { /* Interrupt trigger level for recv_fifo */ uint8_t recv_fifo_itl; - struct QEMUTimer *fifo_timeout_timer; + QEMUTimer *fifo_timeout_timer; int timeout_ipending; /* timeout interrupt pending state */ uint64_t char_transmit_time; /* time to transmit a char in ticks */ int poll_msl; - struct QEMUTimer *modem_status_poll; + QEMUTimer *modem_status_poll; MemoryRegion io; }; diff --git a/include/hw/cpu/a9mpcore.h b/include/hw/cpu/a9mpcore.h index 010489b98e..5d67ca22c4 100644 --- a/include/hw/cpu/a9mpcore.h +++ b/include/hw/cpu/a9mpcore.h @@ -14,6 +14,7 @@ #include "hw/intc/arm_gic.h" #include "hw/misc/a9scu.h" #include "hw/timer/arm_mptimer.h" +#include "hw/timer/a9gtimer.h" #define TYPE_A9MPCORE_PRIV "a9mpcore_priv" #define A9MPCORE_PRIV(obj) \ @@ -28,8 +29,9 @@ typedef struct A9MPPrivState { MemoryRegion container; uint32_t num_irq; - GICState gic; A9SCUState scu; + GICState gic; + A9GTimerState gtimer; ARMMPTimerState mptimer; ARMMPTimerState wdt; } A9MPPrivState; diff --git a/include/hw/pci/pci_ids.h b/include/hw/pci/pci_ids.h index 4c0002beca..e597070ab8 100644 --- a/include/hw/pci/pci_ids.h +++ b/include/hw/pci/pci_ids.h @@ -146,7 +146,6 @@ #define PCI_VENDOR_ID_XEN 0x5853 #define PCI_DEVICE_ID_XEN_PLATFORM 0x0001 -#define PCI_DEVICE_ID_XEN_PVDEVICE 0x0002 #define PCI_VENDOR_ID_NEC 0x1033 #define PCI_DEVICE_ID_NEC_UPD720200 0x0194 diff --git a/include/hw/ppc/ppc.h b/include/hw/ppc/ppc.h index 132ab97b58..835418aeb0 100644 --- a/include/hw/ppc/ppc.h +++ b/include/hw/ppc/ppc.h @@ -24,10 +24,10 @@ struct ppc_tb_t { /* Decrementer management */ uint64_t decr_next; /* Tick for next decr interrupt */ uint32_t decr_freq; /* decrementer frequency */ - struct QEMUTimer *decr_timer; + QEMUTimer *decr_timer; /* Hypervisor decrementer management */ uint64_t hdecr_next; /* Tick for next hdecr interrupt */ - struct QEMUTimer *hdecr_timer; + QEMUTimer *hdecr_timer; uint64_t purr_load; uint64_t purr_start; void *opaque; diff --git a/include/hw/scsi/scsi.h b/include/hw/scsi/scsi.h index 76f6ac24a7..bf6da3d632 100644 --- a/include/hw/scsi/scsi.h +++ b/include/hw/scsi/scsi.h @@ -199,12 +199,16 @@ extern const struct SCSISense sense_code_SAVING_PARAMS_NOT_SUPPORTED; extern const struct SCSISense sense_code_INCOMPATIBLE_FORMAT; /* Illegal request, medium removal prevented */ extern const struct SCSISense sense_code_ILLEGAL_REQ_REMOVAL_PREVENTED; +/* Illegal request, Invalid Transfer Tag */ +extern const struct SCSISense sense_code_INVALID_TAG; /* Command aborted, I/O process terminated */ extern const struct SCSISense sense_code_IO_ERROR; /* Command aborted, I_T Nexus loss occurred */ extern const struct SCSISense sense_code_I_T_NEXUS_LOSS; /* Command aborted, Logical Unit failure */ extern const struct SCSISense sense_code_LUN_FAILURE; +/* Command aborted, Overlapped Commands Attempted */ +extern const struct SCSISense sense_code_OVERLAPPED_COMMANDS; /* LUN not ready, Capacity data has changed */ extern const struct SCSISense sense_code_CAPACITY_CHANGED; /* LUN not ready, Medium not present */ diff --git a/include/hw/timer/a9gtimer.h b/include/hw/timer/a9gtimer.h new file mode 100644 index 0000000000..b88c02a6ef --- /dev/null +++ b/include/hw/timer/a9gtimer.h @@ -0,0 +1,97 @@ +/* + * Global peripheral timer block for ARM A9MP + * + * (C) 2013 Xilinx Inc. + * + * Written by François LEGAL + * Written by Peter Crosthwaite <peter.crosthwaite@xilinx.com> + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, see <http://www.gnu.org/licenses/>. + */ + +#ifndef HW_TIMER_A9_GTIMER_H_H +#define HW_TIMER_A9_GTIMER_H_H + +#include "hw/sysbus.h" + +#define A9_GTIMER_MAX_CPUS 4 + +#define TYPE_A9_GTIMER "arm.cortex-a9-global-timer" +#define A9_GTIMER(obj) OBJECT_CHECK(A9GTimerState, (obj), TYPE_A9_GTIMER) + +#define R_COUNTER_LO 0x00 +#define R_COUNTER_HI 0x04 + +#define R_CONTROL 0x08 +#define R_CONTROL_TIMER_ENABLE (1 << 0) +#define R_CONTROL_COMP_ENABLE (1 << 1) +#define R_CONTROL_IRQ_ENABLE (1 << 2) +#define R_CONTROL_AUTO_INCREMENT (1 << 2) +#define R_CONTROL_PRESCALER_SHIFT 8 +#define R_CONTROL_PRESCALER_LEN 8 +#define R_CONTROL_PRESCALER_MASK (((1 << R_CONTROL_PRESCALER_LEN) - 1) << \ + R_CONTROL_PRESCALER_SHIFT) + +#define R_CONTROL_BANKED (R_CONTROL_COMP_ENABLE | \ + R_CONTROL_IRQ_ENABLE | \ + R_CONTROL_AUTO_INCREMENT) +#define R_CONTROL_NEEDS_SYNC (R_CONTROL_TIMER_ENABLE | \ + R_CONTROL_PRESCALER_MASK) + +#define R_INTERRUPT_STATUS 0x0C +#define R_COMPARATOR_LO 0x10 +#define R_COMPARATOR_HI 0x14 +#define R_AUTO_INCREMENT 0x18 + +typedef struct A9GTimerPerCPU A9GTimerPerCPU; +typedef struct A9GTimerState A9GTimerState; + +struct A9GTimerPerCPU { + A9GTimerState *parent; + + uint32_t control; /* only per cpu banked bits valid */ + uint64_t compare; + uint32_t status; + uint32_t inc; + + MemoryRegion iomem; + qemu_irq irq; /* PPI interrupts */ +}; + +struct A9GTimerState { + /*< private >*/ + SysBusDevice parent_obj; + /*< public >*/ + + MemoryRegion iomem; + /* static props */ + uint32_t num_cpu; + + QEMUTimer *timer; + + uint64_t counter; /* current timer value */ + + uint64_t ref_counter; + uint64_t cpu_ref_time; /* the cpu time as of last update of ref_counter */ + uint32_t control; /* only non per cpu banked bits valid */ + + A9GTimerPerCPU per_cpu[A9_GTIMER_MAX_CPUS]; +}; + +typedef struct A9GTimerUpdate { + uint64_t now; + uint64_t new; +} A9GTimerUpdate; + +#endif /* #ifdef HW_TIMER_A9_GTIMER_H_H */ diff --git a/include/hw/usb.h b/include/hw/usb.h index a7680d4e8a..2a3ea0c92e 100644 --- a/include/hw/usb.h +++ b/include/hw/usb.h @@ -102,17 +102,26 @@ #define DeviceRequest ((USB_DIR_IN|USB_TYPE_STANDARD|USB_RECIP_DEVICE)<<8) #define DeviceOutRequest ((USB_DIR_OUT|USB_TYPE_STANDARD|USB_RECIP_DEVICE)<<8) -#define InterfaceRequest \ +#define VendorDeviceRequest ((USB_DIR_IN|USB_TYPE_VENDOR|USB_RECIP_DEVICE)<<8) +#define VendorDeviceOutRequest \ + ((USB_DIR_OUT|USB_TYPE_VENDOR|USB_RECIP_DEVICE)<<8) + +#define InterfaceRequest \ ((USB_DIR_IN|USB_TYPE_STANDARD|USB_RECIP_INTERFACE)<<8) #define InterfaceOutRequest \ ((USB_DIR_OUT|USB_TYPE_STANDARD|USB_RECIP_INTERFACE)<<8) -#define EndpointRequest ((USB_DIR_IN|USB_TYPE_STANDARD|USB_RECIP_ENDPOINT)<<8) -#define EndpointOutRequest \ - ((USB_DIR_OUT|USB_TYPE_STANDARD|USB_RECIP_ENDPOINT)<<8) #define ClassInterfaceRequest \ ((USB_DIR_IN|USB_TYPE_CLASS|USB_RECIP_INTERFACE)<<8) #define ClassInterfaceOutRequest \ ((USB_DIR_OUT|USB_TYPE_CLASS|USB_RECIP_INTERFACE)<<8) +#define VendorInterfaceRequest \ + ((USB_DIR_IN|USB_TYPE_VENDOR|USB_RECIP_INTERFACE)<<8) +#define VendorInterfaceOutRequest \ + ((USB_DIR_OUT|USB_TYPE_VENDOR|USB_RECIP_INTERFACE)<<8) + +#define EndpointRequest ((USB_DIR_IN|USB_TYPE_STANDARD|USB_RECIP_ENDPOINT)<<8) +#define EndpointOutRequest \ + ((USB_DIR_OUT|USB_TYPE_STANDARD|USB_RECIP_ENDPOINT)<<8) #define USB_REQ_GET_STATUS 0x00 #define USB_REQ_CLEAR_FEATURE 0x01 @@ -189,6 +198,7 @@ struct USBEndpoint { uint8_t type; uint8_t ifnum; int max_packet_size; + int max_streams; bool pipeline; bool halted; USBDevice *dev; @@ -314,6 +324,14 @@ typedef struct USBDeviceClass { */ void (*ep_stopped)(USBDevice *dev, USBEndpoint *ep); + /* + * Called by the hcd to alloc / free streams on a bulk endpoint. + * Optional may be NULL. + */ + int (*alloc_streams)(USBDevice *dev, USBEndpoint **eps, int nr_eps, + int streams); + void (*free_streams)(USBDevice *dev, USBEndpoint **eps, int nr_eps); + const char *product_desc; const USBDesc *usb_desc; } USBDeviceClass; @@ -421,6 +439,8 @@ void usb_ep_set_ifnum(USBDevice *dev, int pid, int ep, uint8_t ifnum); void usb_ep_set_max_packet_size(USBDevice *dev, int pid, int ep, uint16_t raw); int usb_ep_get_max_packet_size(USBDevice *dev, int pid, int ep); +void usb_ep_set_max_streams(USBDevice *dev, int pid, int ep, uint8_t raw); +int usb_ep_get_max_streams(USBDevice *dev, int pid, int ep); void usb_ep_set_pipeline(USBDevice *dev, int pid, int ep, bool enabled); void usb_ep_set_halted(USBDevice *dev, int pid, int ep, bool halted); USBPacket *usb_ep_find_packet_by_id(USBDevice *dev, int pid, int ep, @@ -550,6 +570,10 @@ void usb_device_flush_ep_queue(USBDevice *dev, USBEndpoint *ep); void usb_device_ep_stopped(USBDevice *dev, USBEndpoint *ep); +int usb_device_alloc_streams(USBDevice *dev, USBEndpoint **eps, int nr_eps, + int streams); +void usb_device_free_streams(USBDevice *dev, USBEndpoint **eps, int nr_eps); + const char *usb_device_get_product_desc(USBDevice *dev); const USBDesc *usb_device_get_usb_desc(USBDevice *dev); diff --git a/include/qemu/cache-utils.h b/include/qemu/cache-utils.h index 2c57f78fc1..211245bea0 100644 --- a/include/qemu/cache-utils.h +++ b/include/qemu/cache-utils.h @@ -12,7 +12,7 @@ struct qemu_cache_conf { extern struct qemu_cache_conf qemu_cache_conf; -void qemu_cache_utils_init(char **envp); +void qemu_cache_utils_init(void); /* mildly adjusted code from tcg-dyngen.c */ static inline void flush_icache_range(uintptr_t start, uintptr_t stop) @@ -38,7 +38,7 @@ static inline void flush_icache_range(uintptr_t start, uintptr_t stop) } #else -#define qemu_cache_utils_init(envp) do { (void) (envp); } while (0) +#define qemu_cache_utils_init() do { } while (0) #endif #endif /* QEMU_CACHE_UTILS_H */ diff --git a/include/qemu/osdep.h b/include/qemu/osdep.h index 26136f16ec..b3e2b6d8ea 100644 --- a/include/qemu/osdep.h +++ b/include/qemu/osdep.h @@ -215,4 +215,29 @@ bool fips_get_state(void); */ char *qemu_get_local_state_pathname(const char *relative_pathname); +/** + * qemu_getauxval: + * @type: the auxiliary vector key to lookup + * + * Search the auxiliary vector for @type, returning the value + * or 0 if @type is not present. + */ +#if defined(CONFIG_GETAUXVAL) || defined(__linux__) +unsigned long qemu_getauxval(unsigned long type); +#else +static inline unsigned long qemu_getauxval(unsigned long type) { return 0; } +#endif + +/** + * qemu_init_auxval: + * @envp: the third argument to main + * + * If supported and required, locate the auxiliary vector at program startup. + */ +#if defined(CONFIG_GETAUXVAL) || !defined(__linux__) +static inline void qemu_init_auxval(char **envp) { } +#else +void qemu_init_auxval(char **envp); +#endif + #endif diff --git a/include/ui/console.h b/include/ui/console.h index 98edf413a6..4156a876e1 100644 --- a/include/ui/console.h +++ b/include/ui/console.h @@ -6,7 +6,6 @@ #include "qapi/qmp/qdict.h" #include "qemu/notify.h" #include "monitor/monitor.h" -#include "trace.h" #include "qapi-types.h" #include "qapi/error.h" @@ -72,7 +72,8 @@ typedef struct kvm_dirty_log KVMDirtyLog; struct KVMState { - KVMSlot slots[32]; + KVMSlot *slots; + int nr_slots; int fd; int vmfd; int coalesced_mmio; @@ -125,7 +126,7 @@ static KVMSlot *kvm_alloc_slot(KVMState *s) { int i; - for (i = 0; i < ARRAY_SIZE(s->slots); i++) { + for (i = 0; i < s->nr_slots; i++) { if (s->slots[i].memory_size == 0) { return &s->slots[i]; } @@ -141,7 +142,7 @@ static KVMSlot *kvm_lookup_matching_slot(KVMState *s, { int i; - for (i = 0; i < ARRAY_SIZE(s->slots); i++) { + for (i = 0; i < s->nr_slots; i++) { KVMSlot *mem = &s->slots[i]; if (start_addr == mem->start_addr && @@ -163,7 +164,7 @@ static KVMSlot *kvm_lookup_overlapping_slot(KVMState *s, KVMSlot *found = NULL; int i; - for (i = 0; i < ARRAY_SIZE(s->slots); i++) { + for (i = 0; i < s->nr_slots; i++) { KVMSlot *mem = &s->slots[i]; if (mem->memory_size == 0 || @@ -185,7 +186,7 @@ int kvm_physical_memory_addr_from_host(KVMState *s, void *ram, { int i; - for (i = 0; i < ARRAY_SIZE(s->slots); i++) { + for (i = 0; i < s->nr_slots; i++) { KVMSlot *mem = &s->slots[i]; if (ram >= mem->ram && ram < mem->ram + mem->memory_size) { @@ -357,7 +358,7 @@ static int kvm_set_migration_log(int enable) s->migration_log = enable; - for (i = 0; i < ARRAY_SIZE(s->slots); i++) { + for (i = 0; i < s->nr_slots; i++) { mem = &s->slots[i]; if (!mem->memory_size) { @@ -1383,9 +1384,6 @@ int kvm_init(void) #ifdef KVM_CAP_SET_GUEST_DEBUG QTAILQ_INIT(&s->kvm_sw_breakpoints); #endif - for (i = 0; i < ARRAY_SIZE(s->slots); i++) { - s->slots[i].slot = i; - } s->vmfd = -1; s->fd = qemu_open("/dev/kvm", O_RDWR); if (s->fd == -1) { @@ -1409,6 +1407,19 @@ int kvm_init(void) goto err; } + s->nr_slots = kvm_check_extension(s, KVM_CAP_NR_MEMSLOTS); + + /* If unspecified, use the default value */ + if (!s->nr_slots) { + s->nr_slots = 32; + } + + s->slots = g_malloc0(s->nr_slots * sizeof(KVMSlot)); + + for (i = 0; i < s->nr_slots; i++) { + s->slots[i].slot = i; + } + /* check the vcpu limits */ soft_vcpus_limit = kvm_recommended_vcpus(s); hard_vcpus_limit = kvm_max_vcpus(s); @@ -1527,6 +1538,7 @@ err: if (s->fd != -1) { close(s->fd); } + g_free(s->slots); g_free(s); return ret; diff --git a/libcacard/cac.c b/libcacard/cac.c index 7a06b5a31b..74ef3e3cec 100644 --- a/libcacard/cac.c +++ b/libcacard/cac.c @@ -189,7 +189,6 @@ cac_applet_pki_process_apdu(VCard *card, VCardAPDU *apdu, pki_applet->sign_buffer = sign_buffer; pki_applet->sign_buffer_len = size; *response = vcard_make_response(VCARD7816_STATUS_SUCCESS); - ret = VCARD_DONE; break; case 0x00: /* we now have the whole buffer, do the operation, result will be diff --git a/libcacard/vcard_emul_nss.c b/libcacard/vcard_emul_nss.c index fb429b1f82..ee2dfaee82 100644 --- a/libcacard/vcard_emul_nss.c +++ b/libcacard/vcard_emul_nss.c @@ -934,7 +934,6 @@ vcard_emul_init(const VCardEmulOptions *options) vreader = vreader_new(options->vreader[i].vname, vreader_emul, vreader_emul_delete); vreader_add_reader(vreader); - cert_count = options->vreader[i].cert_count; vcard_emul_alloc_arrays(&certs, &cert_len, &keys, options->vreader[i].cert_count); diff --git a/libcacard/vscclient.c b/libcacard/vscclient.c index a3cb7762b5..f1d46d397a 100644 --- a/libcacard/vscclient.c +++ b/libcacard/vscclient.c @@ -407,7 +407,7 @@ do_socket_read(GIOChannel *source, } break; default: - g_warn_if_reached(); + g_assert_not_reached(); return FALSE; } @@ -760,7 +760,7 @@ main( g_io_channel_unref(channel_stdin); g_io_channel_unref(channel_socket); - g_byte_array_unref(socket_to_send); + g_byte_array_free(socket_to_send, TRUE); closesocket(sock); return 0; diff --git a/linux-headers/asm-arm/kvm.h b/linux-headers/asm-arm/kvm.h index c1ee007523..c498b60c05 100644 --- a/linux-headers/asm-arm/kvm.h +++ b/linux-headers/asm-arm/kvm.h @@ -63,7 +63,8 @@ struct kvm_regs { /* Supported Processor Types */ #define KVM_ARM_TARGET_CORTEX_A15 0 -#define KVM_ARM_NUM_TARGETS 1 +#define KVM_ARM_TARGET_CORTEX_A7 1 +#define KVM_ARM_NUM_TARGETS 2 /* KVM_ARM_SET_DEVICE_ADDR ioctl id encoding */ #define KVM_ARM_DEVICE_TYPE_SHIFT 0 diff --git a/linux-headers/asm-powerpc/epapr_hcalls.h b/linux-headers/asm-powerpc/epapr_hcalls.h index 33b3f89f55..06f724786a 100644 --- a/linux-headers/asm-powerpc/epapr_hcalls.h +++ b/linux-headers/asm-powerpc/epapr_hcalls.h @@ -78,7 +78,7 @@ #define EV_SUCCESS 0 #define EV_EPERM 1 /* Operation not permitted */ #define EV_ENOENT 2 /* Entry Not Found */ -#define EV_EIO 3 /* I/O error occurred */ +#define EV_EIO 3 /* I/O error occured */ #define EV_EAGAIN 4 /* The operation had insufficient * resources to complete and should be * retried @@ -89,7 +89,7 @@ #define EV_ENODEV 7 /* No such device */ #define EV_EINVAL 8 /* An argument supplied to the hcall was out of range or invalid */ -#define EV_INTERNAL 9 /* An internal error occurred */ +#define EV_INTERNAL 9 /* An internal error occured */ #define EV_CONFIG 10 /* A configuration error was detected */ #define EV_INVALID_STATE 11 /* The object is in an invalid state */ #define EV_UNIMPLEMENTED 12 /* Unimplemented hypercall */ diff --git a/linux-headers/asm-powerpc/kvm.h b/linux-headers/asm-powerpc/kvm.h index 0fb1a6e9ff..6836ec79a8 100644 --- a/linux-headers/asm-powerpc/kvm.h +++ b/linux-headers/asm-powerpc/kvm.h @@ -27,6 +27,7 @@ #define __KVM_HAVE_PPC_SMT #define __KVM_HAVE_IRQCHIP #define __KVM_HAVE_IRQ_LINE +#define __KVM_HAVE_GUEST_DEBUG struct kvm_regs { __u64 pc; @@ -269,7 +270,24 @@ struct kvm_fpu { __u64 fpr[32]; }; +/* + * Defines for h/w breakpoint, watchpoint (read, write or both) and + * software breakpoint. + * These are used as "type" in KVM_SET_GUEST_DEBUG ioctl and "status" + * for KVM_DEBUG_EXIT. + */ +#define KVMPPC_DEBUG_NONE 0x0 +#define KVMPPC_DEBUG_BREAKPOINT (1UL << 1) +#define KVMPPC_DEBUG_WATCH_WRITE (1UL << 2) +#define KVMPPC_DEBUG_WATCH_READ (1UL << 3) struct kvm_debug_exit_arch { + __u64 address; + /* + * exiting to userspace because of h/w breakpoint, watchpoint + * (read, write or both) and software breakpoint. + */ + __u32 status; + __u32 reserved; }; /* for KVM_SET_GUEST_DEBUG */ @@ -281,10 +299,6 @@ struct kvm_guest_debug_arch { * Type denotes h/w breakpoint, read watchpoint, write * watchpoint or watchpoint (both read and write). */ -#define KVMPPC_DEBUG_NONE 0x0 -#define KVMPPC_DEBUG_BREAKPOINT (1UL << 1) -#define KVMPPC_DEBUG_WATCH_WRITE (1UL << 2) -#define KVMPPC_DEBUG_WATCH_READ (1UL << 3) __u32 type; __u32 reserved; } bp[16]; @@ -429,6 +443,11 @@ struct kvm_get_htab_header { #define KVM_REG_PPC_MMCR0 (KVM_REG_PPC | KVM_REG_SIZE_U64 | 0x10) #define KVM_REG_PPC_MMCR1 (KVM_REG_PPC | KVM_REG_SIZE_U64 | 0x11) #define KVM_REG_PPC_MMCRA (KVM_REG_PPC | KVM_REG_SIZE_U64 | 0x12) +#define KVM_REG_PPC_MMCR2 (KVM_REG_PPC | KVM_REG_SIZE_U64 | 0x13) +#define KVM_REG_PPC_MMCRS (KVM_REG_PPC | KVM_REG_SIZE_U64 | 0x14) +#define KVM_REG_PPC_SIAR (KVM_REG_PPC | KVM_REG_SIZE_U64 | 0x15) +#define KVM_REG_PPC_SDAR (KVM_REG_PPC | KVM_REG_SIZE_U64 | 0x16) +#define KVM_REG_PPC_SIER (KVM_REG_PPC | KVM_REG_SIZE_U64 | 0x17) #define KVM_REG_PPC_PMC1 (KVM_REG_PPC | KVM_REG_SIZE_U32 | 0x18) #define KVM_REG_PPC_PMC2 (KVM_REG_PPC | KVM_REG_SIZE_U32 | 0x19) @@ -499,6 +518,65 @@ struct kvm_get_htab_header { #define KVM_REG_PPC_TLB3PS (KVM_REG_PPC | KVM_REG_SIZE_U32 | 0x9a) #define KVM_REG_PPC_EPTCFG (KVM_REG_PPC | KVM_REG_SIZE_U32 | 0x9b) +/* Timebase offset */ +#define KVM_REG_PPC_TB_OFFSET (KVM_REG_PPC | KVM_REG_SIZE_U64 | 0x9c) + +/* POWER8 registers */ +#define KVM_REG_PPC_SPMC1 (KVM_REG_PPC | KVM_REG_SIZE_U32 | 0x9d) +#define KVM_REG_PPC_SPMC2 (KVM_REG_PPC | KVM_REG_SIZE_U32 | 0x9e) +#define KVM_REG_PPC_IAMR (KVM_REG_PPC | KVM_REG_SIZE_U64 | 0x9f) +#define KVM_REG_PPC_TFHAR (KVM_REG_PPC | KVM_REG_SIZE_U64 | 0xa0) +#define KVM_REG_PPC_TFIAR (KVM_REG_PPC | KVM_REG_SIZE_U64 | 0xa1) +#define KVM_REG_PPC_TEXASR (KVM_REG_PPC | KVM_REG_SIZE_U64 | 0xa2) +#define KVM_REG_PPC_FSCR (KVM_REG_PPC | KVM_REG_SIZE_U64 | 0xa3) +#define KVM_REG_PPC_PSPB (KVM_REG_PPC | KVM_REG_SIZE_U32 | 0xa4) +#define KVM_REG_PPC_EBBHR (KVM_REG_PPC | KVM_REG_SIZE_U64 | 0xa5) +#define KVM_REG_PPC_EBBRR (KVM_REG_PPC | KVM_REG_SIZE_U64 | 0xa6) +#define KVM_REG_PPC_BESCR (KVM_REG_PPC | KVM_REG_SIZE_U64 | 0xa7) +#define KVM_REG_PPC_TAR (KVM_REG_PPC | KVM_REG_SIZE_U64 | 0xa8) +#define KVM_REG_PPC_DPDES (KVM_REG_PPC | KVM_REG_SIZE_U64 | 0xa9) +#define KVM_REG_PPC_DAWR (KVM_REG_PPC | KVM_REG_SIZE_U64 | 0xaa) +#define KVM_REG_PPC_DAWRX (KVM_REG_PPC | KVM_REG_SIZE_U64 | 0xab) +#define KVM_REG_PPC_CIABR (KVM_REG_PPC | KVM_REG_SIZE_U64 | 0xac) +#define KVM_REG_PPC_IC (KVM_REG_PPC | KVM_REG_SIZE_U64 | 0xad) +#define KVM_REG_PPC_VTB (KVM_REG_PPC | KVM_REG_SIZE_U64 | 0xae) +#define KVM_REG_PPC_CSIGR (KVM_REG_PPC | KVM_REG_SIZE_U64 | 0xaf) +#define KVM_REG_PPC_TACR (KVM_REG_PPC | KVM_REG_SIZE_U64 | 0xb0) +#define KVM_REG_PPC_TCSCR (KVM_REG_PPC | KVM_REG_SIZE_U64 | 0xb1) +#define KVM_REG_PPC_PID (KVM_REG_PPC | KVM_REG_SIZE_U64 | 0xb2) +#define KVM_REG_PPC_ACOP (KVM_REG_PPC | KVM_REG_SIZE_U64 | 0xb3) + +#define KVM_REG_PPC_VRSAVE (KVM_REG_PPC | KVM_REG_SIZE_U32 | 0xb4) +#define KVM_REG_PPC_LPCR (KVM_REG_PPC | KVM_REG_SIZE_U32 | 0xb5) +#define KVM_REG_PPC_PPR (KVM_REG_PPC | KVM_REG_SIZE_U64 | 0xb6) + +/* Architecture compatibility level */ +#define KVM_REG_PPC_ARCH_COMPAT (KVM_REG_PPC | KVM_REG_SIZE_U32 | 0xb7) + +/* Transactional Memory checkpointed state: + * This is all GPRs, all VSX regs and a subset of SPRs + */ +#define KVM_REG_PPC_TM (KVM_REG_PPC | 0x80000000) +/* TM GPRs */ +#define KVM_REG_PPC_TM_GPR0 (KVM_REG_PPC_TM | KVM_REG_SIZE_U64 | 0) +#define KVM_REG_PPC_TM_GPR(n) (KVM_REG_PPC_TM_GPR0 + (n)) +#define KVM_REG_PPC_TM_GPR31 (KVM_REG_PPC_TM | KVM_REG_SIZE_U64 | 0x1f) +/* TM VSX */ +#define KVM_REG_PPC_TM_VSR0 (KVM_REG_PPC_TM | KVM_REG_SIZE_U128 | 0x20) +#define KVM_REG_PPC_TM_VSR(n) (KVM_REG_PPC_TM_VSR0 + (n)) +#define KVM_REG_PPC_TM_VSR63 (KVM_REG_PPC_TM | KVM_REG_SIZE_U128 | 0x5f) +/* TM SPRS */ +#define KVM_REG_PPC_TM_CR (KVM_REG_PPC_TM | KVM_REG_SIZE_U64 | 0x60) +#define KVM_REG_PPC_TM_LR (KVM_REG_PPC_TM | KVM_REG_SIZE_U64 | 0x61) +#define KVM_REG_PPC_TM_CTR (KVM_REG_PPC_TM | KVM_REG_SIZE_U64 | 0x62) +#define KVM_REG_PPC_TM_FPSCR (KVM_REG_PPC_TM | KVM_REG_SIZE_U64 | 0x63) +#define KVM_REG_PPC_TM_AMR (KVM_REG_PPC_TM | KVM_REG_SIZE_U64 | 0x64) +#define KVM_REG_PPC_TM_PPR (KVM_REG_PPC_TM | KVM_REG_SIZE_U64 | 0x65) +#define KVM_REG_PPC_TM_VRSAVE (KVM_REG_PPC_TM | KVM_REG_SIZE_U64 | 0x66) +#define KVM_REG_PPC_TM_VSCR (KVM_REG_PPC_TM | KVM_REG_SIZE_U32 | 0x67) +#define KVM_REG_PPC_TM_DSCR (KVM_REG_PPC_TM | KVM_REG_SIZE_U64 | 0x68) +#define KVM_REG_PPC_TM_TAR (KVM_REG_PPC_TM | KVM_REG_SIZE_U64 | 0x69) + /* PPC64 eXternal Interrupt Controller Specification */ #define KVM_DEV_XICS_GRP_SOURCES 1 /* 64-bit source attributes */ diff --git a/linux-headers/asm-x86/hyperv.h b/linux-headers/asm-x86/hyperv.h index b80420bcd0..b8f1c0176c 100644 --- a/linux-headers/asm-x86/hyperv.h +++ b/linux-headers/asm-x86/hyperv.h @@ -27,6 +27,19 @@ #define HV_X64_MSR_VP_RUNTIME_AVAILABLE (1 << 0) /* Partition Reference Counter (HV_X64_MSR_TIME_REF_COUNT) available*/ #define HV_X64_MSR_TIME_REF_COUNT_AVAILABLE (1 << 1) + +/* + * There is a single feature flag that signifies the presence of the MSR + * that can be used to retrieve both the local APIC Timer frequency as + * well as the TSC frequency. + */ + +/* Local APIC timer frequency MSR (HV_X64_MSR_APIC_FREQUENCY) is available */ +#define HV_X64_MSR_APIC_FREQUENCY_AVAILABLE (1 << 11) + +/* TSC frequency MSR (HV_X64_MSR_TSC_FREQUENCY) is available */ +#define HV_X64_MSR_TSC_FREQUENCY_AVAILABLE (1 << 11) + /* * Basic SynIC MSRs (HV_X64_MSR_SCONTROL through HV_X64_MSR_EOM * and HV_X64_MSR_SINT0 through HV_X64_MSR_SINT15) available @@ -136,6 +149,12 @@ /* MSR used to read the per-partition time reference counter */ #define HV_X64_MSR_TIME_REF_COUNT 0x40000020 +/* MSR used to retrieve the TSC frequency */ +#define HV_X64_MSR_TSC_FREQUENCY 0x40000022 + +/* MSR used to retrieve the local APIC timer frequency */ +#define HV_X64_MSR_APIC_FREQUENCY 0x40000023 + /* Define the virtual APIC registers */ #define HV_X64_MSR_EOI 0x40000070 #define HV_X64_MSR_ICR 0x40000071 diff --git a/linux-headers/asm-x86/kvm.h b/linux-headers/asm-x86/kvm.h index 5d9a3033b3..d3a87780c7 100644 --- a/linux-headers/asm-x86/kvm.h +++ b/linux-headers/asm-x86/kvm.h @@ -211,9 +211,9 @@ struct kvm_cpuid_entry2 { __u32 padding[3]; }; -#define KVM_CPUID_FLAG_SIGNIFCANT_INDEX 1 -#define KVM_CPUID_FLAG_STATEFUL_FUNC 2 -#define KVM_CPUID_FLAG_STATE_READ_NEXT 4 +#define KVM_CPUID_FLAG_SIGNIFCANT_INDEX BIT(0) +#define KVM_CPUID_FLAG_STATEFUL_FUNC BIT(1) +#define KVM_CPUID_FLAG_STATE_READ_NEXT BIT(2) /* for KVM_SET_CPUID2 */ struct kvm_cpuid2 { diff --git a/linux-headers/linux/kvm.h b/linux-headers/linux/kvm.h index 13e890c53b..5a49671845 100644 --- a/linux-headers/linux/kvm.h +++ b/linux-headers/linux/kvm.h @@ -518,6 +518,10 @@ struct kvm_ppc_smmu_info { /* machine type bits, to be used as argument to KVM_CREATE_VM */ #define KVM_VM_S390_UCONTROL 1 +/* on ppc, 0 indicate default, 1 should force HV and 2 PR */ +#define KVM_VM_PPC_HV 1 +#define KVM_VM_PPC_PR 2 + #define KVM_S390_SIE_PAGE_OFFSET 1 /* @@ -541,6 +545,7 @@ struct kvm_ppc_smmu_info { #define KVM_TRACE_ENABLE __KVM_DEPRECATED_MAIN_W_0x06 #define KVM_TRACE_PAUSE __KVM_DEPRECATED_MAIN_0x07 #define KVM_TRACE_DISABLE __KVM_DEPRECATED_MAIN_0x08 +#define KVM_GET_EMULATED_CPUID _IOWR(KVMIO, 0x09, struct kvm_cpuid2) /* * Extension capability list. @@ -668,6 +673,7 @@ struct kvm_ppc_smmu_info { #define KVM_CAP_IRQ_XICS 92 #define KVM_CAP_ARM_EL1_32BIT 93 #define KVM_CAP_SPAPR_MULTITCE 94 +#define KVM_CAP_EXT_EMUL_CPUID 95 #ifdef KVM_CAP_IRQ_ROUTING @@ -843,6 +849,10 @@ struct kvm_device_attr { #define KVM_DEV_TYPE_FSL_MPIC_20 1 #define KVM_DEV_TYPE_FSL_MPIC_42 2 #define KVM_DEV_TYPE_XICS 3 +#define KVM_DEV_TYPE_VFIO 4 +#define KVM_DEV_VFIO_GROUP 1 +#define KVM_DEV_VFIO_GROUP_ADD 1 +#define KVM_DEV_VFIO_GROUP_DEL 2 /* * ioctls for VM fds @@ -1012,6 +1022,7 @@ struct kvm_s390_ucas_mapping { /* VM is being stopped by host */ #define KVM_KVMCLOCK_CTRL _IO(KVMIO, 0xad) #define KVM_ARM_VCPU_INIT _IOW(KVMIO, 0xae, struct kvm_vcpu_init) +#define KVM_ARM_PREFERRED_TARGET _IOR(KVMIO, 0xaf, struct kvm_vcpu_init) #define KVM_GET_REG_LIST _IOWR(KVMIO, 0xb0, struct kvm_reg_list) #define KVM_DEV_ASSIGN_ENABLE_IOMMU (1 << 0) diff --git a/linux-user/aarch64/target_structs.h b/linux-user/aarch64/target_structs.h new file mode 100644 index 0000000000..21c1f2c074 --- /dev/null +++ b/linux-user/aarch64/target_structs.h @@ -0,0 +1,58 @@ +/* + * ARM AArch64 specific structures for linux-user + * + * Copyright (c) 2013 Fabrice Bellard + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, see <http://www.gnu.org/licenses/>. + */ +#ifndef TARGET_STRUCTS_H +#define TARGET_STRUCTS_H + +struct target_ipc_perm { + abi_int __key; /* Key. */ + abi_uint uid; /* Owner's user ID. */ + abi_uint gid; /* Owner's group ID. */ + abi_uint cuid; /* Creator's user ID. */ + abi_uint cgid; /* Creator's group ID. */ + abi_ushort mode; /* Read/write permission. */ + abi_ushort __pad1; + abi_ushort __seq; /* Sequence number. */ + abi_ushort __pad2; + abi_ulong __unused1; + abi_ulong __unused2; +}; + +struct target_shmid_ds { + struct target_ipc_perm shm_perm; /* operation permission struct */ + abi_long shm_segsz; /* size of segment in bytes */ + abi_ulong shm_atime; /* time of last shmat() */ +#if TARGET_ABI_BITS == 32 + abi_ulong __unused1; +#endif + abi_ulong shm_dtime; /* time of last shmdt() */ +#if TARGET_ABI_BITS == 32 + abi_ulong __unused2; +#endif + abi_ulong shm_ctime; /* time of last change by shmctl() */ +#if TARGET_ABI_BITS == 32 + abi_ulong __unused3; +#endif + abi_int shm_cpid; /* pid of creator */ + abi_int shm_lpid; /* pid of last shmop */ + abi_ulong shm_nattch; /* number of current attaches */ + abi_ulong __unused4; + abi_ulong __unused5; +}; + +#endif diff --git a/linux-user/alpha/target_structs.h b/linux-user/alpha/target_structs.h new file mode 100644 index 0000000000..50e7708ffd --- /dev/null +++ b/linux-user/alpha/target_structs.h @@ -0,0 +1,48 @@ +/* + * Alpha specific structures for linux-user + * + * Copyright (c) 2013 Fabrice Bellard + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, see <http://www.gnu.org/licenses/>. + */ +#ifndef TARGET_STRUCTS_H +#define TARGET_STRUCTS_H + +struct target_ipc_perm { + abi_int __key; /* Key. */ + abi_uint uid; /* Owner's user ID. */ + abi_uint gid; /* Owner's group ID. */ + abi_uint cuid; /* Creator's user ID. */ + abi_uint cgid; /* Creator's group ID. */ + abi_uint mode; /* Read/write permission. */ + abi_ushort __seq; /* Sequence number. */ + abi_ushort __pad1; + abi_ulong __unused1; + abi_ulong __unused2; +}; + +struct target_shmid_ds { + struct target_ipc_perm shm_perm; /* operation permission struct */ + abi_long shm_segsz; /* size of segment in bytes */ + abi_ulong shm_atime; /* time of last shmat() */ + abi_ulong shm_dtime; /* time of last shmdt() */ + abi_ulong shm_ctime; /* time of last change by shmctl() */ + abi_int shm_cpid; /* pid of creator */ + abi_int shm_lpid; /* pid of last shmop */ + abi_ulong shm_nattch; /* number of current attaches */ + abi_ulong __unused1; + abi_ulong __unused2; +}; + +#endif diff --git a/linux-user/arm/target_structs.h b/linux-user/arm/target_structs.h new file mode 100644 index 0000000000..f3c85d4e1f --- /dev/null +++ b/linux-user/arm/target_structs.h @@ -0,0 +1,52 @@ +/* + * ARM specific structures for linux-user + * + * Copyright (c) 2013 Fabrice Bellard + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, see <http://www.gnu.org/licenses/>. + */ +#ifndef TARGET_STRUCTS_H +#define TARGET_STRUCTS_H + +struct target_ipc_perm { + abi_int __key; /* Key. */ + abi_uint uid; /* Owner's user ID. */ + abi_uint gid; /* Owner's group ID. */ + abi_uint cuid; /* Creator's user ID. */ + abi_uint cgid; /* Creator's group ID. */ + abi_ushort mode; /* Read/write permission. */ + abi_ushort __pad1; + abi_ushort __seq; /* Sequence number. */ + abi_ushort __pad2; + abi_ulong __unused1; + abi_ulong __unused2; +}; + +struct target_shmid_ds { + struct target_ipc_perm shm_perm; /* operation permission struct */ + abi_long shm_segsz; /* size of segment in bytes */ + abi_ulong shm_atime; /* time of last shmat() */ + abi_ulong __unused1; + abi_ulong shm_dtime; /* time of last shmdt() */ + abi_ulong __unused2; + abi_ulong shm_ctime; /* time of last change by shmctl() */ + abi_ulong __unused3; + abi_int shm_cpid; /* pid of creator */ + abi_int shm_lpid; /* pid of last shmop */ + abi_ulong shm_nattch; /* number of current attaches */ + abi_ulong __unused4; + abi_ulong __unused5; +}; + +#endif diff --git a/linux-user/cris/target_structs.h b/linux-user/cris/target_structs.h new file mode 100644 index 0000000000..e4a1ffb3c1 --- /dev/null +++ b/linux-user/cris/target_structs.h @@ -0,0 +1,58 @@ +/* + * CRIS specific structures for linux-user + * + * Copyright (c) 2013 Fabrice Bellard + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, see <http://www.gnu.org/licenses/>. + */ +#ifndef TARGET_STRUCTS_H +#define TARGET_STRUCTS_H + +struct target_ipc_perm { + abi_int __key; /* Key. */ + abi_uint uid; /* Owner's user ID. */ + abi_uint gid; /* Owner's group ID. */ + abi_uint cuid; /* Creator's user ID. */ + abi_uint cgid; /* Creator's group ID. */ + abi_ushort mode; /* Read/write permission. */ + abi_ushort __pad1; + abi_ushort __seq; /* Sequence number. */ + abi_ushort __pad2; + abi_ulong __unused1; + abi_ulong __unused2; +}; + +struct target_shmid_ds { + struct target_ipc_perm shm_perm; /* operation permission struct */ + abi_long shm_segsz; /* size of segment in bytes */ + abi_ulong shm_atime; /* time of last shmat() */ +#if TARGET_ABI_BITS == 32 + abi_ulong __unused1; +#endif + abi_ulong shm_dtime; /* time of last shmdt() */ +#if TARGET_ABI_BITS == 32 + abi_ulong __unused2; +#endif + abi_ulong shm_ctime; /* time of last change by shmctl() */ +#if TARGET_ABI_BITS == 32 + abi_ulong __unused3; +#endif + abi_int shm_cpid; /* pid of creator */ + abi_int shm_lpid; /* pid of last shmop */ + abi_ulong shm_nattch; /* number of current attaches */ + abi_ulong __unused4; + abi_ulong __unused5; +}; + +#endif diff --git a/linux-user/flatload.c b/linux-user/flatload.c index 58f679e072..ceb89bb6ea 100644 --- a/linux-user/flatload.c +++ b/linux-user/flatload.c @@ -633,7 +633,7 @@ static int load_flat_file(struct linux_binprm * bprm, /* Get the pointer's value. */ if (get_user_ual(addr, rp)) return -EFAULT; - addr = flat_get_addr_from_rp(rp, relval, flags, &persistent); + addr = flat_get_addr_from_rp(addr, relval, flags, &persistent); if (addr != 0) { /* * Do the relocation. PIC relocs in the data section are diff --git a/linux-user/i386/target_structs.h b/linux-user/i386/target_structs.h new file mode 100644 index 0000000000..65f535e16b --- /dev/null +++ b/linux-user/i386/target_structs.h @@ -0,0 +1,58 @@ +/* + * i386 specific structures for linux-user + * + * Copyright (c) 2013 Fabrice Bellard + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, see <http://www.gnu.org/licenses/>. + */ +#ifndef TARGET_STRUCTS_H +#define TARGET_STRUCTS_H + +struct target_ipc_perm { + abi_int __key; /* Key. */ + abi_uint uid; /* Owner's user ID. */ + abi_uint gid; /* Owner's group ID. */ + abi_uint cuid; /* Creator's user ID. */ + abi_uint cgid; /* Creator's group ID. */ + abi_ushort mode; /* Read/write permission. */ + abi_ushort __pad1; + abi_ushort __seq; /* Sequence number. */ + abi_ushort __pad2; + abi_ulong __unused1; + abi_ulong __unused2; +}; + +struct target_shmid_ds { + struct target_ipc_perm shm_perm; /* operation permission struct */ + abi_long shm_segsz; /* size of segment in bytes */ + abi_ulong shm_atime; /* time of last shmat() */ +#if TARGET_ABI_BITS == 32 + abi_ulong __unused1; +#endif + abi_ulong shm_dtime; /* time of last shmdt() */ +#if TARGET_ABI_BITS == 32 + abi_ulong __unused2; +#endif + abi_ulong shm_ctime; /* time of last change by shmctl() */ +#if TARGET_ABI_BITS == 32 + abi_ulong __unused3; +#endif + abi_int shm_cpid; /* pid of creator */ + abi_int shm_lpid; /* pid of last shmop */ + abi_ulong shm_nattch; /* number of current attaches */ + abi_ulong __unused4; + abi_ulong __unused5; +}; + +#endif diff --git a/linux-user/m68k/target_structs.h b/linux-user/m68k/target_structs.h new file mode 100644 index 0000000000..de257c97de --- /dev/null +++ b/linux-user/m68k/target_structs.h @@ -0,0 +1,58 @@ +/* + * m68k specific structures for linux-user + * + * Copyright (c) 2013 Fabrice Bellard + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, see <http://www.gnu.org/licenses/>. + */ +#ifndef TARGET_STRUCTS_H +#define TARGET_STRUCTS_H + +struct target_ipc_perm { + abi_int __key; /* Key. */ + abi_uint uid; /* Owner's user ID. */ + abi_uint gid; /* Owner's group ID. */ + abi_uint cuid; /* Creator's user ID. */ + abi_uint cgid; /* Creator's group ID. */ + abi_ushort mode; /* Read/write permission. */ + abi_ushort __pad1; + abi_ushort __seq; /* Sequence number. */ + abi_ushort __pad2; + abi_ulong __unused1; + abi_ulong __unused2; +}; + +struct target_shmid_ds { + struct target_ipc_perm shm_perm; /* operation permission struct */ + abi_long shm_segsz; /* size of segment in bytes */ + abi_ulong shm_atime; /* time of last shmat() */ +#if TARGET_ABI_BITS == 32 + abi_ulong __unused1; +#endif + abi_ulong shm_dtime; /* time of last shmdt() */ +#if TARGET_ABI_BITS == 32 + abi_ulong __unused2; +#endif + abi_ulong shm_ctime; /* time of last change by shmctl() */ +#if TARGET_ABI_BITS == 32 + abi_ulong __unused3; +#endif + abi_int shm_cpid; /* pid of creator */ + abi_int shm_lpid; /* pid of last shmop */ + abi_ulong shm_nattch; /* number of current attaches */ + abi_ulong __unused4; + abi_ulong __unused5; +}; + +#endif diff --git a/linux-user/main.c b/linux-user/main.c index 6b4ab0930e..54f71fe8f6 100644 --- a/linux-user/main.c +++ b/linux-user/main.c @@ -3663,26 +3663,6 @@ static int parse_args(int argc, char **argv) return optind; } -static int get_execfd(char **envp) -{ - typedef struct { - long a_type; - long a_val; - } auxv_t; - auxv_t *auxv; - - while (*envp++ != NULL) { - ; - } - - for (auxv = (auxv_t *)envp; auxv->a_type != AT_NULL; auxv++) { - if (auxv->a_type == AT_EXECFD) { - return auxv->a_val; - } - } - return -1; -} - int main(int argc, char **argv, char **envp) { struct target_pt_regs regs1, *regs = ®s1; @@ -3701,7 +3681,8 @@ int main(int argc, char **argv, char **envp) module_call_init(MODULE_INIT_QOM); - qemu_cache_utils_init(envp); + qemu_init_auxval(envp); + qemu_cache_utils_init(); if ((envlist = envlist_create()) == NULL) { (void) fprintf(stderr, "Unable to allocate envlist\n"); @@ -3875,13 +3856,13 @@ int main(int argc, char **argv, char **envp) env->opaque = ts; task_settid(ts); - execfd = get_execfd(envp); - if (execfd < 0) { + execfd = qemu_getauxval(AT_EXECFD); + if (execfd == 0) { execfd = open(filename, O_RDONLY); - } - if (execfd < 0) { - printf("Error while loading %s: %s\n", filename, strerror(-execfd)); - _exit(1); + if (execfd < 0) { + printf("Error while loading %s: %s\n", filename, strerror(errno)); + _exit(1); + } } ret = loader_exec(execfd, filename, target_argv, target_environ, regs, diff --git a/linux-user/microblaze/target_structs.h b/linux-user/microblaze/target_structs.h new file mode 100644 index 0000000000..325e2f6d4d --- /dev/null +++ b/linux-user/microblaze/target_structs.h @@ -0,0 +1,58 @@ +/* + * MicroBlaze specific structures for linux-user + * + * Copyright (c) 2013 Fabrice Bellard + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, see <http://www.gnu.org/licenses/>. + */ +#ifndef TARGET_STRUCTS_H +#define TARGET_STRUCTS_H + +struct target_ipc_perm { + abi_int __key; /* Key. */ + abi_uint uid; /* Owner's user ID. */ + abi_uint gid; /* Owner's group ID. */ + abi_uint cuid; /* Creator's user ID. */ + abi_uint cgid; /* Creator's group ID. */ + abi_ushort mode; /* Read/write permission. */ + abi_ushort __pad1; + abi_ushort __seq; /* Sequence number. */ + abi_ushort __pad2; + abi_ulong __unused1; + abi_ulong __unused2; +}; + +struct target_shmid_ds { + struct target_ipc_perm shm_perm; /* operation permission struct */ + abi_long shm_segsz; /* size of segment in bytes */ + abi_ulong shm_atime; /* time of last shmat() */ +#if TARGET_ABI_BITS == 32 + abi_ulong __unused1; +#endif + abi_ulong shm_dtime; /* time of last shmdt() */ +#if TARGET_ABI_BITS == 32 + abi_ulong __unused2; +#endif + abi_ulong shm_ctime; /* time of last change by shmctl() */ +#if TARGET_ABI_BITS == 32 + abi_ulong __unused3; +#endif + abi_int shm_cpid; /* pid of creator */ + abi_int shm_lpid; /* pid of last shmop */ + abi_ulong shm_nattch; /* number of current attaches */ + abi_ulong __unused4; + abi_ulong __unused5; +}; + +#endif diff --git a/linux-user/mips/target_structs.h b/linux-user/mips/target_structs.h new file mode 100644 index 0000000000..16021e8a94 --- /dev/null +++ b/linux-user/mips/target_structs.h @@ -0,0 +1,48 @@ +/* + * MIPS specific structures for linux-user + * + * Copyright (c) 2013 Fabrice Bellard + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, see <http://www.gnu.org/licenses/>. + */ +#ifndef TARGET_STRUCTS_H +#define TARGET_STRUCTS_H + +struct target_ipc_perm { + abi_int __key; /* Key. */ + abi_uint uid; /* Owner's user ID. */ + abi_uint gid; /* Owner's group ID. */ + abi_uint cuid; /* Creator's user ID. */ + abi_uint cgid; /* Creator's group ID. */ + abi_uint mode; /* Read/write permission. */ + abi_ushort __seq; /* Sequence number. */ + abi_ushort __pad1; + abi_ulong __unused1; + abi_ulong __unused2; +}; + +struct target_shmid_ds { + struct target_ipc_perm shm_perm; /* operation permission struct */ + abi_long shm_segsz; /* size of segment in bytes */ + abi_ulong shm_atime; /* time of last shmat() */ + abi_ulong shm_dtime; /* time of last shmdt() */ + abi_ulong shm_ctime; /* time of last change by shmctl() */ + abi_int shm_cpid; /* pid of creator */ + abi_int shm_lpid; /* pid of last shmop */ + abi_ulong shm_nattch; /* number of current attaches */ + abi_ulong __unused1; + abi_ulong __unused2; +}; + +#endif diff --git a/linux-user/mips64/target_cpu.h b/linux-user/mips64/target_cpu.h index fa36407c68..f16991b4ef 100644 --- a/linux-user/mips64/target_cpu.h +++ b/linux-user/mips64/target_cpu.h @@ -1 +1,19 @@ +/* + * MIPS64 specific structures for linux-user + * + * Copyright (c) 2013 Fabrice Bellard + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, see <http://www.gnu.org/licenses/>. + */ #include "../mips/target_cpu.h" diff --git a/linux-user/mips64/target_structs.h b/linux-user/mips64/target_structs.h new file mode 100644 index 0000000000..a4f619e732 --- /dev/null +++ b/linux-user/mips64/target_structs.h @@ -0,0 +1,2 @@ +#include "../mips/target_structs.h" + diff --git a/linux-user/openrisc/target_structs.h b/linux-user/openrisc/target_structs.h new file mode 100644 index 0000000000..f4d560f575 --- /dev/null +++ b/linux-user/openrisc/target_structs.h @@ -0,0 +1,58 @@ +/* + * OpenRISC specific structures for linux-user + * + * Copyright (c) 2013 Fabrice Bellard + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, see <http://www.gnu.org/licenses/>. + */ +#ifndef TARGET_STRUCTS_H +#define TARGET_STRUCTS_H + +struct target_ipc_perm { + abi_int __key; /* Key. */ + abi_uint uid; /* Owner's user ID. */ + abi_uint gid; /* Owner's group ID. */ + abi_uint cuid; /* Creator's user ID. */ + abi_uint cgid; /* Creator's group ID. */ + abi_ushort mode; /* Read/write permission. */ + abi_ushort __pad1; + abi_ushort __seq; /* Sequence number. */ + abi_ushort __pad2; + abi_ulong __unused1; + abi_ulong __unused2; +}; + +struct target_shmid_ds { + struct target_ipc_perm shm_perm; /* operation permission struct */ + abi_long shm_segsz; /* size of segment in bytes */ + abi_ulong shm_atime; /* time of last shmat() */ +#if TARGET_ABI_BITS == 32 + abi_ulong __unused1; +#endif + abi_ulong shm_dtime; /* time of last shmdt() */ +#if TARGET_ABI_BITS == 32 + abi_ulong __unused2; +#endif + abi_ulong shm_ctime; /* time of last change by shmctl() */ +#if TARGET_ABI_BITS == 32 + abi_ulong __unused3; +#endif + abi_int shm_cpid; /* pid of creator */ + abi_int shm_lpid; /* pid of last shmop */ + abi_ulong shm_nattch; /* number of current attaches */ + abi_ulong __unused4; + abi_ulong __unused5; +}; + +#endif diff --git a/linux-user/ppc/target_structs.h b/linux-user/ppc/target_structs.h new file mode 100644 index 0000000000..2b87613104 --- /dev/null +++ b/linux-user/ppc/target_structs.h @@ -0,0 +1,60 @@ +/* + * PowerPC specific structures for linux-user + * + * Copyright (c) 2013 Fabrice Bellard + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, see <http://www.gnu.org/licenses/>. + */ +#ifndef TARGET_STRUCTS_H +#define TARGET_STRUCTS_H + +struct target_ipc_perm { + abi_int __key; /* Key. */ + abi_uint uid; /* Owner's user ID. */ + abi_uint gid; /* Owner's group ID. */ + abi_uint cuid; /* Creator's user ID. */ + abi_uint cgid; /* Creator's group ID. */ + abi_uint mode; /* Read/write permission. */ + uint32_t __seq; /* Sequence number. */ + uint32_t __pad1; + uint64_t __unused1; + uint64_t __unused2; +}; + +struct target_shmid_ds { + struct target_ipc_perm shm_perm; /* operation permission struct */ +#if TARGET_ABI_BITS == 32 + abi_uint __unused1; +#endif + abi_ulong shm_atime; /* time of last shmat() */ +#if TARGET_ABI_BITS == 32 + abi_uint __unused2; +#endif + abi_ulong shm_dtime; /* time of last shmdt() */ +#if TARGET_ABI_BITS == 32 + abi_uint __unused3; +#endif + abi_ulong shm_ctime; /* time of last change by shmctl() */ +#if TARGET_ABI_BITS == 32 + abi_uint __unused4; +#endif + abi_long shm_segsz; /* size of segment in bytes */ + abi_int shm_cpid; /* pid of creator */ + abi_int shm_lpid; /* pid of last shmop */ + abi_ulong shm_nattch; /* number of current attaches */ + abi_ulong __unused5; + abi_ulong __unused6; +}; + +#endif diff --git a/linux-user/qemu.h b/linux-user/qemu.h index da64e877c7..e2717e0775 100644 --- a/linux-user/qemu.h +++ b/linux-user/qemu.h @@ -452,5 +452,6 @@ static inline void *lock_user_string(abi_ulong guest_addr) */ #include "target_cpu.h" #include "target_signal.h" +#include "target_structs.h" #endif /* QEMU_H */ diff --git a/linux-user/s390x/target_structs.h b/linux-user/s390x/target_structs.h new file mode 100644 index 0000000000..6b6f5b5212 --- /dev/null +++ b/linux-user/s390x/target_structs.h @@ -0,0 +1,63 @@ +/* + * S/390 specific structures for linux-user + * + * Copyright (c) 2013 Fabrice Bellard + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, see <http://www.gnu.org/licenses/>. + */ +#ifndef TARGET_STRUCTS_H +#define TARGET_STRUCTS_H + + +struct target_ipc_perm { + abi_int __key; /* Key. */ + abi_uint uid; /* Owner's user ID. */ + abi_uint gid; /* Owner's group ID. */ + abi_uint cuid; /* Creator's user ID. */ + abi_uint cgid; /* Creator's group ID. */ +#if TARGET_ABI_BITS == 64 + abi_uint mode; /* Read/write permission. */ +#else + abi_ushort mode; /* Read/write permission. */ + abi_ushort __pad1; +#endif + abi_ushort __seq; /* Sequence number. */ + abi_ushort __pad2; + abi_ulong __unused1; + abi_ulong __unused2; +}; + +struct target_shmid_ds { + struct target_ipc_perm shm_perm; /* operation permission struct */ + abi_long shm_segsz; /* size of segment in bytes */ + abi_ulong shm_atime; /* time of last shmat() */ +#if TARGET_ABI_BITS == 32 + abi_ulong __unused1; +#endif + abi_ulong shm_dtime; /* time of last shmdt() */ +#if TARGET_ABI_BITS == 32 + abi_ulong __unused2; +#endif + abi_ulong shm_ctime; /* time of last change by shmctl() */ +#if TARGET_ABI_BITS == 32 + abi_ulong __unused3; +#endif + abi_int shm_cpid; /* pid of creator */ + abi_int shm_lpid; /* pid of last shmop */ + abi_ulong shm_nattch; /* number of current attaches */ + abi_ulong __unused4; + abi_ulong __unused5; +}; + +#endif diff --git a/linux-user/sh4/target_structs.h b/linux-user/sh4/target_structs.h new file mode 100644 index 0000000000..32b235e0b3 --- /dev/null +++ b/linux-user/sh4/target_structs.h @@ -0,0 +1,58 @@ +/* + * SH4 specific structures for linux-user + * + * Copyright (c) 2013 Fabrice Bellard + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, see <http://www.gnu.org/licenses/>. + */ +#ifndef TARGET_STRUCTS_H +#define TARGET_STRUCTS_H + +struct target_ipc_perm { + abi_int __key; /* Key. */ + abi_uint uid; /* Owner's user ID. */ + abi_uint gid; /* Owner's group ID. */ + abi_uint cuid; /* Creator's user ID. */ + abi_uint cgid; /* Creator's group ID. */ + abi_ushort mode; /* Read/write permission. */ + abi_ushort __pad1; + abi_ushort __seq; /* Sequence number. */ + abi_ushort __pad2; + abi_ulong __unused1; + abi_ulong __unused2; +}; + +struct target_shmid_ds { + struct target_ipc_perm shm_perm; /* operation permission struct */ + abi_long shm_segsz; /* size of segment in bytes */ + abi_ulong shm_atime; /* time of last shmat() */ +#if TARGET_ABI_BITS == 32 + abi_ulong __unused1; +#endif + abi_ulong shm_dtime; /* time of last shmdt() */ +#if TARGET_ABI_BITS == 32 + abi_ulong __unused2; +#endif + abi_ulong shm_ctime; /* time of last change by shmctl() */ +#if TARGET_ABI_BITS == 32 + abi_ulong __unused3; +#endif + abi_int shm_cpid; /* pid of creator */ + abi_int shm_lpid; /* pid of last shmop */ + abi_ulong shm_nattch; /* number of current attaches */ + abi_ulong __unused4; + abi_ulong __unused5; +}; + +#endif diff --git a/linux-user/sparc/target_structs.h b/linux-user/sparc/target_structs.h new file mode 100644 index 0000000000..c139e09a61 --- /dev/null +++ b/linux-user/sparc/target_structs.h @@ -0,0 +1,63 @@ +/* + * SPARC specific structures for linux-user + * + * Copyright (c) 2013 Fabrice Bellard + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, see <http://www.gnu.org/licenses/>. + */ +#ifndef TARGET_STRUCTS_H +#define TARGET_STRUCTS_H + +struct target_ipc_perm { + abi_int __key; /* Key. */ + abi_uint uid; /* Owner's user ID. */ + abi_uint gid; /* Owner's group ID. */ + abi_uint cuid; /* Creator's user ID. */ + abi_uint cgid; /* Creator's group ID. */ +#if TARGET_ABI_BITS == 32 + abi_ushort __pad1; + abi_ushort mode; /* Read/write permission. */ + abi_ushort __pad2; +#else + abi_ushort mode; + abi_ushort __pad1; +#endif + abi_ushort __seq; /* Sequence number. */ + uint64_t __unused1; + uint64_t __unused2; +}; + +struct target_shmid_ds { + struct target_ipc_perm shm_perm; /* operation permission struct */ +#if TARGET_ABI_BITS == 32 + abi_uint __pad1; +#endif + abi_ulong shm_atime; /* time of last shmat() */ +#if TARGET_ABI_BITS == 32 + abi_uint __pad2; +#endif + abi_ulong shm_dtime; /* time of last shmdt() */ +#if TARGET_ABI_BITS == 32 + abi_uint __pad3; +#endif + abi_ulong shm_ctime; /* time of last change by shmctl() */ + abi_long shm_segsz; /* size of segment in bytes */ + abi_ulong shm_cpid; /* pid of creator */ + abi_ulong shm_lpid; /* pid of last shmop */ + abi_long shm_nattch; /* number of current attaches */ + abi_ulong __unused1; + abi_ulong __unused2; +}; + +#endif diff --git a/linux-user/sparc64/target_structs.h b/linux-user/sparc64/target_structs.h new file mode 100644 index 0000000000..fc1729007d --- /dev/null +++ b/linux-user/sparc64/target_structs.h @@ -0,0 +1,58 @@ +/* + * SPARC64 specific structures for linux-user + * + * Copyright (c) 2013 Fabrice Bellard + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, see <http://www.gnu.org/licenses/>. + */ +#ifndef TARGET_STRUCTS_H +#define TARGET_STRUCTS_H + +struct target_ipc_perm { + abi_int __key; /* Key. */ + abi_uint uid; /* Owner's user ID. */ + abi_uint gid; /* Owner's group ID. */ + abi_uint cuid; /* Creator's user ID. */ + abi_uint cgid; /* Creator's group ID. */ + abi_ushort mode; /* Read/write permission. */ + abi_ushort __pad1; + abi_ushort __seq; /* Sequence number. */ + abi_ushort __pad2; + abi_ulong __unused1; + abi_ulong __unused2; +}; + +struct target_shmid_ds { + struct target_ipc_perm shm_perm; /* operation permission struct */ + abi_long shm_segsz; /* size of segment in bytes */ + abi_ulong shm_atime; /* time of last shmat() */ +#if TARGET_ABI_BITS == 32 + abi_ulong __unused1; +#endif + abi_ulong shm_dtime; /* time of last shmdt() */ +#if TARGET_ABI_BITS == 32 + abi_ulong __unused2; +#endif + abi_ulong shm_ctime; /* time of last change by shmctl() */ +#if TARGET_ABI_BITS == 32 + abi_ulong __unused3; +#endif + abi_int shm_cpid; /* pid of creator */ + abi_int shm_lpid; /* pid of last shmop */ + abi_ulong shm_nattch; /* number of current attaches */ + abi_ulong __unused4; + abi_ulong __unused5; +}; + +#endif diff --git a/linux-user/syscall.c b/linux-user/syscall.c index eaaf00ddd0..efd1453987 100644 --- a/linux-user/syscall.c +++ b/linux-user/syscall.c @@ -428,6 +428,25 @@ _syscall4(int, sys_prlimit64, pid_t, pid, int, resource, struct host_rlimit64 *, old_limit) #endif + +#if defined(TARGET_NR_timer_create) +/* Maxiumum of 32 active POSIX timers allowed at any one time. */ +static timer_t g_posix_timers[32] = { 0, } ; + +static inline int next_free_host_timer(void) +{ + int k ; + /* FIXME: Does finding the next free slot require a lock? */ + for (k = 0; k < ARRAY_SIZE(g_posix_timers); k++) { + if (g_posix_timers[k] == 0) { + g_posix_timers[k] = (timer_t) 1; + return k; + } + } + return -1; +} +#endif + /* ARM EABI and MIPS expect 64bit types aligned even on pairs or registers */ #ifdef TARGET_ARM static inline int regpairs_aligned(void *cpu_env) { @@ -2417,21 +2436,6 @@ static struct shm_region { abi_ulong size; } shm_regions[N_SHM_REGIONS]; -struct target_ipc_perm -{ - abi_long __key; - abi_ulong uid; - abi_ulong gid; - abi_ulong cuid; - abi_ulong cgid; - unsigned short int mode; - unsigned short int __pad1; - unsigned short int __seq; - unsigned short int __pad2; - abi_ulong __unused1; - abi_ulong __unused2; -}; - struct target_semid_ds { struct target_ipc_perm sem_perm; @@ -2453,12 +2457,21 @@ static inline abi_long target_to_host_ipc_perm(struct ipc_perm *host_ip, if (!lock_user_struct(VERIFY_READ, target_sd, target_addr, 1)) return -TARGET_EFAULT; target_ip = &(target_sd->sem_perm); - host_ip->__key = tswapal(target_ip->__key); - host_ip->uid = tswapal(target_ip->uid); - host_ip->gid = tswapal(target_ip->gid); - host_ip->cuid = tswapal(target_ip->cuid); - host_ip->cgid = tswapal(target_ip->cgid); + host_ip->__key = tswap32(target_ip->__key); + host_ip->uid = tswap32(target_ip->uid); + host_ip->gid = tswap32(target_ip->gid); + host_ip->cuid = tswap32(target_ip->cuid); + host_ip->cgid = tswap32(target_ip->cgid); +#if defined(TARGET_ALPHA) || defined(TARGET_MIPS) || defined(TARGET_PPC) + host_ip->mode = tswap32(target_ip->mode); +#else host_ip->mode = tswap16(target_ip->mode); +#endif +#if defined(TARGET_PPC) + host_ip->__seq = tswap32(target_ip->__seq); +#else + host_ip->__seq = tswap16(target_ip->__seq); +#endif unlock_user_struct(target_sd, target_addr, 0); return 0; } @@ -2472,12 +2485,21 @@ static inline abi_long host_to_target_ipc_perm(abi_ulong target_addr, if (!lock_user_struct(VERIFY_WRITE, target_sd, target_addr, 0)) return -TARGET_EFAULT; target_ip = &(target_sd->sem_perm); - target_ip->__key = tswapal(host_ip->__key); - target_ip->uid = tswapal(host_ip->uid); - target_ip->gid = tswapal(host_ip->gid); - target_ip->cuid = tswapal(host_ip->cuid); - target_ip->cgid = tswapal(host_ip->cgid); + target_ip->__key = tswap32(host_ip->__key); + target_ip->uid = tswap32(host_ip->uid); + target_ip->gid = tswap32(host_ip->gid); + target_ip->cuid = tswap32(host_ip->cuid); + target_ip->cgid = tswap32(host_ip->cgid); +#if defined(TARGET_ALPHA) || defined(TARGET_MIPS) || defined(TARGET_PPC) + target_ip->mode = tswap32(host_ip->mode); +#else target_ip->mode = tswap16(host_ip->mode); +#endif +#if defined(TARGET_PPC) + target_ip->__seq = tswap32(host_ip->__seq); +#else + target_ip->__seq = tswap16(host_ip->__seq); +#endif unlock_user_struct(target_sd, target_addr, 1); return 0; } @@ -2908,29 +2930,6 @@ end: return ret; } -struct target_shmid_ds -{ - struct target_ipc_perm shm_perm; - abi_ulong shm_segsz; - abi_ulong shm_atime; -#if TARGET_ABI_BITS == 32 - abi_ulong __unused1; -#endif - abi_ulong shm_dtime; -#if TARGET_ABI_BITS == 32 - abi_ulong __unused2; -#endif - abi_ulong shm_ctime; -#if TARGET_ABI_BITS == 32 - abi_ulong __unused3; -#endif - int shm_cpid; - int shm_lpid; - abi_ulong shm_nattch; - unsigned long int __unused4; - unsigned long int __unused5; -}; - static inline abi_long target_to_host_shmid_ds(struct shmid_ds *host_sd, abi_ulong target_addr) { @@ -3216,7 +3215,7 @@ static abi_long do_ipc(unsigned int call, int first, /* IPC_* and SHM_* command values are the same on all linux platforms */ case IPCOP_shmctl: - ret = do_shmctl(first, second, third); + ret = do_shmctl(first, second, ptr); break; default: gemu_log("Unsupported ipc call: %d (version %d)\n", call, version); @@ -4838,6 +4837,45 @@ static inline abi_long host_to_target_timespec(abi_ulong target_addr, return 0; } +static inline abi_long target_to_host_itimerspec(struct itimerspec *host_itspec, + abi_ulong target_addr) +{ + struct target_itimerspec *target_itspec; + + if (!lock_user_struct(VERIFY_READ, target_itspec, target_addr, 1)) { + return -TARGET_EFAULT; + } + + host_itspec->it_interval.tv_sec = + tswapal(target_itspec->it_interval.tv_sec); + host_itspec->it_interval.tv_nsec = + tswapal(target_itspec->it_interval.tv_nsec); + host_itspec->it_value.tv_sec = tswapal(target_itspec->it_value.tv_sec); + host_itspec->it_value.tv_nsec = tswapal(target_itspec->it_value.tv_nsec); + + unlock_user_struct(target_itspec, target_addr, 1); + return 0; +} + +static inline abi_long host_to_target_itimerspec(abi_ulong target_addr, + struct itimerspec *host_its) +{ + struct target_itimerspec *target_itspec; + + if (!lock_user_struct(VERIFY_WRITE, target_itspec, target_addr, 0)) { + return -TARGET_EFAULT; + } + + target_itspec->it_interval.tv_sec = tswapal(host_its->it_interval.tv_sec); + target_itspec->it_interval.tv_nsec = tswapal(host_its->it_interval.tv_nsec); + + target_itspec->it_value.tv_sec = tswapal(host_its->it_value.tv_sec); + target_itspec->it_value.tv_nsec = tswapal(host_its->it_value.tv_nsec); + + unlock_user_struct(target_itspec, target_addr, 0); + return 0; +} + #if defined(TARGET_NR_stat64) || defined(TARGET_NR_newfstatat) static inline abi_long host_to_target_stat64(void *cpu_env, abi_ulong target_addr, @@ -9195,6 +9233,124 @@ abi_long do_syscall(void *cpu_env, int num, abi_long arg1, break; } #endif + +#ifdef TARGET_NR_timer_create + case TARGET_NR_timer_create: + { + /* args: clockid_t clockid, struct sigevent *sevp, timer_t *timerid */ + + struct sigevent host_sevp = { {0}, }, *phost_sevp = NULL; + struct target_sigevent *ptarget_sevp; + struct target_timer_t *ptarget_timer; + + int clkid = arg1; + int timer_index = next_free_host_timer(); + + if (timer_index < 0) { + ret = -TARGET_EAGAIN; + } else { + timer_t *phtimer = g_posix_timers + timer_index; + + if (arg2) { + if (!lock_user_struct(VERIFY_READ, ptarget_sevp, arg2, 1)) { + goto efault; + } + + host_sevp.sigev_signo = tswap32(ptarget_sevp->sigev_signo); + host_sevp.sigev_notify = tswap32(ptarget_sevp->sigev_notify); + + phost_sevp = &host_sevp; + } + + ret = get_errno(timer_create(clkid, phost_sevp, phtimer)); + if (ret) { + phtimer = NULL; + } else { + if (!lock_user_struct(VERIFY_WRITE, ptarget_timer, arg3, 1)) { + goto efault; + } + ptarget_timer->ptr = tswap32(0xcafe0000 | timer_index); + unlock_user_struct(ptarget_timer, arg3, 1); + } + } + break; + } +#endif + +#ifdef TARGET_NR_timer_settime + case TARGET_NR_timer_settime: + { + /* args: timer_t timerid, int flags, const struct itimerspec *new_value, + * struct itimerspec * old_value */ + arg1 &= 0xffff; + if (arg3 == 0 || arg1 < 0 || arg1 >= ARRAY_SIZE(g_posix_timers)) { + ret = -TARGET_EINVAL; + } else { + timer_t htimer = g_posix_timers[arg1]; + struct itimerspec hspec_new = {{0},}, hspec_old = {{0},}; + + target_to_host_itimerspec(&hspec_new, arg3); + ret = get_errno( + timer_settime(htimer, arg2, &hspec_new, &hspec_old)); + host_to_target_itimerspec(arg2, &hspec_old); + } + break; + } +#endif + +#ifdef TARGET_NR_timer_gettime + case TARGET_NR_timer_gettime: + { + /* args: timer_t timerid, struct itimerspec *curr_value */ + arg1 &= 0xffff; + if (!arg2) { + return -TARGET_EFAULT; + } else if (arg1 < 0 || arg1 >= ARRAY_SIZE(g_posix_timers)) { + ret = -TARGET_EINVAL; + } else { + timer_t htimer = g_posix_timers[arg1]; + struct itimerspec hspec; + ret = get_errno(timer_gettime(htimer, &hspec)); + + if (host_to_target_itimerspec(arg2, &hspec)) { + ret = -TARGET_EFAULT; + } + } + break; + } +#endif + +#ifdef TARGET_NR_timer_getoverrun + case TARGET_NR_timer_getoverrun: + { + /* args: timer_t timerid */ + arg1 &= 0xffff; + if (arg1 < 0 || arg1 >= ARRAY_SIZE(g_posix_timers)) { + ret = -TARGET_EINVAL; + } else { + timer_t htimer = g_posix_timers[arg1]; + ret = get_errno(timer_getoverrun(htimer)); + } + break; + } +#endif + +#ifdef TARGET_NR_timer_delete + case TARGET_NR_timer_delete: + { + /* args: timer_t timerid */ + arg1 &= 0xffff; + if (arg1 < 0 || arg1 >= ARRAY_SIZE(g_posix_timers)) { + ret = -TARGET_EINVAL; + } else { + timer_t htimer = g_posix_timers[arg1]; + ret = get_errno(timer_delete(htimer)); + g_posix_timers[arg1] = 0; + } + break; + } +#endif + default: unimplemented: gemu_log("qemu: Unsupported syscall: %d\n", num); diff --git a/linux-user/syscall_defs.h b/linux-user/syscall_defs.h index fe540f6563..cf08db5a23 100644 --- a/linux-user/syscall_defs.h +++ b/linux-user/syscall_defs.h @@ -168,6 +168,11 @@ struct target_itimerval { struct target_timeval it_value; }; +struct target_itimerspec { + struct target_timespec it_interval; + struct target_timespec it_value; +}; + typedef abi_long target_clock_t; #define TARGET_HZ 100 @@ -2527,3 +2532,23 @@ struct target_ucred { }; #endif + + +struct target_timer_t { + abi_ulong ptr; +}; + +struct target_sigevent { + target_sigval_t sigev_value; + int32_t sigev_signo; + int32_t sigev_notify; + union { + int32_t _pad[ARRAY_SIZE(((struct sigevent *)0)->_sigev_un._pad)]; + int32_t _tid; + + struct { + void (*_function)(sigval_t); + void *_attribute; + } _sigev_thread; + } _sigev_un; +}; diff --git a/linux-user/unicore32/target_structs.h b/linux-user/unicore32/target_structs.h new file mode 100644 index 0000000000..789369503b --- /dev/null +++ b/linux-user/unicore32/target_structs.h @@ -0,0 +1,58 @@ +/* + * UniCore32 specific structures for linux-user + * + * Copyright (c) 2013 Fabrice Bellard + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, see <http://www.gnu.org/licenses/>. + */ +#ifndef TARGET_STRUCTS_H +#define TARGET_STRUCTS_H + +struct target_ipc_perm { + abi_int __key; /* Key. */ + abi_uint uid; /* Owner's user ID. */ + abi_uint gid; /* Owner's group ID. */ + abi_uint cuid; /* Creator's user ID. */ + abi_uint cgid; /* Creator's group ID. */ + abi_ushort mode; /* Read/write permission. */ + abi_ushort __pad1; + abi_ushort __seq; /* Sequence number. */ + abi_ushort __pad2; + abi_ulong __unused1; + abi_ulong __unused2; +}; + +struct target_shmid_ds { + struct target_ipc_perm shm_perm; /* operation permission struct */ + abi_long shm_segsz; /* size of segment in bytes */ + abi_ulong shm_atime; /* time of last shmat() */ +#if TARGET_ABI_BITS == 32 + abi_ulong __unused1; +#endif + abi_ulong shm_dtime; /* time of last shmdt() */ +#if TARGET_ABI_BITS == 32 + abi_ulong __unused2; +#endif + abi_ulong shm_ctime; /* time of last change by shmctl() */ +#if TARGET_ABI_BITS == 32 + abi_ulong __unused3; +#endif + abi_int shm_cpid; /* pid of creator */ + abi_int shm_lpid; /* pid of last shmop */ + abi_ulong shm_nattch; /* number of current attaches */ + abi_ulong __unused4; + abi_ulong __unused5; +}; + +#endif diff --git a/linux-user/x86_64/target_structs.h b/linux-user/x86_64/target_structs.h new file mode 100644 index 0000000000..d934056149 --- /dev/null +++ b/linux-user/x86_64/target_structs.h @@ -0,0 +1,58 @@ +/* + * X86-64 specific structures for linux-user + * + * Copyright (c) 2013 Fabrice Bellard + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, see <http://www.gnu.org/licenses/>. + */ +#ifndef TARGET_STRUCTS_H +#define TARGET_STRUCTS_H + +struct target_ipc_perm { + abi_int __key; /* Key. */ + abi_uint uid; /* Owner's user ID. */ + abi_uint gid; /* Owner's group ID. */ + abi_uint cuid; /* Creator's user ID. */ + abi_uint cgid; /* Creator's group ID. */ + abi_ushort mode; /* Read/write permission. */ + abi_ushort __pad1; + abi_ushort __seq; /* Sequence number. */ + abi_ushort __pad2; + abi_ulong __unused1; + abi_ulong __unused2; +}; + +struct target_shmid_ds { + struct target_ipc_perm shm_perm; /* operation permission struct */ + abi_long shm_segsz; /* size of segment in bytes */ + abi_ulong shm_atime; /* time of last shmat() */ +#if TARGET_ABI_BITS == 32 + abi_ulong __unused1; +#endif + abi_ulong shm_dtime; /* time of last shmdt() */ +#if TARGET_ABI_BITS == 32 + abi_ulong __unused2; +#endif + abi_ulong shm_ctime; /* time of last change by shmctl() */ +#if TARGET_ABI_BITS == 32 + abi_ulong __unused3; +#endif + abi_int shm_cpid; /* pid of creator */ + abi_int shm_lpid; /* pid of last shmop */ + abi_ulong shm_nattch; /* number of current attaches */ + abi_ulong __unused4; + abi_ulong __unused5; +}; + +#endif diff --git a/net/Makefile.objs b/net/Makefile.objs index 4854a14fe4..c25fe6920c 100644 --- a/net/Makefile.objs +++ b/net/Makefile.objs @@ -11,3 +11,4 @@ common-obj-$(CONFIG_AIX) += tap-aix.o common-obj-$(CONFIG_HAIKU) += tap-haiku.o common-obj-$(CONFIG_SLIRP) += slirp.o common-obj-$(CONFIG_VDE) += vde.o +common-obj-$(CONFIG_NETMAP) += netmap.o diff --git a/net/clients.h b/net/clients.h index 77932942bd..7322ff5f33 100644 --- a/net/clients.h +++ b/net/clients.h @@ -52,4 +52,9 @@ int net_init_vde(const NetClientOptions *opts, const char *name, NetClientState *peer); #endif +#ifdef CONFIG_NETMAP +int net_init_netmap(const NetClientOptions *opts, const char *name, + NetClientState *peer); +#endif + #endif /* QEMU_NET_CLIENTS_H */ @@ -726,6 +726,9 @@ static int (* const net_client_init_fun[NET_CLIENT_OPTIONS_KIND_MAX])( #ifdef CONFIG_VDE [NET_CLIENT_OPTIONS_KIND_VDE] = net_init_vde, #endif +#ifdef CONFIG_NETMAP + [NET_CLIENT_OPTIONS_KIND_NETMAP] = net_init_netmap, +#endif [NET_CLIENT_OPTIONS_KIND_DUMP] = net_init_dump, #ifdef CONFIG_NET_BRIDGE [NET_CLIENT_OPTIONS_KIND_BRIDGE] = net_init_bridge, @@ -757,6 +760,9 @@ static int net_client_init1(const void *object, int is_netdev, Error **errp) #ifdef CONFIG_VDE case NET_CLIENT_OPTIONS_KIND_VDE: #endif +#ifdef CONFIG_NETMAP + case NET_CLIENT_OPTIONS_KIND_NETMAP: +#endif #ifdef CONFIG_NET_BRIDGE case NET_CLIENT_OPTIONS_KIND_BRIDGE: #endif @@ -1065,15 +1071,23 @@ void qmp_set_link(const char *name, bool up, Error **errp) nc->info->link_status_changed(nc); } - /* Notify peer. Don't update peer link status: this makes it possible to - * disconnect from host network without notifying the guest. - * FIXME: is disconnected link status change operation useful? - * - * Current behaviour is compatible with qemu vlans where there could be - * multiple clients that can still communicate with each other in - * disconnected mode. For now maintain this compatibility. */ - if (nc->peer && nc->peer->info->link_status_changed) { - nc->peer->info->link_status_changed(nc->peer); + if (nc->peer) { + /* Change peer link only if the peer is NIC and then notify peer. + * If the peer is a HUBPORT or a backend, we do not change the + * link status. + * + * This behavior is compatible with qemu vlans where there could be + * multiple clients that can still communicate with each other in + * disconnected mode. For now maintain this compatibility. + */ + if (nc->peer->info->type == NET_CLIENT_OPTIONS_KIND_NIC) { + for (i = 0; i < queues; i++) { + ncs[i]->peer->link_down = !up; + } + } + if (nc->peer->info->link_status_changed) { + nc->peer->info->link_status_changed(nc->peer); + } } } diff --git a/net/netmap.c b/net/netmap.c new file mode 100644 index 0000000000..0ccc4976b5 --- /dev/null +++ b/net/netmap.c @@ -0,0 +1,435 @@ +/* + * netmap access for qemu + * + * Copyright (c) 2012-2013 Luigi Rizzo + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + + +#include <sys/ioctl.h> +#include <net/if.h> +#include <sys/mman.h> +#include <stdint.h> +#include <net/netmap.h> +#include <net/netmap_user.h> + +#include "net/net.h" +#include "clients.h" +#include "sysemu/sysemu.h" +#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; + bool read_poll; + bool write_poll; + struct iovec iov[IOV_MAX]; +} NetmapState; + +#define D(format, ...) \ + do { \ + struct timeval __xxts; \ + gettimeofday(&__xxts, NULL); \ + printf("%03d.%06d %s [%d] " format "\n", \ + (int)__xxts.tv_sec % 1000, (int)__xxts.tv_usec, \ + __func__, __LINE__, ##__VA_ARGS__); \ + } while (0) + +/* Rate limited version of "D", lps indicates how many per second */ +#define RD(lps, format, ...) \ + do { \ + static int t0, __cnt; \ + struct timeval __xxts; \ + gettimeofday(&__xxts, NULL); \ + if (t0 != __xxts.tv_sec) { \ + t0 = __xxts.tv_sec; \ + __cnt = 0; \ + } \ + if (__cnt++ < lps) { \ + D(format, ##__VA_ARGS__); \ + } \ + } while (0) + + +#ifndef __FreeBSD__ +#define pkt_copy bcopy +#else +/* A fast copy routine only for multiples of 64 bytes, non overlapped. */ +static inline void +pkt_copy(const void *_src, void *_dst, int l) +{ + const uint64_t *src = _src; + uint64_t *dst = _dst; + if (unlikely(l >= 1024)) { + bcopy(src, dst, l); + return; + } + for (; l > 0; l -= 64) { + *dst++ = *src++; + *dst++ = *src++; + *dst++ = *src++; + *dst++ = *src++; + *dst++ = *src++; + *dst++ = *src++; + *dst++ = *src++; + *dst++ = *src++; + } +} +#endif /* __FreeBSD__ */ + +/* + * Open a netmap device. We assume there is only one queue + * (which is the case for the VALE bridge). + */ +static int netmap_open(NetmapPriv *me) +{ + int fd; + int err; + size_t l; + struct nmreq req; + + me->fd = fd = open(me->fdname, O_RDWR); + if (fd < 0) { + error_report("Unable to open netmap device '%s' (%s)", + me->fdname, strerror(errno)); + return -1; + } + 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_report("Unable to register %s: %s", me->ifname, strerror(errno)); + 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_report("Unable to mmap netmap shared memory: %s", + strerror(errno)); + me->mem = NULL; + goto error; + } + + me->nifp = NETMAP_IF(me->mem, req.nr_offset); + me->tx = NETMAP_TXRING(me->nifp, 0); + me->rx = NETMAP_RXRING(me->nifp, 0); + return 0; + +error: + close(me->fd); + return -1; +} + +/* Tell the event-loop if the netmap backend can send packets + to the frontend. */ +static int netmap_can_send(void *opaque) +{ + NetmapState *s = opaque; + + return qemu_can_send_packet(&s->nc); +} + +static void netmap_send(void *opaque); +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_handler2(s->me.fd, + s->read_poll ? netmap_can_send : NULL, + s->read_poll ? netmap_send : NULL, + s->write_poll ? netmap_writable : NULL, + s); +} + +/* Update the read handler. */ +static void netmap_read_poll(NetmapState *s, bool enable) +{ + if (s->read_poll != enable) { /* Do nothing if not changed. */ + s->read_poll = enable; + netmap_update_fd_handler(s); + } +} + +/* Update the write handler. */ +static void netmap_write_poll(NetmapState *s, bool enable) +{ + if (s->write_poll != enable) { + s->write_poll = enable; + netmap_update_fd_handler(s); + } +} + +static void netmap_poll(NetClientState *nc, bool enable) +{ + NetmapState *s = DO_UPCAST(NetmapState, nc, nc); + + if (s->read_poll != enable || s->write_poll != enable) { + s->read_poll = enable; + s->read_poll = enable; + netmap_update_fd_handler(s); + } +} + +/* + * The fd_write() callback, invoked if the fd is marked as + * writable after a poll. Unregister the handler and flush any + * buffered packets. + */ +static void netmap_writable(void *opaque) +{ + NetmapState *s = opaque; + + netmap_write_poll(s, false); + qemu_flush_queued_packets(&s->nc); +} + +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; + uint32_t i; + uint32_t idx; + uint8_t *dst; + + if (unlikely(!ring)) { + /* Drop. */ + return size; + } + + if (unlikely(size > ring->nr_buf_size)) { + RD(5, "[netmap_receive] drop packet of size %d > %d\n", + (int)size, ring->nr_buf_size); + return size; + } + + if (ring->avail == 0) { + /* No available slots in the netmap TX ring. */ + netmap_write_poll(s, true); + return 0; + } + + i = ring->cur; + idx = ring->slot[i].buf_idx; + dst = (uint8_t *)NETMAP_BUF(ring, idx); + + ring->slot[i].len = size; + ring->slot[i].flags = 0; + pkt_copy(buf, dst, size); + ring->cur = NETMAP_RING_NEXT(ring, i); + ring->avail--; + ioctl(s->me.fd, NIOCTXSYNC, NULL); + + return size; +} + +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; + uint32_t last; + uint32_t idx; + uint8_t *dst; + int j; + uint32_t i; + uint32_t avail; + + if (unlikely(!ring)) { + /* Drop the packet. */ + return iov_size(iov, iovcnt); + } + + i = ring->cur; + avail = ring->avail; + + if (avail < iovcnt) { + /* Not enough netmap slots. */ + netmap_write_poll(s, true); + return 0; + } + + for (j = 0; j < iovcnt; j++) { + int iov_frag_size = iov[j].iov_len; + int offset = 0; + int nm_frag_size; + + /* Split each iovec fragment over more netmap slots, if + necessary. */ + while (iov_frag_size) { + nm_frag_size = MIN(iov_frag_size, ring->nr_buf_size); + + if (unlikely(avail == 0)) { + /* We run out of netmap slots while splitting the + iovec fragments. */ + netmap_write_poll(s, true); + return 0; + } + + idx = ring->slot[i].buf_idx; + dst = (uint8_t *)NETMAP_BUF(ring, idx); + + ring->slot[i].len = nm_frag_size; + ring->slot[i].flags = NS_MOREFRAG; + pkt_copy(iov[j].iov_base + offset, dst, nm_frag_size); + + last = i; + i = NETMAP_RING_NEXT(ring, i); + avail--; + + offset += nm_frag_size; + iov_frag_size -= nm_frag_size; + } + } + /* The last slot must not have NS_MOREFRAG set. */ + ring->slot[last].flags &= ~NS_MOREFRAG; + + /* Now update ring->cur and ring->avail. */ + ring->cur = i; + ring->avail = avail; + + ioctl(s->me.fd, NIOCTXSYNC, NULL); + + return iov_size(iov, iovcnt); +} + +/* Complete a previous send (backend --> guest) and enable the + fd_read callback. */ +static void netmap_send_completed(NetClientState *nc, ssize_t len) +{ + NetmapState *s = DO_UPCAST(NetmapState, nc, nc); + + netmap_read_poll(s, true); +} + +static void netmap_send(void *opaque) +{ + NetmapState *s = opaque; + struct netmap_ring *ring = s->me.rx; + + /* Keep sending while there are available packets into the netmap + RX ring and the forwarding path towards the peer is open. */ + while (ring->avail > 0 && qemu_can_send_packet(&s->nc)) { + uint32_t i; + uint32_t idx; + bool morefrag; + int iovcnt = 0; + int iovsize; + + do { + i = ring->cur; + idx = ring->slot[i].buf_idx; + morefrag = (ring->slot[i].flags & NS_MOREFRAG); + s->iov[iovcnt].iov_base = (u_char *)NETMAP_BUF(ring, idx); + s->iov[iovcnt].iov_len = ring->slot[i].len; + iovcnt++; + + ring->cur = NETMAP_RING_NEXT(ring, i); + ring->avail--; + } while (ring->avail && morefrag); + + if (unlikely(!ring->avail && morefrag)) { + RD(5, "[netmap_send] ran out of slots, with a pending" + "incomplete packet\n"); + } + + iovsize = qemu_sendv_packet_async(&s->nc, s->iov, iovcnt, + netmap_send_completed); + + if (iovsize == 0) { + /* The peer does not receive anymore. Packet is queued, stop + * reading from the backend until netmap_send_completed() + */ + netmap_read_poll(s, false); + break; + } + } +} + +/* Flush and close. */ +static void netmap_cleanup(NetClientState *nc) +{ + NetmapState *s = DO_UPCAST(NetmapState, nc, 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; +} + + +/* NetClientInfo methods */ +static NetClientInfo net_netmap_info = { + .type = NET_CLIENT_OPTIONS_KIND_NETMAP, + .size = sizeof(NetmapState), + .receive = netmap_receive, + .receive_iov = netmap_receive_iov, + .poll = netmap_poll, + .cleanup = netmap_cleanup, +}; + +/* The exported init function + * + * ... -net netmap,ifname="..." + */ +int net_init_netmap(const NetClientOptions *opts, + const char *name, NetClientState *peer) +{ + const NetdevNetmapOptions *netmap_opts = opts->netmap; + NetClientState *nc; + 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); + if (netmap_open(&me)) { + return -1; + } + /* Create the object. */ + nc = qemu_new_net_client(&net_netmap_info, peer, "netmap", name); + s = DO_UPCAST(NetmapState, nc, nc); + s->me = me; + netmap_read_poll(s, true); /* Initially only poll for reads. */ + + return 0; +} + diff --git a/pc-bios/acpi-dsdt.aml b/pc-bios/acpi-dsdt.aml Binary files differindex 528372b41e..cfd16d756e 100644 --- a/pc-bios/acpi-dsdt.aml +++ b/pc-bios/acpi-dsdt.aml diff --git a/pc-bios/bios-256k.bin b/pc-bios/bios-256k.bin Binary files differnew file mode 100644 index 0000000000..68017e59ce --- /dev/null +++ b/pc-bios/bios-256k.bin diff --git a/pc-bios/bios.bin b/pc-bios/bios.bin Binary files differindex 697440c26c..4f4383b06a 100644 --- a/pc-bios/bios.bin +++ b/pc-bios/bios.bin diff --git a/pc-bios/q35-acpi-dsdt.aml b/pc-bios/q35-acpi-dsdt.aml Binary files differindex 4d23746ebf..d71b3a328c 100644 --- a/pc-bios/q35-acpi-dsdt.aml +++ b/pc-bios/q35-acpi-dsdt.aml diff --git a/pc-bios/vgabios-cirrus.bin b/pc-bios/vgabios-cirrus.bin Binary files differindex 424dd0c70c..36b197d190 100644 --- a/pc-bios/vgabios-cirrus.bin +++ b/pc-bios/vgabios-cirrus.bin diff --git a/pc-bios/vgabios-qxl.bin b/pc-bios/vgabios-qxl.bin Binary files differindex 3156c6e3f1..aaa3b10c7c 100644 --- a/pc-bios/vgabios-qxl.bin +++ b/pc-bios/vgabios-qxl.bin diff --git a/pc-bios/vgabios-stdvga.bin b/pc-bios/vgabios-stdvga.bin Binary files differindex 5123c5fd07..d329e240f9 100644 --- a/pc-bios/vgabios-stdvga.bin +++ b/pc-bios/vgabios-stdvga.bin diff --git a/pc-bios/vgabios-vmware.bin b/pc-bios/vgabios-vmware.bin Binary files differindex 5e8c06b228..31d56a94d0 100644 --- a/pc-bios/vgabios-vmware.bin +++ b/pc-bios/vgabios-vmware.bin diff --git a/pc-bios/vgabios.bin b/pc-bios/vgabios.bin Binary files differindex 892a2b5374..b87f74de7c 100644 --- a/pc-bios/vgabios.bin +++ b/pc-bios/vgabios.bin diff --git a/qapi-schema.json b/qapi-schema.json index 8630eb50ea..d6f8615942 100644 --- a/qapi-schema.json +++ b/qapi-schema.json @@ -3009,6 +3009,27 @@ 'hubid': 'int32' } } ## +# @NetdevNetmapOptions +# +# Connect a client to a netmap-enabled NIC or to a VALE switch port +# +# @ifname: Either the name of an existing network interface supported by +# netmap, or the name of a VALE port (created on the fly). +# A VALE port name is in the form 'valeXXX:YYY', where XXX and +# YYY are non-negative integers. XXX identifies a switch and +# YYY identifies a port of the switch. VALE ports having the +# same XXX are therefore connected to the same switch. +# +# @devname: #optional path of the netmap device (default: '/dev/netmap'). +# +# Since 1.8 +## +{ 'type': 'NetdevNetmapOptions', + 'data': { + 'ifname': 'str', + '*devname': 'str' } } + +## # @NetClientOptions # # A discriminated record of network device traits. @@ -3025,7 +3046,8 @@ 'vde': 'NetdevVdeOptions', 'dump': 'NetdevDumpOptions', 'bridge': 'NetdevBridgeOptions', - 'hubport': 'NetdevHubPortOptions' } } + 'hubport': 'NetdevHubPortOptions', + 'netmap': 'NetdevNetmapOptions' } } ## # @NetLegacy diff --git a/qemu-options.hx b/qemu-options.hx index 7f12f02e04..bcfe9eaa3e 100644 --- a/qemu-options.hx +++ b/qemu-options.hx @@ -228,7 +228,6 @@ STEXI Allocate guest RAM from a temporarily created file in @var{path}. ETEXI -#ifdef MAP_POPULATE DEF("mem-prealloc", 0, QEMU_OPTION_mem_prealloc, "-mem-prealloc preallocate guest memory (use with -mem-path)\n", QEMU_ARCH_ALL) @@ -237,7 +236,6 @@ STEXI @findex -mem-prealloc Preallocate memory when using -mem-path. ETEXI -#endif DEF("k", HAS_ARG, QEMU_OPTION_k, "-k language use keyboard layout (for example 'fr' for French)\n", @@ -1409,6 +1407,12 @@ DEF("net", HAS_ARG, QEMU_OPTION_net, " Use group 'groupname' and mode 'octalmode' to change default\n" " ownership and permissions for communication port.\n" #endif +#ifdef CONFIG_NETMAP + "-net netmap,ifname=name[,devname=nmname]\n" + " attach to the existing netmap-enabled network interface 'name', or to a\n" + " VALE port (created on the fly) called 'name' ('nmname' is name of the \n" + " netmap device, defaults to '/dev/netmap')\n" +#endif "-net dump[,vlan=n][,file=f][,len=n]\n" " dump traffic on vlan 'n' to file 'f' (max n bytes per packet)\n" "-net none use it alone to have zero network devices. If no -net option\n" @@ -1423,6 +1427,9 @@ DEF("netdev", HAS_ARG, QEMU_OPTION_netdev, #ifdef CONFIG_VDE "vde|" #endif +#ifdef CONFIG_NETMAP + "netmap|" +#endif "socket|" "hubport],id=str[,option][,option][,...]\n", QEMU_ARCH_ALL) STEXI diff --git a/qemu-seccomp.c b/qemu-seccomp.c index 69cee443af..cf07869599 100644 --- a/qemu-seccomp.c +++ b/qemu-seccomp.c @@ -114,6 +114,7 @@ static const struct QemuSeccompSyscall seccomp_whitelist[] = { { SCMP_SYS(write), 244 }, { SCMP_SYS(fcntl), 243 }, { SCMP_SYS(tgkill), 242 }, + { SCMP_SYS(kill), 242 }, { SCMP_SYS(rt_sigaction), 242 }, { SCMP_SYS(pipe2), 242 }, { SCMP_SYS(munmap), 242 }, diff --git a/qobject/qerror.c b/qobject/qerror.c index 3aee1cf6a6..fc8331aa67 100644 --- a/qobject/qerror.c +++ b/qobject/qerror.c @@ -42,8 +42,8 @@ static QError *qerror_new(void) * * Return strong reference. */ -static QError *qerror_from_info(ErrorClass err_class, const char *fmt, - va_list *va) +static QError * GCC_FMT_ATTR(2, 0) +qerror_from_info(ErrorClass err_class, const char *fmt, va_list *va) { QError *qerr; diff --git a/roms/Makefile b/roms/Makefile index 10d5a65d61..1e04669af4 100644 --- a/roms/Makefile +++ b/roms/Makefile @@ -56,9 +56,10 @@ default: @echo " the EfiRom utility from edk2 / tianocore)" @echo " slof -- update slof.bin" -bios: build-seabios-config-seabios - cp seabios/builds/seabios/bios.bin ../pc-bios/bios.bin - cp seabios/builds/seabios/*dsdt.aml ../pc-bios/ +bios: build-seabios-config-seabios-128k build-seabios-config-seabios-256k + cp seabios/builds/seabios-128k/bios.bin ../pc-bios/bios.bin + cp seabios/builds/seabios-256k/bios.bin ../pc-bios/bios-256k.bin + cp seabios/builds/seabios-256k/src/fw/*dsdt.aml ../pc-bios/ seavgabios: $(patsubst %,seavgabios-%,$(vgabios_variants)) @@ -72,9 +73,11 @@ build-seabios-config-%: config.% mkdir -p seabios/builds/$* cp $< seabios/builds/$*/.config $(MAKE) $(MAKEFLAGS) -C seabios \ + CROSS_COMPILE=$(x86_64_cross_prefix) \ KCONFIG_CONFIG=$(CURDIR)/seabios/builds/$*/.config \ OUT=$(CURDIR)/seabios/builds/$*/ oldnoconfig $(MAKE) $(MAKEFLAGS) -C seabios \ + CROSS_COMPILE=$(x86_64_cross_prefix) \ KCONFIG_CONFIG=$(CURDIR)/seabios/builds/$*/.config \ OUT=$(CURDIR)/seabios/builds/$*/ all diff --git a/roms/config.seabios b/roms/config.seabios deleted file mode 100644 index c373b87a98..0000000000 --- a/roms/config.seabios +++ /dev/null @@ -1 +0,0 @@ -# empty, default config works for us diff --git a/roms/config.seabios-128k b/roms/config.seabios-128k new file mode 100644 index 0000000000..41f83816fc --- /dev/null +++ b/roms/config.seabios-128k @@ -0,0 +1,6 @@ +# for qemu machine types 1.7 + older +# need to turn off features (xhci) to make it fit into 128k +CONFIG_QEMU=y +CONFIG_ROM_SIZE=128 +CONFIG_XEN=n +CONFIG_USB_XHCI=n diff --git a/roms/config.seabios-256k b/roms/config.seabios-256k new file mode 100644 index 0000000000..65e5015c2f --- /dev/null +++ b/roms/config.seabios-256k @@ -0,0 +1,3 @@ +# for qemu machine types 2.0 + newer +CONFIG_QEMU=y +CONFIG_ROM_SIZE=256 diff --git a/roms/seabios b/roms/seabios -Subproject ece025f5980bae88fa677bc9c0d24d2e580e205 +Subproject 31b8b4eea9d9ad58a73b22a6060d3ac1c419c26 diff --git a/target-alpha/cpu-qom.h b/target-alpha/cpu-qom.h index 2ebc9bcacb..198f1b13a3 100644 --- a/target-alpha/cpu-qom.h +++ b/target-alpha/cpu-qom.h @@ -62,7 +62,7 @@ typedef struct AlphaCPU { CPUAlphaState env; /* This alarm doesn't exist in real hardware; we wish it did. */ - struct QEMUTimer *alarm_timer; + QEMUTimer *alarm_timer; } AlphaCPU; static inline AlphaCPU *alpha_env_get_cpu(CPUAlphaState *env) diff --git a/target-arm/cpu-qom.h b/target-arm/cpu-qom.h index b55306a3c3..f32178a9db 100644 --- a/target-arm/cpu-qom.h +++ b/target-arm/cpu-qom.h @@ -91,6 +91,17 @@ typedef struct ARMCPU { /* GPIO outputs for generic timer */ qemu_irq gt_timer_outputs[NUM_GTIMERS]; + /* 'compatible' string for this CPU for Linux device trees */ + const char *dtb_compatible; + + /* Should CPU start in PSCI powered-off state? */ + bool start_powered_off; + + /* [QEMU_]KVM_ARM_TARGET_* constant for this CPU, or + * QEMU_KVM_ARM_TARGET_NONE if the kernel doesn't support this CPU type. + */ + uint32_t kvm_target; + /* The instance init functions for implementation-specific subclasses * set these fields to specify the implementation-dependent values of * various constant registers and reset values of non-constant diff --git a/target-arm/cpu.c b/target-arm/cpu.c index d40f2a7a4f..0635e78ec2 100644 --- a/target-arm/cpu.c +++ b/target-arm/cpu.c @@ -20,6 +20,7 @@ #include "cpu.h" #include "qemu-common.h" +#include "hw/qdev-properties.h" #if !defined(CONFIG_USER_ONLY) #include "hw/loader.h" #endif @@ -217,6 +218,13 @@ static void arm_cpu_initfn(Object *obj) ARRAY_SIZE(cpu->gt_timer_outputs)); #endif + /* DTB consumers generally don't in fact care what the 'compatible' + * string is, so always provide some string and trust that a hypothetical + * picky DTB consumer will also provide a helpful error message. + */ + cpu->dtb_compatible = "qemu,unknown"; + cpu->kvm_target = QEMU_KVM_ARM_TARGET_NONE; + if (tcg_enabled() && !inited) { inited = true; arm_translate_init(); @@ -318,6 +326,8 @@ static ObjectClass *arm_cpu_class_by_name(const char *cpu_model) static void arm926_initfn(Object *obj) { ARMCPU *cpu = ARM_CPU(obj); + + cpu->dtb_compatible = "arm,arm926"; set_feature(&cpu->env, ARM_FEATURE_V5); set_feature(&cpu->env, ARM_FEATURE_VFP); set_feature(&cpu->env, ARM_FEATURE_DUMMY_C15_REGS); @@ -331,6 +341,8 @@ static void arm926_initfn(Object *obj) static void arm946_initfn(Object *obj) { ARMCPU *cpu = ARM_CPU(obj); + + cpu->dtb_compatible = "arm,arm946"; set_feature(&cpu->env, ARM_FEATURE_V5); set_feature(&cpu->env, ARM_FEATURE_MPU); set_feature(&cpu->env, ARM_FEATURE_DUMMY_C15_REGS); @@ -342,6 +354,8 @@ static void arm946_initfn(Object *obj) static void arm1026_initfn(Object *obj) { ARMCPU *cpu = ARM_CPU(obj); + + cpu->dtb_compatible = "arm,arm1026"; set_feature(&cpu->env, ARM_FEATURE_V5); set_feature(&cpu->env, ARM_FEATURE_VFP); set_feature(&cpu->env, ARM_FEATURE_AUXCR); @@ -374,6 +388,8 @@ static void arm1136_r2_initfn(Object *obj) * for 1136_r2 (in particular r0p2 does not actually implement most * of the ID registers). */ + + cpu->dtb_compatible = "arm,arm1136"; set_feature(&cpu->env, ARM_FEATURE_V6); set_feature(&cpu->env, ARM_FEATURE_VFP); set_feature(&cpu->env, ARM_FEATURE_DUMMY_C15_REGS); @@ -403,6 +419,8 @@ static void arm1136_r2_initfn(Object *obj) static void arm1136_initfn(Object *obj) { ARMCPU *cpu = ARM_CPU(obj); + + cpu->dtb_compatible = "arm,arm1136"; set_feature(&cpu->env, ARM_FEATURE_V6K); set_feature(&cpu->env, ARM_FEATURE_V6); set_feature(&cpu->env, ARM_FEATURE_VFP); @@ -433,6 +451,8 @@ static void arm1136_initfn(Object *obj) static void arm1176_initfn(Object *obj) { ARMCPU *cpu = ARM_CPU(obj); + + cpu->dtb_compatible = "arm,arm1176"; set_feature(&cpu->env, ARM_FEATURE_V6K); set_feature(&cpu->env, ARM_FEATURE_VFP); set_feature(&cpu->env, ARM_FEATURE_VAPA); @@ -463,6 +483,8 @@ static void arm1176_initfn(Object *obj) static void arm11mpcore_initfn(Object *obj) { ARMCPU *cpu = ARM_CPU(obj); + + cpu->dtb_compatible = "arm,arm11mpcore"; set_feature(&cpu->env, ARM_FEATURE_V6K); set_feature(&cpu->env, ARM_FEATURE_VFP); set_feature(&cpu->env, ARM_FEATURE_VAPA); @@ -516,6 +538,8 @@ static const ARMCPRegInfo cortexa8_cp_reginfo[] = { static void cortex_a8_initfn(Object *obj) { ARMCPU *cpu = ARM_CPU(obj); + + cpu->dtb_compatible = "arm,cortex-a8"; set_feature(&cpu->env, ARM_FEATURE_V7); set_feature(&cpu->env, ARM_FEATURE_VFP3); set_feature(&cpu->env, ARM_FEATURE_NEON); @@ -580,6 +604,8 @@ static const ARMCPRegInfo cortexa9_cp_reginfo[] = { static void cortex_a9_initfn(Object *obj) { ARMCPU *cpu = ARM_CPU(obj); + + cpu->dtb_compatible = "arm,cortex-a9"; set_feature(&cpu->env, ARM_FEATURE_V7); set_feature(&cpu->env, ARM_FEATURE_VFP3); set_feature(&cpu->env, ARM_FEATURE_VFP_FP16); @@ -649,6 +675,8 @@ static const ARMCPRegInfo cortexa15_cp_reginfo[] = { static void cortex_a15_initfn(Object *obj) { ARMCPU *cpu = ARM_CPU(obj); + + cpu->dtb_compatible = "arm,cortex-a15"; set_feature(&cpu->env, ARM_FEATURE_V7); set_feature(&cpu->env, ARM_FEATURE_VFP4); set_feature(&cpu->env, ARM_FEATURE_VFP_FP16); @@ -658,6 +686,7 @@ static void cortex_a15_initfn(Object *obj) set_feature(&cpu->env, ARM_FEATURE_GENERIC_TIMER); set_feature(&cpu->env, ARM_FEATURE_DUMMY_C15_REGS); set_feature(&cpu->env, ARM_FEATURE_LPAE); + cpu->kvm_target = QEMU_KVM_ARM_TARGET_CORTEX_A15; cpu->midr = 0x412fc0f1; cpu->reset_fpsid = 0x410430f0; cpu->mvfr0 = 0x10110222; @@ -697,6 +726,8 @@ static void ti925t_initfn(Object *obj) static void sa1100_initfn(Object *obj) { ARMCPU *cpu = ARM_CPU(obj); + + cpu->dtb_compatible = "intel,sa1100"; set_feature(&cpu->env, ARM_FEATURE_STRONGARM); set_feature(&cpu->env, ARM_FEATURE_DUMMY_C15_REGS); cpu->midr = 0x4401A11B; @@ -715,6 +746,8 @@ static void sa1110_initfn(Object *obj) static void pxa250_initfn(Object *obj) { ARMCPU *cpu = ARM_CPU(obj); + + cpu->dtb_compatible = "marvell,xscale"; set_feature(&cpu->env, ARM_FEATURE_V5); set_feature(&cpu->env, ARM_FEATURE_XSCALE); cpu->midr = 0x69052100; @@ -725,6 +758,8 @@ static void pxa250_initfn(Object *obj) static void pxa255_initfn(Object *obj) { ARMCPU *cpu = ARM_CPU(obj); + + cpu->dtb_compatible = "marvell,xscale"; set_feature(&cpu->env, ARM_FEATURE_V5); set_feature(&cpu->env, ARM_FEATURE_XSCALE); cpu->midr = 0x69052d00; @@ -735,6 +770,8 @@ static void pxa255_initfn(Object *obj) static void pxa260_initfn(Object *obj) { ARMCPU *cpu = ARM_CPU(obj); + + cpu->dtb_compatible = "marvell,xscale"; set_feature(&cpu->env, ARM_FEATURE_V5); set_feature(&cpu->env, ARM_FEATURE_XSCALE); cpu->midr = 0x69052903; @@ -745,6 +782,8 @@ static void pxa260_initfn(Object *obj) static void pxa261_initfn(Object *obj) { ARMCPU *cpu = ARM_CPU(obj); + + cpu->dtb_compatible = "marvell,xscale"; set_feature(&cpu->env, ARM_FEATURE_V5); set_feature(&cpu->env, ARM_FEATURE_XSCALE); cpu->midr = 0x69052d05; @@ -755,6 +794,8 @@ static void pxa261_initfn(Object *obj) static void pxa262_initfn(Object *obj) { ARMCPU *cpu = ARM_CPU(obj); + + cpu->dtb_compatible = "marvell,xscale"; set_feature(&cpu->env, ARM_FEATURE_V5); set_feature(&cpu->env, ARM_FEATURE_XSCALE); cpu->midr = 0x69052d06; @@ -765,6 +806,8 @@ static void pxa262_initfn(Object *obj) static void pxa270a0_initfn(Object *obj) { ARMCPU *cpu = ARM_CPU(obj); + + cpu->dtb_compatible = "marvell,xscale"; set_feature(&cpu->env, ARM_FEATURE_V5); set_feature(&cpu->env, ARM_FEATURE_XSCALE); set_feature(&cpu->env, ARM_FEATURE_IWMMXT); @@ -776,6 +819,8 @@ static void pxa270a0_initfn(Object *obj) static void pxa270a1_initfn(Object *obj) { ARMCPU *cpu = ARM_CPU(obj); + + cpu->dtb_compatible = "marvell,xscale"; set_feature(&cpu->env, ARM_FEATURE_V5); set_feature(&cpu->env, ARM_FEATURE_XSCALE); set_feature(&cpu->env, ARM_FEATURE_IWMMXT); @@ -787,6 +832,8 @@ static void pxa270a1_initfn(Object *obj) static void pxa270b0_initfn(Object *obj) { ARMCPU *cpu = ARM_CPU(obj); + + cpu->dtb_compatible = "marvell,xscale"; set_feature(&cpu->env, ARM_FEATURE_V5); set_feature(&cpu->env, ARM_FEATURE_XSCALE); set_feature(&cpu->env, ARM_FEATURE_IWMMXT); @@ -798,6 +845,8 @@ static void pxa270b0_initfn(Object *obj) static void pxa270b1_initfn(Object *obj) { ARMCPU *cpu = ARM_CPU(obj); + + cpu->dtb_compatible = "marvell,xscale"; set_feature(&cpu->env, ARM_FEATURE_V5); set_feature(&cpu->env, ARM_FEATURE_XSCALE); set_feature(&cpu->env, ARM_FEATURE_IWMMXT); @@ -809,6 +858,8 @@ static void pxa270b1_initfn(Object *obj) static void pxa270c0_initfn(Object *obj) { ARMCPU *cpu = ARM_CPU(obj); + + cpu->dtb_compatible = "marvell,xscale"; set_feature(&cpu->env, ARM_FEATURE_V5); set_feature(&cpu->env, ARM_FEATURE_XSCALE); set_feature(&cpu->env, ARM_FEATURE_IWMMXT); @@ -820,6 +871,8 @@ static void pxa270c0_initfn(Object *obj) static void pxa270c5_initfn(Object *obj) { ARMCPU *cpu = ARM_CPU(obj); + + cpu->dtb_compatible = "marvell,xscale"; set_feature(&cpu->env, ARM_FEATURE_V5); set_feature(&cpu->env, ARM_FEATURE_XSCALE); set_feature(&cpu->env, ARM_FEATURE_IWMMXT); @@ -894,6 +947,11 @@ static const ARMCPUInfo arm_cpus[] = { #endif }; +static Property arm_cpu_properties[] = { + DEFINE_PROP_BOOL("start-powered-off", ARMCPU, start_powered_off, false), + DEFINE_PROP_END_OF_LIST() +}; + static void arm_cpu_class_init(ObjectClass *oc, void *data) { ARMCPUClass *acc = ARM_CPU_CLASS(oc); @@ -902,6 +960,7 @@ static void arm_cpu_class_init(ObjectClass *oc, void *data) acc->parent_realize = dc->realize; dc->realize = arm_cpu_realizefn; + dc->props = arm_cpu_properties; acc->parent_reset = cc->reset; cc->reset = arm_cpu_reset; diff --git a/target-arm/cpu.h b/target-arm/cpu.h index 9f110f15b6..c3f007fc53 100644 --- a/target-arm/cpu.h +++ b/target-arm/cpu.h @@ -21,6 +21,8 @@ #include "config.h" +#include "kvm-consts.h" + #if defined(TARGET_AARCH64) /* AArch64 definitions */ # define TARGET_LONG_BITS 64 @@ -497,17 +499,6 @@ void armv7m_nvic_complete_irq(void *opaque, int irq); (((cp) << 16) | ((is64) << 15) | ((crn) << 11) | \ ((crm) << 7) | ((opc1) << 3) | (opc2)) -/* Note that these must line up with the KVM/ARM register - * ID field definitions (kvm.c will check this, but we - * can't just use the KVM defines here as the kvm headers - * are unavailable to non-KVM-specific files) - */ -#define CP_REG_SIZE_SHIFT 52 -#define CP_REG_SIZE_MASK 0x00f0000000000000ULL -#define CP_REG_SIZE_U32 0x0020000000000000ULL -#define CP_REG_SIZE_U64 0x0030000000000000ULL -#define CP_REG_ARM 0x4000000000000000ULL - /* Convert a full 64 bit KVM register ID to the truncated 32 bit * version used as a key for the coprocessor register hashtable */ diff --git a/target-arm/helper.c b/target-arm/helper.c index 3445813465..5e5e5aad2b 100644 --- a/target-arm/helper.c +++ b/target-arm/helper.c @@ -1173,7 +1173,7 @@ static int vmsa_ttbcr_raw_write(CPUARMState *env, const ARMCPRegInfo *ri, { int maskshift = extract32(value, 0, 3); - if (arm_feature(env, ARM_FEATURE_LPAE)) { + if (arm_feature(env, ARM_FEATURE_LPAE) && (value & (1 << 31))) { value &= ~((7 << 19) | (3 << 14) | (0xf << 3)); } else { value &= 7; @@ -1842,6 +1842,12 @@ void arm_cpu_list(FILE *f, fprintf_function cpu_fprintf) (*cpu_fprintf)(f, "Available CPUs:\n"); g_slist_foreach(list, arm_cpu_list_entry, &s); g_slist_free(list); +#ifdef CONFIG_KVM + /* The 'host' CPU type is dynamically registered only if KVM is + * enabled, so we have to special-case it here: + */ + (*cpu_fprintf)(f, " host (only available in KVM mode)\n"); +#endif } static void arm_cpu_add_definition(gpointer data, gpointer user_data) @@ -4079,3 +4085,28 @@ float64 VFP_HELPER(muladd, d)(float64 a, float64 b, float64 c, void *fpstp) float_status *fpst = fpstp; return float64_muladd(a, b, c, 0, fpst); } + +/* ARMv8 VMAXNM/VMINNM */ +float32 VFP_HELPER(maxnm, s)(float32 a, float32 b, void *fpstp) +{ + float_status *fpst = fpstp; + return float32_maxnum(a, b, fpst); +} + +float64 VFP_HELPER(maxnm, d)(float64 a, float64 b, void *fpstp) +{ + float_status *fpst = fpstp; + return float64_maxnum(a, b, fpst); +} + +float32 VFP_HELPER(minnm, s)(float32 a, float32 b, void *fpstp) +{ + float_status *fpst = fpstp; + return float32_minnum(a, b, fpst); +} + +float64 VFP_HELPER(minnm, d)(float64 a, float64 b, void *fpstp) +{ + float_status *fpst = fpstp; + return float64_minnum(a, b, fpst); +} diff --git a/target-arm/helper.h b/target-arm/helper.h index cac9564f5f..d459a39e46 100644 --- a/target-arm/helper.h +++ b/target-arm/helper.h @@ -132,6 +132,11 @@ DEF_HELPER_2(neon_fcvt_f32_to_f16, i32, f32, env) DEF_HELPER_4(vfp_muladdd, f64, f64, f64, f64, ptr) DEF_HELPER_4(vfp_muladds, f32, f32, f32, f32, ptr) +DEF_HELPER_3(vfp_maxnmd, f64, f64, f64, ptr) +DEF_HELPER_3(vfp_maxnms, f32, f32, f32, ptr) +DEF_HELPER_3(vfp_minnmd, f64, f64, f64, ptr) +DEF_HELPER_3(vfp_minnms, f32, f32, f32, ptr) + DEF_HELPER_3(recps_f32, f32, f32, f32, env) DEF_HELPER_3(rsqrts_f32, f32, f32, f32, env) DEF_HELPER_2(recpe_f32, f32, f32, env) diff --git a/target-arm/kvm-consts.h b/target-arm/kvm-consts.h new file mode 100644 index 0000000000..2bba0bd198 --- /dev/null +++ b/target-arm/kvm-consts.h @@ -0,0 +1,64 @@ +/* + * KVM ARM ABI constant definitions + * + * Copyright (c) 2013 Linaro Limited + * + * Provide versions of KVM constant defines that can be used even + * when CONFIG_KVM is not set and we don't have access to the + * KVM headers. If CONFIG_KVM is set, we do a compile-time check + * that we haven't got out of sync somehow. + * + * This work is licensed under the terms of the GNU GPL, version 2 or later. + * See the COPYING file in the top-level directory. + */ +#ifndef ARM_KVM_CONSTS_H +#define ARM_KVM_CONSTS_H + +#ifdef CONFIG_KVM +#include "qemu/compiler.h" +#include <linux/kvm.h> + +#define MISMATCH_CHECK(X, Y) QEMU_BUILD_BUG_ON(X != Y) + +#else +#define MISMATCH_CHECK(X, Y) +#endif + +#define CP_REG_SIZE_SHIFT 52 +#define CP_REG_SIZE_MASK 0x00f0000000000000ULL +#define CP_REG_SIZE_U32 0x0020000000000000ULL +#define CP_REG_SIZE_U64 0x0030000000000000ULL +#define CP_REG_ARM 0x4000000000000000ULL + +MISMATCH_CHECK(CP_REG_SIZE_SHIFT, KVM_REG_SIZE_SHIFT) +MISMATCH_CHECK(CP_REG_SIZE_MASK, KVM_REG_SIZE_MASK) +MISMATCH_CHECK(CP_REG_SIZE_U32, KVM_REG_SIZE_U32) +MISMATCH_CHECK(CP_REG_SIZE_U64, KVM_REG_SIZE_U64) +MISMATCH_CHECK(CP_REG_ARM, KVM_REG_ARM) + +#define PSCI_FN_BASE 0x95c1ba5e +#define PSCI_FN(n) (PSCI_FN_BASE + (n)) +#define PSCI_FN_CPU_SUSPEND PSCI_FN(0) +#define PSCI_FN_CPU_OFF PSCI_FN(1) +#define PSCI_FN_CPU_ON PSCI_FN(2) +#define PSCI_FN_MIGRATE PSCI_FN(3) + +MISMATCH_CHECK(PSCI_FN_CPU_SUSPEND, KVM_PSCI_FN_CPU_SUSPEND) +MISMATCH_CHECK(PSCI_FN_CPU_OFF, KVM_PSCI_FN_CPU_OFF) +MISMATCH_CHECK(PSCI_FN_CPU_ON, KVM_PSCI_FN_CPU_ON) +MISMATCH_CHECK(PSCI_FN_MIGRATE, KVM_PSCI_FN_MIGRATE) + +#define QEMU_KVM_ARM_TARGET_CORTEX_A15 0 + +/* There's no kernel define for this: sentinel value which + * matches no KVM target value for either 64 or 32 bit + */ +#define QEMU_KVM_ARM_TARGET_NONE UINT_MAX + +#ifndef TARGET_AARCH64 +MISMATCH_CHECK(QEMU_KVM_ARM_TARGET_CORTEX_A15, KVM_ARM_TARGET_CORTEX_A15) +#endif + +#undef MISMATCH_CHECK + +#endif diff --git a/target-arm/kvm.c b/target-arm/kvm.c index 6e5cd36fae..f865dac871 100644 --- a/target-arm/kvm.c +++ b/target-arm/kvm.c @@ -23,25 +23,240 @@ #include "cpu.h" #include "hw/arm/arm.h" -/* Check that cpu.h's idea of coprocessor fields matches KVM's */ -#if (CP_REG_SIZE_SHIFT != KVM_REG_SIZE_SHIFT) || \ - (CP_REG_SIZE_MASK != KVM_REG_SIZE_MASK) || \ - (CP_REG_SIZE_U32 != KVM_REG_SIZE_U32) || \ - (CP_REG_SIZE_U64 != KVM_REG_SIZE_U64) || \ - (CP_REG_ARM != KVM_REG_ARM) -#error mismatch between cpu.h and KVM header definitions -#endif - const KVMCapabilityInfo kvm_arch_required_capabilities[] = { KVM_CAP_LAST_INFO }; +bool kvm_arm_create_scratch_host_vcpu(const uint32_t *cpus_to_try, + int *fdarray, + struct kvm_vcpu_init *init) +{ + int ret, kvmfd = -1, vmfd = -1, cpufd = -1; + + kvmfd = qemu_open("/dev/kvm", O_RDWR); + if (kvmfd < 0) { + goto err; + } + vmfd = ioctl(kvmfd, KVM_CREATE_VM, 0); + if (vmfd < 0) { + goto err; + } + cpufd = ioctl(vmfd, KVM_CREATE_VCPU, 0); + if (cpufd < 0) { + goto err; + } + + ret = ioctl(vmfd, KVM_ARM_PREFERRED_TARGET, init); + if (ret >= 0) { + ret = ioctl(cpufd, KVM_ARM_VCPU_INIT, init); + if (ret < 0) { + goto err; + } + } else { + /* Old kernel which doesn't know about the + * PREFERRED_TARGET ioctl: we know it will only support + * creating one kind of guest CPU which is its preferred + * CPU type. + */ + while (*cpus_to_try != QEMU_KVM_ARM_TARGET_NONE) { + init->target = *cpus_to_try++; + memset(init->features, 0, sizeof(init->features)); + ret = ioctl(cpufd, KVM_ARM_VCPU_INIT, init); + if (ret >= 0) { + break; + } + } + if (ret < 0) { + goto err; + } + } + + fdarray[0] = kvmfd; + fdarray[1] = vmfd; + fdarray[2] = cpufd; + + return true; + +err: + if (cpufd >= 0) { + close(cpufd); + } + if (vmfd >= 0) { + close(vmfd); + } + if (kvmfd >= 0) { + close(kvmfd); + } + + return false; +} + +void kvm_arm_destroy_scratch_host_vcpu(int *fdarray) +{ + int i; + + for (i = 2; i >= 0; i--) { + close(fdarray[i]); + } +} + +static inline void set_feature(uint64_t *features, int feature) +{ + *features |= 1ULL << feature; +} + +bool kvm_arm_get_host_cpu_features(ARMHostCPUClass *ahcc) +{ + /* Identify the feature bits corresponding to the host CPU, and + * fill out the ARMHostCPUClass fields accordingly. To do this + * we have to create a scratch VM, create a single CPU inside it, + * and then query that CPU for the relevant ID registers. + */ + int i, ret, fdarray[3]; + uint32_t midr, id_pfr0, id_isar0, mvfr1; + uint64_t features = 0; + /* Old kernels may not know about the PREFERRED_TARGET ioctl: however + * we know these will only support creating one kind of guest CPU, + * which is its preferred CPU type. + */ + static const uint32_t cpus_to_try[] = { + QEMU_KVM_ARM_TARGET_CORTEX_A15, + QEMU_KVM_ARM_TARGET_NONE + }; + struct kvm_vcpu_init init; + struct kvm_one_reg idregs[] = { + { + .id = KVM_REG_ARM | KVM_REG_SIZE_U32 + | ENCODE_CP_REG(15, 0, 0, 0, 0, 0), + .addr = (uintptr_t)&midr, + }, + { + .id = KVM_REG_ARM | KVM_REG_SIZE_U32 + | ENCODE_CP_REG(15, 0, 0, 1, 0, 0), + .addr = (uintptr_t)&id_pfr0, + }, + { + .id = KVM_REG_ARM | KVM_REG_SIZE_U32 + | ENCODE_CP_REG(15, 0, 0, 2, 0, 0), + .addr = (uintptr_t)&id_isar0, + }, + { + .id = KVM_REG_ARM | KVM_REG_SIZE_U32 + | KVM_REG_ARM_VFP | KVM_REG_ARM_VFP_MVFR1, + .addr = (uintptr_t)&mvfr1, + }, + }; + + if (!kvm_arm_create_scratch_host_vcpu(cpus_to_try, fdarray, &init)) { + return false; + } + + ahcc->target = init.target; + + /* This is not strictly blessed by the device tree binding docs yet, + * but in practice the kernel does not care about this string so + * there is no point maintaining an KVM_ARM_TARGET_* -> string table. + */ + ahcc->dtb_compatible = "arm,arm-v7"; + + for (i = 0; i < ARRAY_SIZE(idregs); i++) { + ret = ioctl(fdarray[2], KVM_GET_ONE_REG, &idregs[i]); + if (ret) { + break; + } + } + + kvm_arm_destroy_scratch_host_vcpu(fdarray); + + if (ret) { + return false; + } + + /* Now we've retrieved all the register information we can + * set the feature bits based on the ID register fields. + * We can assume any KVM supporting CPU is at least a v7 + * with VFPv3, LPAE and the generic timers; this in turn implies + * most of the other feature bits, but a few must be tested. + */ + set_feature(&features, ARM_FEATURE_V7); + set_feature(&features, ARM_FEATURE_VFP3); + set_feature(&features, ARM_FEATURE_LPAE); + set_feature(&features, ARM_FEATURE_GENERIC_TIMER); + + switch (extract32(id_isar0, 24, 4)) { + case 1: + set_feature(&features, ARM_FEATURE_THUMB_DIV); + break; + case 2: + set_feature(&features, ARM_FEATURE_ARM_DIV); + set_feature(&features, ARM_FEATURE_THUMB_DIV); + break; + default: + break; + } + + if (extract32(id_pfr0, 12, 4) == 1) { + set_feature(&features, ARM_FEATURE_THUMB2EE); + } + if (extract32(mvfr1, 20, 4) == 1) { + set_feature(&features, ARM_FEATURE_VFP_FP16); + } + if (extract32(mvfr1, 12, 4) == 1) { + set_feature(&features, ARM_FEATURE_NEON); + } + if (extract32(mvfr1, 28, 4) == 1) { + /* FMAC support implies VFPv4 */ + set_feature(&features, ARM_FEATURE_VFP4); + } + + ahcc->features = features; + + return true; +} + +static void kvm_arm_host_cpu_class_init(ObjectClass *oc, void *data) +{ + ARMHostCPUClass *ahcc = ARM_HOST_CPU_CLASS(oc); + + /* All we really need to set up for the 'host' CPU + * is the feature bits -- we rely on the fact that the + * various ID register values in ARMCPU are only used for + * TCG CPUs. + */ + if (!kvm_arm_get_host_cpu_features(ahcc)) { + fprintf(stderr, "Failed to retrieve host CPU features!\n"); + abort(); + } +} + +static void kvm_arm_host_cpu_initfn(Object *obj) +{ + ARMHostCPUClass *ahcc = ARM_HOST_CPU_GET_CLASS(obj); + ARMCPU *cpu = ARM_CPU(obj); + CPUARMState *env = &cpu->env; + + cpu->kvm_target = ahcc->target; + cpu->dtb_compatible = ahcc->dtb_compatible; + env->features = ahcc->features; +} + +static const TypeInfo host_arm_cpu_type_info = { + .name = TYPE_ARM_HOST_CPU, + .parent = TYPE_ARM_CPU, + .instance_init = kvm_arm_host_cpu_initfn, + .class_init = kvm_arm_host_cpu_class_init, + .class_size = sizeof(ARMHostCPUClass), +}; + int kvm_arch_init(KVMState *s) { /* For ARM interrupt delivery is always asynchronous, * whether we are using an in-kernel VGIC or not. */ kvm_async_interrupts_allowed = true; + + type_register_static(&host_arm_cpu_type_info); + return 0; } @@ -86,8 +301,16 @@ int kvm_arch_init_vcpu(CPUState *cs) struct kvm_reg_list *rlp; ARMCPU *cpu = ARM_CPU(cs); - init.target = KVM_ARM_TARGET_CORTEX_A15; + if (cpu->kvm_target == QEMU_KVM_ARM_TARGET_NONE) { + fprintf(stderr, "KVM is not supported for this guest CPU type\n"); + return -EINVAL; + } + + init.target = cpu->kvm_target; memset(init.features, 0, sizeof(init.features)); + if (cpu->start_powered_off) { + init.features[0] = 1 << KVM_ARM_VCPU_POWER_OFF; + } ret = kvm_vcpu_ioctl(cs, KVM_ARM_VCPU_INIT, &init); if (ret) { return ret; diff --git a/target-arm/kvm_arm.h b/target-arm/kvm_arm.h index 5d14887e66..cd3d13ca2d 100644 --- a/target-arm/kvm_arm.h +++ b/target-arm/kvm_arm.h @@ -62,4 +62,59 @@ bool write_list_to_kvmstate(ARMCPU *cpu); */ bool write_kvmstate_to_list(ARMCPU *cpu); +#ifdef CONFIG_KVM +/** + * kvm_arm_create_scratch_host_vcpu: + * @cpus_to_try: array of QEMU_KVM_ARM_TARGET_* values (terminated with + * QEMU_KVM_ARM_TARGET_NONE) to try as fallback if the kernel does not + * know the PREFERRED_TARGET ioctl + * @fdarray: filled in with kvmfd, vmfd, cpufd file descriptors in that order + * @init: filled in with the necessary values for creating a host vcpu + * + * Create a scratch vcpu in its own VM of the type preferred by the host + * kernel (as would be used for '-cpu host'), for purposes of probing it + * for capabilities. + * + * Returns: true on success (and fdarray and init are filled in), + * false on failure (and fdarray and init are not valid). + */ +bool kvm_arm_create_scratch_host_vcpu(const uint32_t *cpus_to_try, + int *fdarray, + struct kvm_vcpu_init *init); + +/** + * kvm_arm_destroy_scratch_host_vcpu: + * @fdarray: array of fds as set up by kvm_arm_create_scratch_host_vcpu + * + * Tear down the scratch vcpu created by kvm_arm_create_scratch_host_vcpu. + */ +void kvm_arm_destroy_scratch_host_vcpu(int *fdarray); + +#define TYPE_ARM_HOST_CPU "host-" TYPE_ARM_CPU +#define ARM_HOST_CPU_CLASS(klass) \ + OBJECT_CLASS_CHECK(ARMHostCPUClass, (klass), TYPE_ARM_HOST_CPU) +#define ARM_HOST_CPU_GET_CLASS(obj) \ + OBJECT_GET_CLASS(ARMHostCPUClass, (obj), TYPE_ARM_HOST_CPU) + +typedef struct ARMHostCPUClass { + /*< private >*/ + ARMCPUClass parent_class; + /*< public >*/ + + uint64_t features; + uint32_t target; + const char *dtb_compatible; +} ARMHostCPUClass; + +/** + * kvm_arm_get_host_cpu_features: + * @ahcc: ARMHostCPUClass to fill in + * + * Probe the capabilities of the host kernel's preferred CPU and fill + * in the ARMHostCPUClass struct accordingly. + */ +bool kvm_arm_get_host_cpu_features(ARMHostCPUClass *ahcc); + +#endif + #endif diff --git a/target-arm/translate.c b/target-arm/translate.c index 5f003e785e..8c479ff9a8 100644 --- a/target-arm/translate.c +++ b/target-arm/translate.c @@ -825,63 +825,57 @@ static inline void store_reg_from_load(CPUARMState *env, DisasContext *s, * extended if we're a 64 bit core) and data is also * 32 bits unless specifically doing a 64 bit access. * These functions work like tcg_gen_qemu_{ld,st}* except - * that their arguments are TCGv_i32 rather than TCGv. + * that the address argument is TCGv_i32 rather than TCGv. */ #if TARGET_LONG_BITS == 32 -#define DO_GEN_LD(OP) \ -static inline void gen_aa32_##OP(TCGv_i32 val, TCGv_i32 addr, int index) \ +#define DO_GEN_LD(SUFF, OPC) \ +static inline void gen_aa32_ld##SUFF(TCGv_i32 val, TCGv_i32 addr, int index) \ { \ - tcg_gen_qemu_##OP(val, addr, index); \ + tcg_gen_qemu_ld_i32(val, addr, index, OPC); \ } -#define DO_GEN_ST(OP) \ -static inline void gen_aa32_##OP(TCGv_i32 val, TCGv_i32 addr, int index) \ +#define DO_GEN_ST(SUFF, OPC) \ +static inline void gen_aa32_st##SUFF(TCGv_i32 val, TCGv_i32 addr, int index) \ { \ - tcg_gen_qemu_##OP(val, addr, index); \ + tcg_gen_qemu_st_i32(val, addr, index, OPC); \ } static inline void gen_aa32_ld64(TCGv_i64 val, TCGv_i32 addr, int index) { - tcg_gen_qemu_ld64(val, addr, index); + tcg_gen_qemu_ld_i64(val, addr, index, MO_TEQ); } static inline void gen_aa32_st64(TCGv_i64 val, TCGv_i32 addr, int index) { - tcg_gen_qemu_st64(val, addr, index); + tcg_gen_qemu_st_i64(val, addr, index, MO_TEQ); } #else -#define DO_GEN_LD(OP) \ -static inline void gen_aa32_##OP(TCGv_i32 val, TCGv_i32 addr, int index) \ +#define DO_GEN_LD(SUFF, OPC) \ +static inline void gen_aa32_ld##SUFF(TCGv_i32 val, TCGv_i32 addr, int index) \ { \ TCGv addr64 = tcg_temp_new(); \ - TCGv val64 = tcg_temp_new(); \ tcg_gen_extu_i32_i64(addr64, addr); \ - tcg_gen_qemu_##OP(val64, addr64, index); \ + tcg_gen_qemu_ld_i32(val, addr64, index, OPC); \ tcg_temp_free(addr64); \ - tcg_gen_trunc_i64_i32(val, val64); \ - tcg_temp_free(val64); \ } -#define DO_GEN_ST(OP) \ -static inline void gen_aa32_##OP(TCGv_i32 val, TCGv_i32 addr, int index) \ +#define DO_GEN_ST(SUFF, OPC) \ +static inline void gen_aa32_st##SUFF(TCGv_i32 val, TCGv_i32 addr, int index) \ { \ TCGv addr64 = tcg_temp_new(); \ - TCGv val64 = tcg_temp_new(); \ tcg_gen_extu_i32_i64(addr64, addr); \ - tcg_gen_extu_i32_i64(val64, val); \ - tcg_gen_qemu_##OP(val64, addr64, index); \ + tcg_gen_qemu_st_i32(val, addr64, index, OPC); \ tcg_temp_free(addr64); \ - tcg_temp_free(val64); \ } static inline void gen_aa32_ld64(TCGv_i64 val, TCGv_i32 addr, int index) { TCGv addr64 = tcg_temp_new(); tcg_gen_extu_i32_i64(addr64, addr); - tcg_gen_qemu_ld64(val, addr64, index); + tcg_gen_qemu_ld_i64(val, addr64, index, MO_TEQ); tcg_temp_free(addr64); } @@ -889,20 +883,20 @@ static inline void gen_aa32_st64(TCGv_i64 val, TCGv_i32 addr, int index) { TCGv addr64 = tcg_temp_new(); tcg_gen_extu_i32_i64(addr64, addr); - tcg_gen_qemu_st64(val, addr64, index); + tcg_gen_qemu_st_i64(val, addr64, index, MO_TEQ); tcg_temp_free(addr64); } #endif -DO_GEN_LD(ld8s) -DO_GEN_LD(ld8u) -DO_GEN_LD(ld16s) -DO_GEN_LD(ld16u) -DO_GEN_LD(ld32u) -DO_GEN_ST(st8) -DO_GEN_ST(st16) -DO_GEN_ST(st32) +DO_GEN_LD(8s, MO_SB) +DO_GEN_LD(8u, MO_UB) +DO_GEN_LD(16s, MO_TESW) +DO_GEN_LD(16u, MO_TEUW) +DO_GEN_LD(32u, MO_TEUL) +DO_GEN_ST(8, MO_UB) +DO_GEN_ST(16, MO_TEUW) +DO_GEN_ST(32, MO_TEUL) static inline void gen_set_pc_im(DisasContext *s, target_ulong val) { @@ -2614,6 +2608,189 @@ static TCGv_i32 gen_load_and_replicate(DisasContext *s, TCGv_i32 addr, int size) return tmp; } +static int handle_vsel(uint32_t insn, uint32_t rd, uint32_t rn, uint32_t rm, + uint32_t dp) +{ + uint32_t cc = extract32(insn, 20, 2); + + if (dp) { + TCGv_i64 frn, frm, dest; + TCGv_i64 tmp, zero, zf, nf, vf; + + zero = tcg_const_i64(0); + + frn = tcg_temp_new_i64(); + frm = tcg_temp_new_i64(); + dest = tcg_temp_new_i64(); + + zf = tcg_temp_new_i64(); + nf = tcg_temp_new_i64(); + vf = tcg_temp_new_i64(); + + tcg_gen_extu_i32_i64(zf, cpu_ZF); + tcg_gen_ext_i32_i64(nf, cpu_NF); + tcg_gen_ext_i32_i64(vf, cpu_VF); + + tcg_gen_ld_f64(frn, cpu_env, vfp_reg_offset(dp, rn)); + tcg_gen_ld_f64(frm, cpu_env, vfp_reg_offset(dp, rm)); + switch (cc) { + case 0: /* eq: Z */ + tcg_gen_movcond_i64(TCG_COND_EQ, dest, zf, zero, + frn, frm); + break; + case 1: /* vs: V */ + tcg_gen_movcond_i64(TCG_COND_LT, dest, vf, zero, + frn, frm); + break; + case 2: /* ge: N == V -> N ^ V == 0 */ + tmp = tcg_temp_new_i64(); + tcg_gen_xor_i64(tmp, vf, nf); + tcg_gen_movcond_i64(TCG_COND_GE, dest, tmp, zero, + frn, frm); + tcg_temp_free_i64(tmp); + break; + case 3: /* gt: !Z && N == V */ + tcg_gen_movcond_i64(TCG_COND_NE, dest, zf, zero, + frn, frm); + tmp = tcg_temp_new_i64(); + tcg_gen_xor_i64(tmp, vf, nf); + tcg_gen_movcond_i64(TCG_COND_GE, dest, tmp, zero, + dest, frm); + tcg_temp_free_i64(tmp); + break; + } + tcg_gen_st_f64(dest, cpu_env, vfp_reg_offset(dp, rd)); + tcg_temp_free_i64(frn); + tcg_temp_free_i64(frm); + tcg_temp_free_i64(dest); + + tcg_temp_free_i64(zf); + tcg_temp_free_i64(nf); + tcg_temp_free_i64(vf); + + tcg_temp_free_i64(zero); + } else { + TCGv_i32 frn, frm, dest; + TCGv_i32 tmp, zero; + + zero = tcg_const_i32(0); + + frn = tcg_temp_new_i32(); + frm = tcg_temp_new_i32(); + dest = tcg_temp_new_i32(); + tcg_gen_ld_f32(frn, cpu_env, vfp_reg_offset(dp, rn)); + tcg_gen_ld_f32(frm, cpu_env, vfp_reg_offset(dp, rm)); + switch (cc) { + case 0: /* eq: Z */ + tcg_gen_movcond_i32(TCG_COND_EQ, dest, cpu_ZF, zero, + frn, frm); + break; + case 1: /* vs: V */ + tcg_gen_movcond_i32(TCG_COND_LT, dest, cpu_VF, zero, + frn, frm); + break; + case 2: /* ge: N == V -> N ^ V == 0 */ + tmp = tcg_temp_new_i32(); + tcg_gen_xor_i32(tmp, cpu_VF, cpu_NF); + tcg_gen_movcond_i32(TCG_COND_GE, dest, tmp, zero, + frn, frm); + tcg_temp_free_i32(tmp); + break; + case 3: /* gt: !Z && N == V */ + tcg_gen_movcond_i32(TCG_COND_NE, dest, cpu_ZF, zero, + frn, frm); + tmp = tcg_temp_new_i32(); + tcg_gen_xor_i32(tmp, cpu_VF, cpu_NF); + tcg_gen_movcond_i32(TCG_COND_GE, dest, tmp, zero, + dest, frm); + tcg_temp_free_i32(tmp); + break; + } + tcg_gen_st_f32(dest, cpu_env, vfp_reg_offset(dp, rd)); + tcg_temp_free_i32(frn); + tcg_temp_free_i32(frm); + tcg_temp_free_i32(dest); + + tcg_temp_free_i32(zero); + } + + return 0; +} + +static int handle_vminmaxnm(uint32_t insn, uint32_t rd, uint32_t rn, + uint32_t rm, uint32_t dp) +{ + uint32_t vmin = extract32(insn, 6, 1); + TCGv_ptr fpst = get_fpstatus_ptr(0); + + if (dp) { + TCGv_i64 frn, frm, dest; + + frn = tcg_temp_new_i64(); + frm = tcg_temp_new_i64(); + dest = tcg_temp_new_i64(); + + tcg_gen_ld_f64(frn, cpu_env, vfp_reg_offset(dp, rn)); + tcg_gen_ld_f64(frm, cpu_env, vfp_reg_offset(dp, rm)); + if (vmin) { + gen_helper_vfp_minnmd(dest, frn, frm, fpst); + } else { + gen_helper_vfp_maxnmd(dest, frn, frm, fpst); + } + tcg_gen_st_f64(dest, cpu_env, vfp_reg_offset(dp, rd)); + tcg_temp_free_i64(frn); + tcg_temp_free_i64(frm); + tcg_temp_free_i64(dest); + } else { + TCGv_i32 frn, frm, dest; + + frn = tcg_temp_new_i32(); + frm = tcg_temp_new_i32(); + dest = tcg_temp_new_i32(); + + tcg_gen_ld_f32(frn, cpu_env, vfp_reg_offset(dp, rn)); + tcg_gen_ld_f32(frm, cpu_env, vfp_reg_offset(dp, rm)); + if (vmin) { + gen_helper_vfp_minnms(dest, frn, frm, fpst); + } else { + gen_helper_vfp_maxnms(dest, frn, frm, fpst); + } + tcg_gen_st_f32(dest, cpu_env, vfp_reg_offset(dp, rd)); + tcg_temp_free_i32(frn); + tcg_temp_free_i32(frm); + tcg_temp_free_i32(dest); + } + + tcg_temp_free_ptr(fpst); + return 0; +} + +static int disas_vfp_v8_insn(CPUARMState *env, DisasContext *s, uint32_t insn) +{ + uint32_t rd, rn, rm, dp = extract32(insn, 8, 1); + + if (!arm_feature(env, ARM_FEATURE_V8)) { + return 1; + } + + if (dp) { + VFP_DREG_D(rd, insn); + VFP_DREG_N(rn, insn); + VFP_DREG_M(rm, insn); + } else { + rd = VFP_SREG_D(insn); + rn = VFP_SREG_N(insn); + rm = VFP_SREG_M(insn); + } + + if ((insn & 0x0f800e50) == 0x0e000a00) { + return handle_vsel(insn, rd, rn, rm, dp); + } else if ((insn & 0x0fb00e10) == 0x0e800a00) { + return handle_vminmaxnm(insn, rd, rn, rm, dp); + } + return 1; +} + /* Disassemble a VFP instruction. Returns nonzero if an error occurred (ie. an undefined instruction). */ static int disas_vfp_insn(CPUARMState * env, DisasContext *s, uint32_t insn) @@ -2636,6 +2813,14 @@ static int disas_vfp_insn(CPUARMState * env, DisasContext *s, uint32_t insn) && rn != ARM_VFP_MVFR1 && rn != ARM_VFP_MVFR0) return 1; } + + if (extract32(insn, 28, 4) == 0xf) { + /* Encodings with T=1 (Thumb) or unconditional (ARM): + * only used in v8 and above. + */ + return disas_vfp_v8_insn(env, s, insn); + } + dp = ((insn & 0xf00) == 0xb00); switch ((insn >> 24) & 0xf) { case 0xe: @@ -4362,7 +4547,7 @@ static void gen_neon_narrow_op(int op, int u, int size, #define NEON_3R_FLOAT_CMP 28 /* float VCEQ, VCGE, VCGT */ #define NEON_3R_FLOAT_ACMP 29 /* float VACGE, VACGT, VACLE, VACLT */ #define NEON_3R_FLOAT_MINMAX 30 /* float VMIN, VMAX */ -#define NEON_3R_VRECPS_VRSQRTS 31 /* float VRECPS, VRSQRTS */ +#define NEON_3R_FLOAT_MISC 31 /* float VRECPS, VRSQRTS, VMAXNM/MINNM */ static const uint8_t neon_3r_sizes[] = { [NEON_3R_VHADD] = 0x7, @@ -4395,7 +4580,7 @@ static const uint8_t neon_3r_sizes[] = { [NEON_3R_FLOAT_CMP] = 0x5, /* size bit 1 encodes op */ [NEON_3R_FLOAT_ACMP] = 0x5, /* size bit 1 encodes op */ [NEON_3R_FLOAT_MINMAX] = 0x5, /* size bit 1 encodes op */ - [NEON_3R_VRECPS_VRSQRTS] = 0x5, /* size bit 1 encodes op */ + [NEON_3R_FLOAT_MISC] = 0x5, /* size bit 1 encodes op */ }; /* Symbolic constants for op fields for Neon 2-register miscellaneous. @@ -4656,8 +4841,9 @@ static int disas_neon_data_insn(CPUARMState * env, DisasContext *s, uint32_t ins return 1; } break; - case NEON_3R_VRECPS_VRSQRTS: - if (u) { + case NEON_3R_FLOAT_MISC: + /* VMAXNM/VMINNM in ARMv8 */ + if (u && !arm_feature(env, ARM_FEATURE_V8)) { return 1; } break; @@ -4946,11 +5132,23 @@ static int disas_neon_data_insn(CPUARMState * env, DisasContext *s, uint32_t ins tcg_temp_free_ptr(fpstatus); break; } - case NEON_3R_VRECPS_VRSQRTS: - if (size == 0) - gen_helper_recps_f32(tmp, tmp, tmp2, cpu_env); - else - gen_helper_rsqrts_f32(tmp, tmp, tmp2, cpu_env); + case NEON_3R_FLOAT_MISC: + if (u) { + /* VMAXNM/VMINNM */ + TCGv_ptr fpstatus = get_fpstatus_ptr(1); + if (size == 0) { + gen_helper_vfp_maxnms(tmp, tmp, tmp2, fpstatus); + } else { + gen_helper_vfp_minnms(tmp, tmp, tmp2, fpstatus); + } + tcg_temp_free_ptr(fpstatus); + } else { + if (size == 0) { + gen_helper_recps_f32(tmp, tmp, tmp2, cpu_env); + } else { + gen_helper_rsqrts_f32(tmp, tmp, tmp2, cpu_env); + } + } break; case NEON_3R_VFM: { @@ -6296,9 +6494,6 @@ static int disas_coproc_insn(CPUARMState * env, DisasContext *s, uint32_t insn) return disas_dsp_insn(env, s, insn); } return 1; - case 10: - case 11: - return disas_vfp_insn (env, s, insn); default: break; } @@ -6753,6 +6948,13 @@ static void disas_arm_insn(CPUARMState * env, DisasContext *s) goto illegal_op; return; } + if ((insn & 0x0f000e10) == 0x0e000a00) { + /* VFP. */ + if (disas_vfp_insn(env, s, insn)) { + goto illegal_op; + } + return; + } if (((insn & 0x0f30f000) == 0x0510f000) || ((insn & 0x0f30f010) == 0x0710f000)) { if ((insn & (1 << 22)) == 0) { @@ -8033,9 +8235,15 @@ static void disas_arm_insn(CPUARMState * env, DisasContext *s) case 0xc: case 0xd: case 0xe: - /* Coprocessor. */ - if (disas_coproc_insn(env, s, insn)) + if (((insn >> 8) & 0xe) == 10) { + /* VFP. */ + if (disas_vfp_insn(env, s, insn)) { + goto illegal_op; + } + } else if (disas_coproc_insn(env, s, insn)) { + /* Coprocessor. */ goto illegal_op; + } break; case 0xf: /* swi */ @@ -8765,6 +8973,10 @@ static int disas_thumb2_insn(CPUARMState *env, DisasContext *s, uint16_t insn_hw insn = (insn & 0xe2ffffff) | ((insn & (1 << 28)) >> 4) | (1 << 28); if (disas_neon_data_insn(env, s, insn)) goto illegal_op; + } else if (((insn >> 8) & 0xe) == 10) { + if (disas_vfp_insn(env, s, insn)) { + goto illegal_op; + } } else { if (insn & (1 << 28)) goto illegal_op; diff --git a/target-cris/translate.c b/target-cris/translate.c index 5faa44c1ea..f990d591c7 100644 --- a/target-cris/translate.c +++ b/target-cris/translate.c @@ -1133,7 +1133,7 @@ static void gen_load64(DisasContext *dc, TCGv_i64 dst, TCGv addr) cris_store_direct_jmp(dc); } - tcg_gen_qemu_ld64(dst, addr, mem_index); + tcg_gen_qemu_ld_i64(dst, addr, mem_index, MO_TEQ); } static void gen_load(DisasContext *dc, TCGv dst, TCGv addr, @@ -1147,23 +1147,8 @@ static void gen_load(DisasContext *dc, TCGv dst, TCGv addr, cris_store_direct_jmp(dc); } - if (size == 1) { - if (sign) { - tcg_gen_qemu_ld8s(dst, addr, mem_index); - } else { - tcg_gen_qemu_ld8u(dst, addr, mem_index); - } - } else if (size == 2) { - if (sign) { - tcg_gen_qemu_ld16s(dst, addr, mem_index); - } else { - tcg_gen_qemu_ld16u(dst, addr, mem_index); - } - } else if (size == 4) { - tcg_gen_qemu_ld32u(dst, addr, mem_index); - } else { - abort(); - } + tcg_gen_qemu_ld_tl(dst, addr, mem_index, + MO_TE + ctz32(size) + (sign ? MO_SIGN : 0)); } static void gen_store (DisasContext *dc, TCGv addr, TCGv val, @@ -1187,13 +1172,7 @@ static void gen_store (DisasContext *dc, TCGv addr, TCGv val, return; } - if (size == 1) { - tcg_gen_qemu_st8(val, addr, mem_index); - } else if (size == 2) { - tcg_gen_qemu_st16(val, addr, mem_index); - } else { - tcg_gen_qemu_st32(val, addr, mem_index); - } + tcg_gen_qemu_st_tl(val, addr, mem_index, MO_TE + ctz32(size)); if (dc->flagx_known && dc->flags_x) { cris_evaluate_flags(dc); diff --git a/target-i386/cpu.c b/target-i386/cpu.c index 47af9a8816..bb98f6defc 100644 --- a/target-i386/cpu.c +++ b/target-i386/cpu.c @@ -335,7 +335,7 @@ typedef struct ExtSaveArea { static const ExtSaveArea ext_save_areas[] = { [2] = { .feature = FEAT_1_ECX, .bits = CPUID_EXT_AVX, - .offset = 0x100, .size = 0x240 }, + .offset = 0x240, .size = 0x100 }, }; const char *get_register_name_32(unsigned int reg) @@ -2227,8 +2227,8 @@ void cpu_x86_cpuid(CPUX86State *env, uint32_t index, uint32_t count, const ExtSaveArea *esa = &ext_save_areas[count]; if ((env->features[esa->feature] & esa->bits) == esa->bits && (kvm_mask & (1 << count)) != 0) { - *eax = esa->offset; - *ebx = esa->size; + *eax = esa->size; + *ebx = esa->offset; } } break; diff --git a/target-microblaze/translate.c b/target-microblaze/translate.c index 9edcb67e66..270138c6d2 100644 --- a/target-microblaze/translate.c +++ b/target-microblaze/translate.c @@ -864,26 +864,6 @@ static void dec_imm(DisasContext *dc) dc->clear_imm = 0; } -static inline void gen_load(DisasContext *dc, TCGv dst, TCGv addr, - unsigned int size, bool exclusive) -{ - int mem_index = cpu_mmu_index(dc->env); - - if (size == 1) { - tcg_gen_qemu_ld8u(dst, addr, mem_index); - } else if (size == 2) { - tcg_gen_qemu_ld16u(dst, addr, mem_index); - } else if (size == 4) { - tcg_gen_qemu_ld32u(dst, addr, mem_index); - } else - cpu_abort(dc->env, "Incorrect load size %d\n", size); - - if (exclusive) { - tcg_gen_mov_tl(env_res_addr, addr); - tcg_gen_mov_tl(env_res_val, dst); - } -} - static inline TCGv *compute_ldst_addr(DisasContext *dc, TCGv *t) { unsigned int extimm = dc->tb_flags & IMM_FLAG; @@ -935,35 +915,22 @@ static inline TCGv *compute_ldst_addr(DisasContext *dc, TCGv *t) return t; } -static inline void dec_byteswap(DisasContext *dc, TCGv dst, TCGv src, int size) -{ - if (size == 4) { - tcg_gen_bswap32_tl(dst, src); - } else if (size == 2) { - TCGv t = tcg_temp_new(); - - /* bswap16 assumes the high bits are zero. */ - tcg_gen_andi_tl(t, src, 0xffff); - tcg_gen_bswap16_tl(dst, t); - tcg_temp_free(t); - } else { - /* Ignore. - cpu_abort(dc->env, "Invalid ldst byteswap size %d\n", size); - */ - } -} - static void dec_load(DisasContext *dc) { - TCGv t, *addr; + TCGv t, v, *addr; unsigned int size, rev = 0, ex = 0; + TCGMemOp mop; - size = 1 << (dc->opcode & 3); - + mop = dc->opcode & 3; + size = 1 << mop; if (!dc->type_b) { rev = (dc->ir >> 9) & 1; ex = (dc->ir >> 10) & 1; } + mop |= MO_TE; + if (rev) { + mop ^= MO_BSWAP; + } if (size > 4 && (dc->tb_flags & MSR_EE_FLAG) && (dc->env->pvr.regs[2] & PVR2_ILL_OPCODE_EXC_MASK)) { @@ -1044,40 +1011,30 @@ static void dec_load(DisasContext *dc) sync_jmpstate(dc); /* Verify alignment if needed. */ - if ((dc->env->pvr.regs[2] & PVR2_UNALIGNED_EXC_MASK) && size > 1) { - TCGv v = tcg_temp_new(); - - /* - * Microblaze gives MMU faults priority over faults due to - * unaligned addresses. That's why we speculatively do the load - * into v. If the load succeeds, we verify alignment of the - * address and if that succeeds we write into the destination reg. - */ - gen_load(dc, v, *addr, size, ex); + /* + * Microblaze gives MMU faults priority over faults due to + * unaligned addresses. That's why we speculatively do the load + * into v. If the load succeeds, we verify alignment of the + * address and if that succeeds we write into the destination reg. + */ + v = tcg_temp_new(); + tcg_gen_qemu_ld_tl(v, *addr, cpu_mmu_index(dc->env), mop); + if ((dc->env->pvr.regs[2] & PVR2_UNALIGNED_EXC_MASK) && size > 1) { tcg_gen_movi_tl(cpu_SR[SR_PC], dc->pc); gen_helper_memalign(cpu_env, *addr, tcg_const_tl(dc->rd), tcg_const_tl(0), tcg_const_tl(size - 1)); - if (dc->rd) { - if (rev) { - dec_byteswap(dc, cpu_R[dc->rd], v, size); - } else { - tcg_gen_mov_tl(cpu_R[dc->rd], v); - } - } - tcg_temp_free(v); - } else { - if (dc->rd) { - gen_load(dc, cpu_R[dc->rd], *addr, size, ex); - if (rev) { - dec_byteswap(dc, cpu_R[dc->rd], cpu_R[dc->rd], size); - } - } else { - /* We are loading into r0, no need to reverse. */ - gen_load(dc, env_imm, *addr, size, ex); - } } + if (ex) { + tcg_gen_mov_tl(env_res_addr, *addr); + tcg_gen_mov_tl(env_res_val, v); + } + if (dc->rd) { + tcg_gen_mov_tl(cpu_R[dc->rd], v); + } + tcg_temp_free(v); + if (ex) { /* lwx */ /* no support for for AXI exclusive so always clear C */ write_carryi(dc, 0); @@ -1087,32 +1044,23 @@ static void dec_load(DisasContext *dc) tcg_temp_free(t); } -static void gen_store(DisasContext *dc, TCGv addr, TCGv val, - unsigned int size) -{ - int mem_index = cpu_mmu_index(dc->env); - - if (size == 1) - tcg_gen_qemu_st8(val, addr, mem_index); - else if (size == 2) { - tcg_gen_qemu_st16(val, addr, mem_index); - } else if (size == 4) { - tcg_gen_qemu_st32(val, addr, mem_index); - } else - cpu_abort(dc->env, "Incorrect store size %d\n", size); -} - static void dec_store(DisasContext *dc) { TCGv t, *addr, swx_addr; int swx_skip = 0; unsigned int size, rev = 0, ex = 0; + TCGMemOp mop; - size = 1 << (dc->opcode & 3); + mop = dc->opcode & 3; + size = 1 << mop; if (!dc->type_b) { rev = (dc->ir >> 9) & 1; ex = (dc->ir >> 10) & 1; } + mop |= MO_TE; + if (rev) { + mop ^= MO_BSWAP; + } if (size > 4 && (dc->tb_flags & MSR_EE_FLAG) && (dc->env->pvr.regs[2] & PVR2_ILL_OPCODE_EXC_MASK)) { @@ -1148,7 +1096,7 @@ static void dec_store(DisasContext *dc) this compare and the following write to be atomic. For user emulation we need to add atomicity between threads. */ tval = tcg_temp_new(); - gen_load(dc, tval, swx_addr, 4, false); + tcg_gen_qemu_ld_tl(tval, swx_addr, cpu_mmu_index(dc->env), MO_TEUL); tcg_gen_brcond_tl(TCG_COND_NE, env_res_val, tval, swx_skip); write_carryi(dc, 0); tcg_temp_free(tval); @@ -1197,25 +1145,8 @@ static void dec_store(DisasContext *dc) cpu_abort(dc->env, "Invalid reverse size\n"); break; } - - if (size != 1) { - TCGv bs_data = tcg_temp_new(); - dec_byteswap(dc, bs_data, cpu_R[dc->rd], size); - gen_store(dc, *addr, bs_data, size); - tcg_temp_free(bs_data); - } else { - gen_store(dc, *addr, cpu_R[dc->rd], size); - } - } else { - if (rev) { - TCGv bs_data = tcg_temp_new(); - dec_byteswap(dc, bs_data, cpu_R[dc->rd], size); - gen_store(dc, *addr, bs_data, size); - tcg_temp_free(bs_data); - } else { - gen_store(dc, *addr, cpu_R[dc->rd], size); - } } + tcg_gen_qemu_st_tl(cpu_R[dc->rd], *addr, cpu_mmu_index(dc->env), mop); /* Verify alignment if needed. */ if ((dc->env->pvr.regs[2] & PVR2_UNALIGNED_EXC_MASK) && size > 1) { diff --git a/target-mips/cpu.h b/target-mips/cpu.h index a29c82faf1..9caf4474b9 100644 --- a/target-mips/cpu.h +++ b/target-mips/cpu.h @@ -476,7 +476,7 @@ struct CPUMIPSState { const mips_def_t *cpu_model; void *irq[8]; - struct QEMUTimer *timer; /* Internal timer */ + QEMUTimer *timer; /* Internal timer */ }; #include "cpu-qom.h" diff --git a/target-mips/dsp_helper.c b/target-mips/dsp_helper.c index b088a25017..a2f46d9637 100644 --- a/target-mips/dsp_helper.c +++ b/target-mips/dsp_helper.c @@ -1088,12 +1088,11 @@ static inline int32_t mipsdsp_cmpu_lt(uint32_t a, uint32_t b) target_ulong helper_##name(target_ulong rt, CPUMIPSState *env) \ { \ DSP32Value dt; \ - unsigned int i, n; \ + unsigned int i; \ \ - n = sizeof(DSP32Value) / sizeof(dt.element[0]); \ dt.sw[0] = rt; \ \ - for (i = 0; i < n; i++) { \ + for (i = 0; i < ARRAY_SIZE(dt.element); i++) { \ dt.element[i] = mipsdsp_##func(dt.element[i], env); \ } \ \ @@ -1109,12 +1108,11 @@ MIPSDSP32_UNOP_ENV(absq_s_w, sat_abs32, sw) target_ulong helper_##name(target_ulong rt, CPUMIPSState *env) \ { \ DSP64Value dt; \ - unsigned int i, n; \ + unsigned int i; \ \ - n = sizeof(DSP64Value) / sizeof(dt.element[0]); \ dt.sl[0] = rt; \ \ - for (i = 0; i < n; i++) { \ + for (i = 0; i < ARRAY_SIZE(dt.element); i++) { \ dt.element[i] = mipsdsp_##func(dt.element[i], env); \ } \ \ @@ -1130,13 +1128,12 @@ MIPSDSP64_UNOP_ENV(absq_s_pw, sat_abs32, sw) target_ulong helper_##name(target_ulong rs, target_ulong rt) \ { \ DSP32Value ds, dt; \ - unsigned int i, n; \ + unsigned int i; \ \ - n = sizeof(DSP32Value) / sizeof(ds.element[0]); \ ds.sw[0] = rs; \ dt.sw[0] = rt; \ \ - for (i = 0; i < n; i++) { \ + for (i = 0; i < ARRAY_SIZE(ds.element); i++) { \ ds.element[i] = mipsdsp_##func(ds.element[i], dt.element[i]); \ } \ \ @@ -1159,13 +1156,12 @@ target_ulong helper_##name(target_ulong rs, target_ulong rt, \ CPUMIPSState *env) \ { \ DSP32Value ds, dt; \ - unsigned int i, n; \ + unsigned int i; \ \ - n = sizeof(DSP32Value) / sizeof(ds.element[0]); \ ds.sw[0] = rs; \ dt.sw[0] = rt; \ \ - for (i = 0 ; i < n ; i++) { \ + for (i = 0 ; i < ARRAY_SIZE(ds.element); i++) { \ ds.element[i] = mipsdsp_##func(ds.element[i], dt.element[i], env); \ } \ \ @@ -1192,13 +1188,12 @@ MIPSDSP32_BINOP_ENV(subu_s_qb, satu8_sub, ub); target_ulong helper_##name(target_ulong rs, target_ulong rt) \ { \ DSP64Value ds, dt; \ - unsigned int i, n; \ + unsigned int i; \ \ - n = sizeof(DSP64Value) / sizeof(ds.element[0]); \ ds.sl[0] = rs; \ dt.sl[0] = rt; \ \ - for (i = 0 ; i < n ; i++) { \ + for (i = 0 ; i < ARRAY_SIZE(ds.element); i++) { \ ds.element[i] = mipsdsp_##func(ds.element[i], dt.element[i]); \ } \ \ @@ -1215,13 +1210,12 @@ target_ulong helper_##name(target_ulong rs, target_ulong rt, \ CPUMIPSState *env) \ { \ DSP64Value ds, dt; \ - unsigned int i, n; \ + unsigned int i; \ \ - n = sizeof(DSP64Value) / sizeof(ds.element[0]); \ ds.sl[0] = rs; \ dt.sl[0] = rt; \ \ - for (i = 0 ; i < n ; i++) { \ + for (i = 0 ; i < ARRAY_SIZE(ds.element); i++) { \ ds.element[i] = mipsdsp_##func(ds.element[i], dt.element[i], env); \ } \ \ diff --git a/target-mips/translate.c b/target-mips/translate.c index 67f326b205..e30273438a 100644 --- a/target-mips/translate.c +++ b/target-mips/translate.c @@ -15983,10 +15983,13 @@ void cpu_state_reset(CPUMIPSState *env) if (env->CP0_Config3 & (1 << CP0C3_DSPP)) { env->CP0_Status |= (1 << CP0St_MX); } - /* Enable 64-bit FPU if the target cpu supports it. */ - if (env->active_fpu.fcr0 & (1 << FCR0_F64)) { +# if defined(TARGET_MIPS64) + /* For MIPS64, init FR bit to 1 if FPU unit is there and bit is writable. */ + if ((env->CP0_Config1 & (1 << CP0C1_FP)) && + (env->CP0_Status_rw_bitmask & (1 << CP0St_FR))) { env->CP0_Status |= (1 << CP0St_FR); } +# endif #else if (env->hflags & MIPS_HFLAG_BMASK) { /* If the exception was raised from a delay slot, diff --git a/target-openrisc/cpu.h b/target-openrisc/cpu.h index 0f9efdf6de..51d6afd153 100644 --- a/target-openrisc/cpu.h +++ b/target-openrisc/cpu.h @@ -307,7 +307,7 @@ typedef struct CPUOpenRISCState { #ifndef CONFIG_USER_ONLY CPUOpenRISCTLBContext * tlb; - struct QEMUTimer *timer; + QEMUTimer *timer; uint32_t ttmr; /* Timer tick mode register */ uint32_t ttcr; /* Timer tick count register */ diff --git a/target-sh4/cpu.h b/target-sh4/cpu.h index 276d2955c3..c181ddacf5 100644 --- a/target-sh4/cpu.h +++ b/target-sh4/cpu.h @@ -157,9 +157,6 @@ typedef struct CPUSH4State { /* float point status register */ float_status fp_status; - /* The features that we should emulate. See sh_features above. */ - uint32_t features; - /* Those belong to the specific unit (SH7750) but are handled here */ uint32_t mmucr; /* MMU control register */ uint32_t pteh; /* page table entry high register */ @@ -180,6 +177,9 @@ typedef struct CPUSH4State { int id; /* CPU model */ + /* The features that we should emulate. See sh_features above. */ + uint32_t features; + void *intc_handle; int in_sleep; /* SR_BL ignored during sleep */ memory_content *movcal_backup; diff --git a/target-sparc/cpu.h b/target-sparc/cpu.h index 41194ec06b..c519063ba9 100644 --- a/target-sparc/cpu.h +++ b/target-sparc/cpu.h @@ -370,7 +370,7 @@ struct CPUTimer uint32_t disabled; uint64_t disabled_mask; int64_t clock_offset; - struct QEMUTimer *qtimer; + QEMUTimer *qtimer; }; typedef struct CPUTimer CPUTimer; diff --git a/tcg/arm/tcg-target.c b/tcg/arm/tcg-target.c index e93a4a237b..82658a170c 100644 --- a/tcg/arm/tcg-target.c +++ b/tcg/arm/tcg-target.c @@ -22,6 +22,7 @@ * THE SOFTWARE. */ +#include "elf.h" #include "tcg-be-ldst.h" /* The __ARM_ARCH define is provided by gcc 4.8. Construct it otherwise. */ @@ -58,9 +59,6 @@ static int arm_arch = __ARM_ARCH; #ifndef use_idiv_instructions bool use_idiv_instructions; #endif -#ifdef CONFIG_GETAUXVAL -# include <sys/auxv.h> -#endif #ifndef NDEBUG static const char * const tcg_target_reg_names[TCG_TARGET_NB_REGS] = { @@ -2036,22 +2034,20 @@ static const TCGTargetOpDef arm_op_defs[] = { static void tcg_target_init(TCGContext *s) { -#if defined(CONFIG_GETAUXVAL) /* Only probe for the platform and capabilities if we havn't already determined maximum values at compile time. */ -# if !defined(use_idiv_instructions) +#ifndef use_idiv_instructions { - unsigned long hwcap = getauxval(AT_HWCAP); + unsigned long hwcap = qemu_getauxval(AT_HWCAP); use_idiv_instructions = (hwcap & HWCAP_ARM_IDIVA) != 0; } -# endif +#endif if (__ARM_ARCH < 7) { - const char *pl = (const char *)getauxval(AT_PLATFORM); + const char *pl = (const char *)qemu_getauxval(AT_PLATFORM); if (pl != NULL && pl[0] == 'v' && pl[1] >= '4' && pl[1] <= '9') { arm_arch = pl[1] - '0'; } } -#endif /* GETAUXVAL */ tcg_regset_set32(tcg_target_available_regs[TCG_TYPE_I32], 0, 0xffff); tcg_regset_set32(tcg_target_call_clobber_regs, 0, diff --git a/tcg/ppc64/tcg-target.c b/tcg/ppc64/tcg-target.c index 6109d862db..06e440f9bc 100644 --- a/tcg/ppc64/tcg-target.c +++ b/tcg/ppc64/tcg-target.c @@ -45,15 +45,10 @@ static uint8_t *tb_ret_addr; #define GUEST_BASE 0 #endif -#ifdef CONFIG_GETAUXVAL -#include <sys/auxv.h> +#include "elf.h" static bool have_isa_2_06; #define HAVE_ISA_2_06 have_isa_2_06 #define HAVE_ISEL have_isa_2_06 -#else -#define HAVE_ISA_2_06 0 -#define HAVE_ISEL 0 -#endif #ifdef CONFIG_USE_GUEST_BASE #define TCG_GUEST_BASE_REG 30 @@ -2132,12 +2127,10 @@ static const TCGTargetOpDef ppc_op_defs[] = { static void tcg_target_init(TCGContext *s) { -#ifdef CONFIG_GETAUXVAL - unsigned long hwcap = getauxval(AT_HWCAP); + unsigned long hwcap = qemu_getauxval(AT_HWCAP); if (hwcap & PPC_FEATURE_ARCH_2_06) { have_isa_2_06 = true; } -#endif tcg_regset_set32(tcg_target_available_regs[TCG_TYPE_I32], 0, 0xffffffff); tcg_regset_set32(tcg_target_available_regs[TCG_TYPE_I64], 0, 0xffffffff); diff --git a/tcg/s390/tcg-target.c b/tcg/s390/tcg-target.c index 0a4f3be0e9..248726e82f 100644 --- a/tcg/s390/tcg-target.c +++ b/tcg/s390/tcg-target.c @@ -31,6 +31,8 @@ #error "unsupported code generation mode" #endif +#include "elf.h" + /* ??? The translation blocks produced by TCG are generally small enough to be entirely reachable with a 16-bit displacement. Leaving the option for a 32-bit displacement here Just In Case. */ @@ -2233,91 +2235,18 @@ static void sigill_handler(int sig) static void query_facilities(void) { - struct sigaction sa_old, sa_new; - register int r0 __asm__("0"); - register void *r1 __asm__("1"); - int fail; - - memset(&sa_new, 0, sizeof(sa_new)); - sa_new.sa_handler = sigill_handler; - sigaction(SIGILL, &sa_new, &sa_old); - - /* First, try STORE FACILITY LIST EXTENDED. If this is present, then - we need not do any more probing. Unfortunately, this itself is an - extension and the original STORE FACILITY LIST instruction is - kernel-only, storing its results at absolute address 200. */ - /* stfle 0(%r1) */ - r1 = &facilities; - asm volatile(".word 0xb2b0,0x1000" - : "=r"(r0) : "0"(0), "r"(r1) : "memory", "cc"); - - if (got_sigill) { - /* STORE FACILITY EXTENDED is not available. Probe for one of each - kind of instruction that we're interested in. */ - /* ??? Possibly some of these are in practice never present unless - the store-facility-extended facility is also present. But since - that isn't documented it's just better to probe for each. */ - - /* Test for z/Architecture. Required even in 31-bit mode. */ - got_sigill = 0; - /* agr %r0,%r0 */ - asm volatile(".word 0xb908,0x0000" : "=r"(r0) : : "cc"); - if (!got_sigill) { - facilities |= FACILITY_ZARCH_ACTIVE; - } - - /* Test for long displacement. */ - got_sigill = 0; - /* ly %r0,0(%r1) */ - r1 = &facilities; - asm volatile(".word 0xe300,0x1000,0x0058" - : "=r"(r0) : "r"(r1) : "cc"); - if (!got_sigill) { - facilities |= FACILITY_LONG_DISP; - } - - /* Test for extended immediates. */ - got_sigill = 0; - /* afi %r0,0 */ - asm volatile(".word 0xc209,0x0000,0x0000" : : : "cc"); - if (!got_sigill) { - facilities |= FACILITY_EXT_IMM; - } - - /* Test for general-instructions-extension. */ - got_sigill = 0; - /* msfi %r0,1 */ - asm volatile(".word 0xc201,0x0000,0x0001"); - if (!got_sigill) { - facilities |= FACILITY_GEN_INST_EXT; - } - } - - sigaction(SIGILL, &sa_old, NULL); + unsigned long hwcap = qemu_getauxval(AT_HWCAP); - /* The translator currently uses these extensions unconditionally. - Pruning this back to the base ESA/390 architecture doesn't seem - worthwhile, since even the KVM target requires z/Arch. */ - fail = 0; - if ((facilities & FACILITY_ZARCH_ACTIVE) == 0) { - fprintf(stderr, "TCG: z/Arch facility is required.\n"); - fprintf(stderr, "TCG: Boot with a 64-bit enabled kernel.\n"); - fail = 1; - } - if ((facilities & FACILITY_LONG_DISP) == 0) { - fprintf(stderr, "TCG: long-displacement facility is required.\n"); - fail = 1; - } + /* Is STORE FACILITY LIST EXTENDED available? Honestly, I believe this + is present on all 64-bit systems, but let's check for it anyway. */ + if (hwcap & HWCAP_S390_STFLE) { + register int r0 __asm__("0"); + register void *r1 __asm__("1"); - /* So far there's just enough support for 31-bit mode to let the - compile succeed. This is good enough to run QEMU with KVM. */ - if (sizeof(void *) != 8) { - fprintf(stderr, "TCG: 31-bit mode is not supported.\n"); - fail = 1; - } - - if (fail) { - exit(-1); + /* stfle 0(%r1) */ + r1 = &facilities; + asm volatile(".word 0xb2b0,0x1000" + : "=r"(r0) : "0"(0), "r"(r1) : "memory", "cc"); } } @@ -357,11 +357,12 @@ void tcg_set_frame(TCGContext *s, int reg, intptr_t start, intptr_t size) void tcg_func_start(TCGContext *s) { - int i; tcg_pool_reset(s); s->nb_temps = s->nb_globals; - for(i = 0; i < (TCG_TYPE_COUNT * 2); i++) - s->first_free_temp[i] = -1; + + /* No temps have been previously allocated for size or locality. */ + memset(s->free_temps, 0, sizeof(s->free_temps)); + s->labels = tcg_malloc(sizeof(TCGLabel) * TCG_MAX_LABELS); s->nb_labels = 0; s->current_frame_offset = s->frame_start; @@ -503,16 +504,15 @@ static inline int tcg_temp_new_internal(TCGType type, int temp_local) TCGTemp *ts; int idx, k; - k = type; - if (temp_local) - k += TCG_TYPE_COUNT; - idx = s->first_free_temp[k]; - if (idx != -1) { - /* There is already an available temp with the - right type */ + k = type + (temp_local ? TCG_TYPE_COUNT : 0); + idx = find_first_bit(s->free_temps[k].l, TCG_MAX_TEMPS); + if (idx < TCG_MAX_TEMPS) { + /* There is already an available temp with the right type. */ + clear_bit(idx, s->free_temps[k].l); + ts = &s->temps[idx]; - s->first_free_temp[k] = ts->next_free_temp; ts->temp_allocated = 1; + assert(ts->base_type == type); assert(ts->temp_local == temp_local); } else { idx = s->nb_temps; @@ -568,7 +568,7 @@ TCGv_i64 tcg_temp_new_internal_i64(int temp_local) return MAKE_TCGV_I64(idx); } -static inline void tcg_temp_free_internal(int idx) +static void tcg_temp_free_internal(int idx) { TCGContext *s = &tcg_ctx; TCGTemp *ts; @@ -585,11 +585,9 @@ static inline void tcg_temp_free_internal(int idx) ts = &s->temps[idx]; assert(ts->temp_allocated != 0); ts->temp_allocated = 0; - k = ts->base_type; - if (ts->temp_local) - k += TCG_TYPE_COUNT; - ts->next_free_temp = s->first_free_temp[k]; - s->first_free_temp[k] = idx; + + k = ts->type + (ts->temp_local ? TCG_TYPE_COUNT : 0); + set_bit(idx, s->free_temps[k].l); } void tcg_temp_free_i32(TCGv_i32 arg) @@ -26,7 +26,7 @@ #define TCG_H #include "qemu-common.h" - +#include "qemu/bitops.h" #include "tcg-target.h" /* Default target word size to pointer size. */ @@ -436,13 +436,15 @@ typedef struct TCGTemp { basic blocks. Otherwise, it is not preserved across basic blocks. */ unsigned int temp_allocated:1; /* never used for code gen */ - /* index of next free temp of same base type, -1 if end */ - int next_free_temp; const char *name; } TCGTemp; typedef struct TCGContext TCGContext; +typedef struct TCGTempSet { + unsigned long l[BITS_TO_LONGS(TCG_MAX_TEMPS)]; +} TCGTempSet; + struct TCGContext { uint8_t *pool_cur, *pool_end; TCGPool *pool_first, *pool_current, *pool_first_large; @@ -450,8 +452,6 @@ struct TCGContext { int nb_labels; int nb_globals; int nb_temps; - /* index of free temps, -1 if none */ - int first_free_temp[TCG_TYPE_COUNT * 2]; /* goto_tb support */ uint8_t *code_buf; @@ -477,6 +477,7 @@ struct TCGContext { uint8_t *code_ptr; TCGTemp temps[TCG_MAX_TEMPS]; /* globals first, temps after */ + TCGTempSet free_temps[TCG_TYPE_COUNT * 2]; GHashTable *helpers; diff --git a/trace-events b/trace-events index e32d00cefb..9f4456a82e 100644 --- a/trace-events +++ b/trace-events @@ -311,6 +311,9 @@ usb_ehci_sitd(uint32_t addr, uint32_t nxt, uint32_t active) "ITD @ %08x: next %0 usb_ehci_port_attach(uint32_t port, const char *owner, const char *device) "attach port #%d, owner %s, device %s" usb_ehci_port_detach(uint32_t port, const char *owner) "detach port #%d, owner %s" usb_ehci_port_reset(uint32_t port, int enable) "reset port #%d - %d" +usb_ehci_port_suspend(uint32_t port) "port #%d" +usb_ehci_port_wakeup(uint32_t port) "port #%d" +usb_ehci_port_resume(uint32_t port) "port #%d" usb_ehci_queue_action(void *q, const char *action) "q %p: %s" usb_ehci_packet_action(void *q, void *p, const char *action) "q %p p %p: %s" usb_ehci_irq(uint32_t level, uint32_t frindex, uint32_t sts, uint32_t mask) "level %d, frindex 0x%04x, sts 0x%x, mask 0x%x" @@ -429,45 +432,32 @@ usb_uas_tmf_abort_task(int addr, uint16_t tag, uint16_t task_tag) "dev %d, tag 0 usb_uas_tmf_logical_unit_reset(int addr, uint16_t tag, int lun) "dev %d, tag 0x%x, lun %d" usb_uas_tmf_unsupported(int addr, uint16_t tag, uint32_t function) "dev %d, tag 0x%x, function 0x%x" -# hw/usb/host-linux.c # hw/usb/host-libusb.c usb_host_open_started(int bus, int addr) "dev %d:%d" usb_host_open_success(int bus, int addr) "dev %d:%d" usb_host_open_failure(int bus, int addr) "dev %d:%d" -usb_host_disconnect(int bus, int addr) "dev %d:%d" usb_host_close(int bus, int addr) "dev %d:%d" usb_host_attach_kernel(int bus, int addr, int interface) "dev %d:%d, if %d" usb_host_detach_kernel(int bus, int addr, int interface) "dev %d:%d, if %d" usb_host_set_address(int bus, int addr, int config) "dev %d:%d, address %d" usb_host_set_config(int bus, int addr, int config) "dev %d:%d, config %d" usb_host_set_interface(int bus, int addr, int interface, int alt) "dev %d:%d, interface %d, alt %d" -usb_host_claim_interfaces(int bus, int addr, int config, int nif) "dev %d:%d, config %d, nif %d" usb_host_claim_interface(int bus, int addr, int config, int interface) "dev %d:%d, config %d, if %d" -usb_host_release_interfaces(int bus, int addr) "dev %d:%d" usb_host_release_interface(int bus, int addr, int interface) "dev %d:%d, if %d" usb_host_req_control(int bus, int addr, void *p, int req, int value, int index) "dev %d:%d, packet %p, req 0x%x, value %d, index %d" usb_host_req_data(int bus, int addr, void *p, int in, int ep, int size) "dev %d:%d, packet %p, in %d, ep %d, size %d" usb_host_req_complete(int bus, int addr, void *p, int status, int length) "dev %d:%d, packet %p, status %d, length %d" usb_host_req_emulated(int bus, int addr, void *p, int status) "dev %d:%d, packet %p, status %d" usb_host_req_canceled(int bus, int addr, void *p) "dev %d:%d, packet %p" -usb_host_urb_submit(int bus, int addr, void *aurb, int length, int more) "dev %d:%d, aurb %p, length %d, more %d" -usb_host_urb_complete(int bus, int addr, void *aurb, int status, int length, int more) "dev %d:%d, aurb %p, status %d, length %d, more %d" -usb_host_urb_canceled(int bus, int addr, void *aurb) "dev %d:%d, aurb %p" -usb_host_ep_set_halt(int bus, int addr, int ep) "dev %d:%d, ep %d" -usb_host_ep_clear_halt(int bus, int addr, int ep) "dev %d:%d, ep %d" usb_host_iso_start(int bus, int addr, int ep) "dev %d:%d, ep %d" usb_host_iso_stop(int bus, int addr, int ep) "dev %d:%d, ep %d" usb_host_iso_out_of_bufs(int bus, int addr, int ep) "dev %d:%d, ep %d" -usb_host_iso_many_urbs(int bus, int addr, int count) "dev %d:%d, count %d" usb_host_reset(int bus, int addr) "dev %d:%d" usb_host_auto_scan_enabled(void) usb_host_auto_scan_disabled(void) -usb_host_claim_port(int bus, int hub, int port) "bus %d, hub addr %d, port %d" -usb_host_parse_device(int bus, int addr, int vendor, int product) "dev %d:%d, id %04x:%04x" usb_host_parse_config(int bus, int addr, int value, int active) "dev %d:%d, value %d, active %d" usb_host_parse_interface(int bus, int addr, int num, int alt, int active) "dev %d:%d, num %d, alt %d, active %d" usb_host_parse_endpoint(int bus, int addr, int ep, const char *dir, const char *type, int active) "dev %d:%d, ep %d, %s, %s, active %d" -usb_host_parse_unknown(int bus, int addr, int len, int type) "dev %d:%d, len %d, type %d" usb_host_parse_error(int bus, int addr, const char *errmsg) "dev %d:%d, msg %s" # hw/scsi/scsi-bus.c @@ -1012,6 +1002,8 @@ dma_map_wait(void *dbs) "dbs=%p" # ui/console.c console_gfx_new(void) "" +console_putchar_csi(int esc_param0, int esc_param1, int ch, int nb_esc_params) "escape sequence CSI%d;%d%c, %d parameters" +console_putchar_unhandled(int ch) "unhandled escape character '%c'" console_txt_new(int w, int h) "%dx%d" console_select(int nr) "%d" console_refresh(int interval) "interval %d ms" @@ -1022,6 +1014,11 @@ displaychangelistener_register(void *dcl, const char *name) "%p [ %s ]" displaychangelistener_unregister(void *dcl, const char *name) "%p [ %s ]" ppm_save(const char *filename, void *display_surface) "%s surface=%p" +# ui/gtk.c +gd_switch(int width, int height) "width=%d, height=%d" +gd_update(int x, int y, int w, int h) "x=%d, y=%d, w=%d, h=%d" +gd_key_event(int gdk_keycode, int qemu_keycode, const char *action) "translated GDK keycode %d to QEMU keycode %d (%s)" + # hw/display/vmware_vga.c vmware_value_read(uint32_t index, uint32_t value) "index %d, value 0x%x" vmware_value_write(uint32_t index, uint32_t value) "index %d, value 0x%x" diff --git a/ui/console.c b/ui/console.c index 199ba69101..502e1600ab 100644 --- a/ui/console.c +++ b/ui/console.c @@ -27,8 +27,8 @@ #include "qemu/timer.h" #include "qmp-commands.h" #include "sysemu/char.h" +#include "trace.h" -//#define DEBUG_CONSOLE #define DEFAULT_BACKSCROLL 512 #define MAX_CONSOLES 12 #define CONSOLE_CURSOR_PERIOD 500 @@ -161,7 +161,7 @@ struct QemuConsole { }; struct DisplayState { - struct QEMUTimer *gui_timer; + QEMUTimer *gui_timer; uint64_t last_update; uint64_t update_interval; bool refreshing; @@ -866,10 +866,8 @@ static void console_putchar(QemuConsole *s, int ch) s->nb_esc_params++; if (ch == ';') break; -#ifdef DEBUG_CONSOLE - fprintf(stderr, "escape sequence CSI%d;%d%c, %d parameters\n", - s->esc_params[0], s->esc_params[1], ch, s->nb_esc_params); -#endif + trace_console_putchar_csi(s->esc_params[0], s->esc_params[1], + ch, s->nb_esc_params); s->state = TTY_STATE_NORM; switch(ch) { case 'A': @@ -983,9 +981,7 @@ static void console_putchar(QemuConsole *s, int ch) s->y = s->y_saved; break; default: -#ifdef DEBUG_CONSOLE - fprintf(stderr, "unhandled escape character '%c'\n", ch); -#endif + trace_console_putchar_unhandled(ch); break; } break; @@ -53,6 +53,7 @@ #include <vte/vte.h> #include <math.h> +#include "trace.h" #include "ui/console.h" #include "sysemu/sysemu.h" #include "qmp-commands.h" @@ -60,14 +61,6 @@ #include "keymaps.h" #include "sysemu/char.h" -//#define DEBUG_GTK - -#ifdef DEBUG_GTK -#define DPRINTF(fmt, ...) printf(fmt, ## __VA_ARGS__) -#else -#define DPRINTF(fmt, ...) do { } while (0) -#endif - #define MAX_VCS 10 @@ -302,7 +295,7 @@ static void gd_update(DisplayChangeListener *dcl, int fbw, fbh; int ww, wh; - DPRINTF("update(x=%d, y=%d, w=%d, h=%d)\n", x, y, w, h); + trace_gd_update(x, y, w, h); if (s->convert) { pixman_image_composite(PIXMAN_OP_SRC, s->ds->image, NULL, s->convert, @@ -396,8 +389,7 @@ static void gd_switch(DisplayChangeListener *dcl, GtkDisplayState *s = container_of(dcl, GtkDisplayState, dcl); bool resized = true; - DPRINTF("resize(width=%d, height=%d)\n", - surface_width(surface), surface_height(surface)); + trace_gd_switch(surface_width(surface), surface_height(surface)); if (s->surface) { cairo_surface_destroy(s->surface); @@ -732,9 +724,8 @@ static gboolean gd_key_event(GtkWidget *widget, GdkEventKey *key, void *opaque) qemu_keycode = 0; } - DPRINTF("translated GDK keycode %d to QEMU keycode %d (%s)\n", - gdk_keycode, qemu_keycode, - (key->type == GDK_KEY_PRESS) ? "down" : "up"); + trace_gd_key_event(gdk_keycode, qemu_keycode, + (key->type == GDK_KEY_PRESS) ? "down" : "up"); for (i = 0; i < ARRAY_SIZE(modifier_keycode); i++) { if (qemu_keycode == modifier_keycode[i]) { diff --git a/ui/input.c b/ui/input.c index 10d8c056f1..1c70f60e0d 100644 --- a/ui/input.c +++ b/ui/input.c @@ -414,7 +414,7 @@ void kbd_put_keycode(int keycode) if (!runstate_is_running() && !runstate_check(RUN_STATE_SUSPENDED)) { return; } - if (entry) { + if (entry && entry->put_kbd) { entry->put_kbd(entry->opaque, keycode); } } diff --git a/util/Makefile.objs b/util/Makefile.objs index 2bb13a2a59..af3e5cb157 100644 --- a/util/Makefile.objs +++ b/util/Makefile.objs @@ -12,3 +12,4 @@ util-obj-y += qemu-option.o qemu-progress.o util-obj-y += hexdump.o util-obj-y += crc32c.o util-obj-y += throttle.o +util-obj-y += getauxval.o diff --git a/util/cache-utils.c b/util/cache-utils.c index b94013a8cb..047003008b 100644 --- a/util/cache-utils.c +++ b/util/cache-utils.c @@ -1,3 +1,4 @@ +#include "qemu-common.h" #include "qemu/cache-utils.h" #if defined(_ARCH_PPC) @@ -9,31 +10,33 @@ struct qemu_cache_conf qemu_cache_conf = { #if defined _AIX #include <sys/systemcfg.h> -static void ppc_init_cacheline_sizes(void) +void qemu_cache_utils_init(void) { qemu_cache_conf.icache_bsize = _system_configuration.icache_line; qemu_cache_conf.dcache_bsize = _system_configuration.dcache_line; } #elif defined __linux__ +#include "qemu/osdep.h" +#include "elf.h" -#define QEMU_AT_NULL 0 -#define QEMU_AT_DCACHEBSIZE 19 -#define QEMU_AT_ICACHEBSIZE 20 - -static void ppc_init_cacheline_sizes(char **envp) +void qemu_cache_utils_init(void) { - unsigned long *auxv; - - while (*envp++); + unsigned long dsize = qemu_getauxval(AT_DCACHEBSIZE); + unsigned long isize = qemu_getauxval(AT_ICACHEBSIZE); - for (auxv = (unsigned long *) envp; *auxv != QEMU_AT_NULL; auxv += 2) { - switch (*auxv) { - case QEMU_AT_DCACHEBSIZE: qemu_cache_conf.dcache_bsize = auxv[1]; break; - case QEMU_AT_ICACHEBSIZE: qemu_cache_conf.icache_bsize = auxv[1]; break; - default: break; + if (dsize == 0 || isize == 0) { + if (dsize == 0) { + fprintf(stderr, "getauxval AT_DCACHEBSIZE failed\n"); + } + if (isize == 0) { + fprintf(stderr, "getauxval AT_ICACHEBSIZE failed\n"); } + exit(1); + } + qemu_cache_conf.dcache_bsize = dsize; + qemu_cache_conf.icache_bsize = isize; } #elif defined __APPLE__ @@ -41,7 +44,7 @@ static void ppc_init_cacheline_sizes(char **envp) #include <sys/types.h> #include <sys/sysctl.h> -static void ppc_init_cacheline_sizes(void) +void qemu_cache_utils_init(void) { size_t len; unsigned cacheline; @@ -55,9 +58,8 @@ static void ppc_init_cacheline_sizes(void) qemu_cache_conf.icache_bsize = cacheline; } } -#endif -#if defined(__FreeBSD__) || defined(__FreeBSD_kernel__) +#elif defined(__FreeBSD__) || defined(__FreeBSD_kernel__) #include <errno.h> #include <stdio.h> #include <stdlib.h> @@ -65,7 +67,7 @@ static void ppc_init_cacheline_sizes(void) #include <sys/types.h> #include <sys/sysctl.h> -static void ppc_init_cacheline_sizes(void) +void qemu_cache_utils_init(void) { size_t len = 4; unsigned cacheline; @@ -81,17 +83,4 @@ static void ppc_init_cacheline_sizes(void) } #endif -#ifdef __linux__ -void qemu_cache_utils_init(char **envp) -{ - ppc_init_cacheline_sizes(envp); -} -#else -void qemu_cache_utils_init(char **envp) -{ - (void) envp; - ppc_init_cacheline_sizes(); -} -#endif - #endif /* _ARCH_PPC */ diff --git a/util/getauxval.c b/util/getauxval.c new file mode 100644 index 0000000000..476c883b32 --- /dev/null +++ b/util/getauxval.c @@ -0,0 +1,74 @@ +/* + * QEMU access to the auxiliary vector + * + * Copyright (C) 2013 Red Hat, Inc + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#include "qemu-common.h" +#include "qemu/osdep.h" + +#ifdef CONFIG_GETAUXVAL +/* Don't inline this in qemu/osdep.h, because pulling in <sys/auxv.h> for + the system declaration of getauxval pulls in the system <elf.h>, which + conflicts with qemu's version. */ + +#include <sys/auxv.h> + +unsigned long qemu_getauxval(unsigned long key) +{ + return getauxval(key); +} +#elif defined(__linux__) +#include "elf.h" + +/* Our elf.h doesn't contain Elf32_auxv_t and Elf64_auxv_t, which is ok because + that just makes it easier to define it properly for the host here. */ +typedef struct { + unsigned long a_type; + unsigned long a_val; +} ElfW_auxv_t; + +static const ElfW_auxv_t *auxv; + +void qemu_init_auxval(char **envp) +{ + /* The auxiliary vector is located just beyond the initial environment. */ + while (*envp++ != NULL) { + continue; + } + auxv = (const ElfW_auxv_t *)envp; +} + +unsigned long qemu_getauxval(unsigned long type) +{ + /* If we were able to find the auxiliary vector, use it. */ + if (auxv) { + const ElfW_auxv_t *a; + for (a = auxv; a->a_type != 0; a++) { + if (a->a_type == type) { + return a->a_val; + } + } + } + + return 0; +} +#endif @@ -188,9 +188,7 @@ static int display_remote; const char* keyboard_layout = NULL; ram_addr_t ram_size; const char *mem_path = NULL; -#ifdef MAP_POPULATE int mem_prealloc = 0; /* force preallocation of physical target memory */ -#endif int nb_nics; NICInfo nd_table[MAX_NICS]; int autostart; @@ -428,6 +426,10 @@ static QemuOptsList qemu_machine_opts = { .name = "usb", .type = QEMU_OPT_BOOL, .help = "Set on/off to enable/disable usb", + },{ + .name = "firmware", + .type = QEMU_OPT_STRING, + .help = "firmware image", }, { /* End of list */ } }, @@ -2894,7 +2896,8 @@ int main(int argc, char **argv, char **envp) init_clocks(); rtc_clock = QEMU_CLOCK_HOST; - qemu_cache_utils_init(envp); + qemu_init_auxval(envp); + qemu_cache_utils_init(); QLIST_INIT (&vm_change_state_head); os_setup_early_signal_handling(); @@ -3206,11 +3209,9 @@ int main(int argc, char **argv, char **envp) case QEMU_OPTION_mempath: mem_path = optarg; break; -#ifdef MAP_POPULATE case QEMU_OPTION_mem_prealloc: mem_prealloc = 1; break; -#endif case QEMU_OPTION_d: log_mask = optarg; break; @@ -3229,7 +3230,7 @@ int main(int argc, char **argv, char **envp) } break; case QEMU_OPTION_bios: - bios_name = optarg; + qemu_opts_set(qemu_find_opts("machine"), 0, "firmware", optarg); break; case QEMU_OPTION_singlestep: singlestep = 1; @@ -4050,6 +4051,7 @@ int main(int argc, char **argv, char **envp) kernel_filename = qemu_opt_get(machine_opts, "kernel"); initrd_filename = qemu_opt_get(machine_opts, "initrd"); kernel_cmdline = qemu_opt_get(machine_opts, "append"); + bios_name = qemu_opt_get(machine_opts, "firmware"); boot_order = machine->default_boot_order; opts = qemu_opts_find(qemu_find_opts("boot-opts"), NULL); @@ -369,8 +369,8 @@ static int xen_remove_from_physmap(XenIOState *state, phys_offset = physmap->phys_offset; size = physmap->size; - DPRINTF("unmapping vram to %"HWADDR_PRIx" - %"HWADDR_PRIx", from ", - "%"HWADDR_PRIx"\n", phys_offset, phys_offset + size, start_addr); + DPRINTF("unmapping vram to %"HWADDR_PRIx" - %"HWADDR_PRIx", at " + "%"HWADDR_PRIx"\n", start_addr, start_addr + size, phys_offset); size >>= TARGET_PAGE_BITS; start_addr >>= TARGET_PAGE_BITS; |