From b0fb84236da4a36a852010b6b5d5d3ca497a2b68 Mon Sep 17 00:00:00 2001 From: Alexander Graf Date: Thu, 2 Jun 2011 13:53:40 +0200 Subject: [PATCH 001/209] PPC: E500: Implement reboot controller MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit When Linux reboots an e500 VM, it writes to a magic register in the "global-utilities" device indicated by the device tree. We were not emulating that device so far, rendering the VM reboot-less. This patch implements that device with only the reboot functionality implemented and adds it to the device tree. With this patch applied, I can successfully reboot a -M mpc8544ds VM. Signed-off-by: Alexander Graf Reviewed-by: Andreas Färber --- Makefile.target | 2 +- hw/mpc8544_guts.c | 135 +++++++++++++++++++++++++++++++++++++++++ hw/ppce500_mpc8544ds.c | 4 ++ pc-bios/mpc8544ds.dtb | Bin 12288 -> 2257 bytes pc-bios/mpc8544ds.dts | 6 ++ 5 files changed, 146 insertions(+), 1 deletion(-) create mode 100644 hw/mpc8544_guts.c diff --git a/Makefile.target b/Makefile.target index b1a0f6d28b..d3ebe579eb 100644 --- a/Makefile.target +++ b/Makefile.target @@ -256,7 +256,7 @@ endif obj-ppc-y += ppc4xx_devs.o ppc4xx_pci.o ppc405_uc.o ppc405_boards.o obj-ppc-y += ppc440.o ppc440_bamboo.o # PowerPC E500 boards -obj-ppc-y += ppce500_mpc8544ds.o +obj-ppc-y += ppce500_mpc8544ds.o mpc8544_guts.o # PowerPC 440 Xilinx ML507 reference board. obj-ppc-y += virtex_ml507.o obj-ppc-$(CONFIG_KVM) += kvm_ppc.o diff --git a/hw/mpc8544_guts.c b/hw/mpc8544_guts.c new file mode 100644 index 0000000000..c685f3e08c --- /dev/null +++ b/hw/mpc8544_guts.c @@ -0,0 +1,135 @@ +/* + * QEMU PowerPC MPC8544 global util pseudo-device + * + * Copyright (C) 2011 Freescale Semiconductor, Inc. All rights reserved. + * + * Author: Alexander Graf, + * + * This 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. + * + * ***************************************************************** + * + * The documentation for this device is noted in the MPC8544 documentation, + * file name "MPC8544ERM.pdf". You can easily find it on the web. + * + */ + +#include "hw.h" +#include "sysemu.h" +#include "sysbus.h" + +#define MPC8544_GUTS_MMIO_SIZE 0x1000 +#define MPC8544_GUTS_RSTCR_RESET 0x02 + +#define MPC8544_GUTS_ADDR_PORPLLSR 0x00 +#define MPC8544_GUTS_ADDR_PORBMSR 0x04 +#define MPC8544_GUTS_ADDR_PORIMPSCR 0x08 +#define MPC8544_GUTS_ADDR_PORDEVSR 0x0C +#define MPC8544_GUTS_ADDR_PORDBGMSR 0x10 +#define MPC8544_GUTS_ADDR_PORDEVSR2 0x14 +#define MPC8544_GUTS_ADDR_GPPORCR 0x20 +#define MPC8544_GUTS_ADDR_GPIOCR 0x30 +#define MPC8544_GUTS_ADDR_GPOUTDR 0x40 +#define MPC8544_GUTS_ADDR_GPINDR 0x50 +#define MPC8544_GUTS_ADDR_PMUXCR 0x60 +#define MPC8544_GUTS_ADDR_DEVDISR 0x70 +#define MPC8544_GUTS_ADDR_POWMGTCSR 0x80 +#define MPC8544_GUTS_ADDR_MCPSUMR 0x90 +#define MPC8544_GUTS_ADDR_RSTRSCR 0x94 +#define MPC8544_GUTS_ADDR_PVR 0xA0 +#define MPC8544_GUTS_ADDR_SVR 0xA4 +#define MPC8544_GUTS_ADDR_RSTCR 0xB0 +#define MPC8544_GUTS_ADDR_IOVSELSR 0xC0 +#define MPC8544_GUTS_ADDR_DDRCSR 0xB20 +#define MPC8544_GUTS_ADDR_DDRCDR 0xB24 +#define MPC8544_GUTS_ADDR_DDRCLKDR 0xB28 +#define MPC8544_GUTS_ADDR_CLKOCR 0xE00 +#define MPC8544_GUTS_ADDR_SRDS1CR1 0xF04 +#define MPC8544_GUTS_ADDR_SRDS2CR1 0xF10 +#define MPC8544_GUTS_ADDR_SRDS2CR3 0xF18 + +struct GutsState { + SysBusDevice busdev; +}; + +typedef struct GutsState GutsState; + +static uint32_t mpc8544_guts_read32(void *opaque, target_phys_addr_t addr) +{ + uint32_t value = 0; + CPUState *env = cpu_single_env; + + addr &= MPC8544_GUTS_MMIO_SIZE - 1; + switch (addr) { + case MPC8544_GUTS_ADDR_PVR: + value = env->spr[SPR_PVR]; + break; + case MPC8544_GUTS_ADDR_SVR: + value = env->spr[SPR_E500_SVR]; + break; + default: + fprintf(stderr, "guts: Unknown register read: %x\n", (int)addr); + break; + } + + return value; +} + +static CPUReadMemoryFunc * const mpc8544_guts_read[] = { + NULL, + NULL, + &mpc8544_guts_read32, +}; + +static void mpc8544_guts_write32(void *opaque, target_phys_addr_t addr, + uint32_t value) +{ + addr &= MPC8544_GUTS_MMIO_SIZE - 1; + + switch (addr) { + case MPC8544_GUTS_ADDR_RSTCR: + if (value & MPC8544_GUTS_RSTCR_RESET) { + qemu_system_reset_request(); + } + break; + default: + fprintf(stderr, "guts: Unknown register write: %x = %x\n", + (int)addr, value); + break; + } +} + +static CPUWriteMemoryFunc * const mpc8544_guts_write[] = { + NULL, + NULL, + &mpc8544_guts_write32, +}; + +static int mpc8544_guts_initfn(SysBusDevice *dev) +{ + GutsState *s; + int iomem; + + s = FROM_SYSBUS(GutsState, sysbus_from_qdev(dev)); + + iomem = cpu_register_io_memory(mpc8544_guts_read, mpc8544_guts_write, s, + DEVICE_BIG_ENDIAN); + sysbus_init_mmio(dev, MPC8544_GUTS_MMIO_SIZE, iomem); + + return 0; +} + +static SysBusDeviceInfo mpc8544_guts_info = { + .init = mpc8544_guts_initfn, + .qdev.name = "mpc8544-guts", + .qdev.size = sizeof(GutsState), +}; + +static void mpc8544_guts_register(void) +{ + sysbus_register_withprop(&mpc8544_guts_info); +} +device_init(mpc8544_guts_register); diff --git a/hw/ppce500_mpc8544ds.c b/hw/ppce500_mpc8544ds.c index 6b57fbf597..3ba8e75e2b 100644 --- a/hw/ppce500_mpc8544ds.c +++ b/hw/ppce500_mpc8544ds.c @@ -50,6 +50,7 @@ #define MPC8544_PCI_REGS_SIZE 0x1000 #define MPC8544_PCI_IO 0xE1000000 #define MPC8544_PCI_IOLEN 0x10000 +#define MPC8544_UTIL_BASE (MPC8544_CCSRBAR_BASE + 0xe0000) struct boot_info { @@ -270,6 +271,9 @@ static void mpc8544ds_init(ram_addr_t ram_size, serial_hds[0], 1, 1); } + /* General Utility device */ + sysbus_create_simple("mpc8544-guts", MPC8544_UTIL_BASE, NULL); + /* PCI */ dev = sysbus_create_varargs("e500-pcihost", MPC8544_PCI_REGS_BASE, mpic[pci_irq_nrs[0]], mpic[pci_irq_nrs[1]], diff --git a/pc-bios/mpc8544ds.dtb b/pc-bios/mpc8544ds.dtb index 3299546696bf21f53f8ce2c9eba7fcb740c547da..189224e5875e9dd0d9195c22624b85bfb29dd820 100644 GIT binary patch delta 254 zcmZojxG1P`f%o5A1_q9c3=9kw3=HfkKw1Nc1%X%qh=G7H7bvbX(NK8fZXcFgBs)q$VJkovDTp5sUCx{MlzVY^28Sx5qJ!T+ zNtKd0PEYG{D@_IA$pB`MM&G$W079^cH)ez$l2eEs)#rP_+*gHo3YTJMqG zBwZpUakiCed@SvM>esQ;EYNxd_U6{cdbiVg__RzQev7m*jT>t`E)mFIB*j_Li~Xkc z9WM;LT<7GP+#On5D|zB$lb&vuvXbj8@WNiF+cqptv7NKAYqQuJ)c3(k>IbIhI<>`) zN?jmz{B&dnAe-kqZC>D=t>lHywl-R3zOo6|^r;Up>~F#$VgCu)%^BaX`BZ#Jp$h-1 z=D$Ibg!{cK-O4|*KF(y$73nC+4onm^mq`1y*ky|GoB*1-I{iqH@V=*UGy81&RL}UU zWHj<1N<;1LSeDV}8tE~qn&4*%Kc>H#XJT9vUI1>d1eH(GU zy4LNQhsH5F`y)!3YtFrxN5*_1zpNKajOD$+n+q*5c!wJPFiTrWs$-XSFey{NNM?UNT=m8G z0X(3$;p^mU$XGS|9G3{+*v-RMl;U&HcBzg+6}CU)6V|z_)KBDDz&Xw|pZPvoXU;JqM=H)9PC?E8)1UpUd%nwtg?SFOXZglP8AL7SI9mACdrzzN&0PY5^G&jOf8cTV zkb|1LHc^LUE|D6X;}4Tu$8ZgX{ui9hv%mG#{{r@aE=I{fhssZ))GLCWP^)EcFvxVC zyS@&?TrKCpOKt7)ThUhKx~k}2wbejB4}85{9Hd%hdQS~p-}8ss4TD&_C|1FV2xI2b z#wmhG@6aEeyPN4}BOUt(iav)ko*yRu{*0e_@gDsxVpunb2YRf6xX@WPN{f7Ix~Z4x zxR?p}NnB(}80t(dR~7c0H2P@VN{3!NAVQ|u$V=VG%lGF)W; + fsl,has-rstcr; + }; }; pci0: pci@e0008000 { From 826e7b827c5f83e88d5aa84c0bf0b3459f28ec35 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Herv=C3=A9=20Poussineau?= Date: Tue, 14 Jun 2011 23:27:56 +0200 Subject: [PATCH 002/209] target-ppc: Handle memory-forced I/O controller access MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit On at least the PowerPC 601, a direct-store (T=1) with bus unit ID 0x07F is special-cased as memory-forced I/O controller access. It is supposed to be checked immediately if T=1, bypassing all protection mechanisms and acting cache-inhibited and global. Signed-off-by: Hervé Poussineau Simplified by avoiding reindentation. Added explanatory comments. Cc: Alexander Graf Signed-off-by: Andreas Färber Signed-off-by: Alexander Graf --- target-ppc/helper.c | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/target-ppc/helper.c b/target-ppc/helper.c index cf2a368b57..2944b062a5 100644 --- a/target-ppc/helper.c +++ b/target-ppc/helper.c @@ -949,8 +949,24 @@ static inline int get_segment(CPUState *env, mmu_ctx_t *ctx, ret = -3; } } else { + target_ulong sr; LOG_MMU("direct store...\n"); /* Direct-store segment : absolutely *BUGGY* for now */ + + /* Direct-store implies a 32-bit MMU. + * Check the Segment Register's bus unit ID (BUID). + */ + sr = env->sr[eaddr >> 28]; + if ((sr & 0x1FF00000) >> 20 == 0x07f) { + /* Memory-forced I/O controller interface access */ + /* If T=1 and BUID=x'07F', the 601 performs a memory access + * to SR[28-31] LA[4-31], bypassing all protection mechanisms. + */ + ctx->raddr = ((sr & 0xF) << 28) | (eaddr & 0x0FFFFFFF); + ctx->prot = PAGE_READ | PAGE_WRITE | PAGE_EXEC; + return 0; + } + switch (type) { case ACCESS_INT: /* Integer load/store : only access allowed */ From b9e17a345382ebb91446fd6cf0bb8f0eb5ba5706 Mon Sep 17 00:00:00 2001 From: Alexander Graf Date: Wed, 15 Jun 2011 23:27:19 +0200 Subject: [PATCH 003/209] PPC: calculate kernel,initrd,cmdline locations dynamically During testing, I was generating a vmlinux binary that easily occupied more than 20MB of RAM. Since the current -kernel code loads the initrd at a fixed address behind the kernel, we were overwriting kernel data when the kernel got too big. To finally get rid of the issue, let's calculate the initrd and cmdline addresses relative to the kernel size, so we can have kernels and initrds that are as big as they want to - as long as they fit in RAM. Signed-off-by: Alexander Graf --- hw/ppc_mac.h | 3 +-- hw/ppc_newworld.c | 15 +++++++++++---- hw/ppc_oldworld.c | 15 +++++++++++---- 3 files changed, 23 insertions(+), 10 deletions(-) diff --git a/hw/ppc_mac.h b/hw/ppc_mac.h index ea8759324c..68dade7e40 100644 --- a/hw/ppc_mac.h +++ b/hw/ppc_mac.h @@ -35,8 +35,7 @@ #define PROM_ADDR 0xfff00000 #define KERNEL_LOAD_ADDR 0x01000000 -#define CMDLINE_ADDR 0x027ff000 -#define INITRD_LOAD_ADDR 0x02800000 +#define KERNEL_GAP 0x00100000 #define ESCC_CLOCK 3686400 diff --git a/hw/ppc_newworld.c b/hw/ppc_newworld.c index 86f1cfbee9..5bce709bab 100644 --- a/hw/ppc_newworld.c +++ b/hw/ppc_newworld.c @@ -120,6 +120,11 @@ static uint64_t translate_kernel_address(void *opaque, uint64_t addr) return (addr & 0x0fffffff) + KERNEL_LOAD_ADDR; } +static target_phys_addr_t round_page(target_phys_addr_t addr) +{ + return (addr + TARGET_PAGE_SIZE - 1) & TARGET_PAGE_MASK; +} + /* PowerPC Mac99 hardware initialisation */ static void ppc_core99_init (ram_addr_t ram_size, const char *boot_device, @@ -134,7 +139,7 @@ static void ppc_core99_init (ram_addr_t ram_size, int unin_memory; int linux_boot, i; ram_addr_t ram_offset, bios_offset; - uint32_t kernel_base, initrd_base; + target_phys_addr_t kernel_base, initrd_base, cmdline_base = 0; long kernel_size, initrd_size; PCIBus *pci_bus; MacIONVRAMState *nvr; @@ -220,7 +225,7 @@ static void ppc_core99_init (ram_addr_t ram_size, } /* load initrd */ if (initrd_filename) { - initrd_base = INITRD_LOAD_ADDR; + initrd_base = round_page(kernel_base + kernel_size + KERNEL_GAP); initrd_size = load_image_targphys(initrd_filename, initrd_base, ram_size - initrd_base); if (initrd_size < 0) { @@ -228,9 +233,11 @@ static void ppc_core99_init (ram_addr_t ram_size, initrd_filename); exit(1); } + cmdline_base = round_page(initrd_base + initrd_size); } else { initrd_base = 0; initrd_size = 0; + cmdline_base = round_page(kernel_base + kernel_size + KERNEL_GAP); } ppc_boot_device = 'm'; } else { @@ -373,8 +380,8 @@ static void ppc_core99_init (ram_addr_t ram_size, fw_cfg_add_i32(fw_cfg, FW_CFG_KERNEL_ADDR, kernel_base); fw_cfg_add_i32(fw_cfg, FW_CFG_KERNEL_SIZE, kernel_size); if (kernel_cmdline) { - fw_cfg_add_i32(fw_cfg, FW_CFG_KERNEL_CMDLINE, CMDLINE_ADDR); - pstrcpy_targphys("cmdline", CMDLINE_ADDR, TARGET_PAGE_SIZE, kernel_cmdline); + fw_cfg_add_i32(fw_cfg, FW_CFG_KERNEL_CMDLINE, cmdline_base); + pstrcpy_targphys("cmdline", cmdline_base, TARGET_PAGE_SIZE, kernel_cmdline); } else { fw_cfg_add_i32(fw_cfg, FW_CFG_KERNEL_CMDLINE, 0); } diff --git a/hw/ppc_oldworld.c b/hw/ppc_oldworld.c index 75a312742e..20cd8e1a8d 100644 --- a/hw/ppc_oldworld.c +++ b/hw/ppc_oldworld.c @@ -59,6 +59,11 @@ static uint64_t translate_kernel_address(void *opaque, uint64_t addr) return (addr & 0x0fffffff) + KERNEL_LOAD_ADDR; } +static target_phys_addr_t round_page(target_phys_addr_t addr) +{ + return (addr + TARGET_PAGE_SIZE - 1) & TARGET_PAGE_MASK; +} + static void ppc_heathrow_init (ram_addr_t ram_size, const char *boot_device, const char *kernel_filename, @@ -71,7 +76,7 @@ static void ppc_heathrow_init (ram_addr_t ram_size, qemu_irq *pic, **heathrow_irqs; int linux_boot, i; ram_addr_t ram_offset, bios_offset; - uint32_t kernel_base, initrd_base; + uint32_t kernel_base, initrd_base, cmdline_base = 0; int32_t kernel_size, initrd_size; PCIBus *pci_bus; MacIONVRAMState *nvr; @@ -157,7 +162,7 @@ static void ppc_heathrow_init (ram_addr_t ram_size, } /* load initrd */ if (initrd_filename) { - initrd_base = INITRD_LOAD_ADDR; + initrd_base = round_page(kernel_base + kernel_size + KERNEL_GAP); initrd_size = load_image_targphys(initrd_filename, initrd_base, ram_size - initrd_base); if (initrd_size < 0) { @@ -165,9 +170,11 @@ static void ppc_heathrow_init (ram_addr_t ram_size, initrd_filename); exit(1); } + cmdline_base = round_page(initrd_base + initrd_size); } else { initrd_base = 0; initrd_size = 0; + cmdline_base = round_page(kernel_base + kernel_size + KERNEL_GAP); } ppc_boot_device = 'm'; } else { @@ -278,8 +285,8 @@ static void ppc_heathrow_init (ram_addr_t ram_size, fw_cfg_add_i32(fw_cfg, FW_CFG_KERNEL_ADDR, kernel_base); fw_cfg_add_i32(fw_cfg, FW_CFG_KERNEL_SIZE, kernel_size); if (kernel_cmdline) { - fw_cfg_add_i32(fw_cfg, FW_CFG_KERNEL_CMDLINE, CMDLINE_ADDR); - pstrcpy_targphys("cmdline", CMDLINE_ADDR, TARGET_PAGE_SIZE, kernel_cmdline); + fw_cfg_add_i32(fw_cfg, FW_CFG_KERNEL_CMDLINE, cmdline_base); + pstrcpy_targphys("cmdline", cmdline_base, TARGET_PAGE_SIZE, kernel_cmdline); } else { fw_cfg_add_i32(fw_cfg, FW_CFG_KERNEL_CMDLINE, 0); } From 5de6b46db51cb34857c8bfaef355e048fbc6e020 Mon Sep 17 00:00:00 2001 From: Alexander Graf Date: Wed, 15 Jun 2011 23:34:04 +0200 Subject: [PATCH 004/209] PPC: mpc8544ds: Add hypervisor node When running a PPC guest with KVM that can do PV operations, we need to indicate the guest which instructions to use for a hypercall and that it is running as KVM guest. This logic was available on openbios based machines already. This patch also adds said functionality to the mpc8544ds machine. Signed-off-by: Alexander Graf Acked-by: Scott Wood --- hw/ppce500_mpc8544ds.c | 21 +++++++++++++++------ pc-bios/mpc8544ds.dtb | Bin 2257 -> 2277 bytes pc-bios/mpc8544ds.dts | 3 +++ 3 files changed, 18 insertions(+), 6 deletions(-) diff --git a/hw/ppce500_mpc8544ds.c b/hw/ppce500_mpc8544ds.c index 3ba8e75e2b..073de3c052 100644 --- a/hw/ppce500_mpc8544ds.c +++ b/hw/ppce500_mpc8544ds.c @@ -82,11 +82,12 @@ out: } #endif -static int mpc8544_load_device_tree(target_phys_addr_t addr, - uint32_t ramsize, - target_phys_addr_t initrd_base, - target_phys_addr_t initrd_size, - const char *kernel_cmdline) +static int mpc8544_load_device_tree(CPUState *env, + target_phys_addr_t addr, + uint32_t ramsize, + target_phys_addr_t initrd_base, + target_phys_addr_t initrd_size, + const char *kernel_cmdline) { int ret = -1; #ifdef CONFIG_FDT @@ -94,6 +95,7 @@ static int mpc8544_load_device_tree(target_phys_addr_t addr, char *filename; int fdt_size; void *fdt; + uint8_t hypercall[16]; filename = qemu_find_file(QEMU_FILE_TYPE_BIOS, BINARY_DEVICE_TREE_FILE); if (!filename) { @@ -157,6 +159,13 @@ static int mpc8544_load_device_tree(target_phys_addr_t addr, mpc8544_copy_soc_cell(fdt, buf, "clock-frequency"); mpc8544_copy_soc_cell(fdt, buf, "timebase-frequency"); + + /* indicate KVM hypercall interface */ + qemu_devtree_setprop_string(fdt, "/hypervisor", "compatible", + "linux,kvm"); + kvmppc_get_hypercall(env, hypercall, sizeof(hypercall)); + qemu_devtree_setprop(fdt, "/hypervisor", "hcall-instructions", + hypercall, sizeof(hypercall)); } else { const uint32_t freq = 400000000; @@ -330,7 +339,7 @@ static void mpc8544ds_init(ram_addr_t ram_size, cpu_abort(env, "Compiled without FDT support - can't load kernel\n"); #endif dt_base = (kernel_size + DTC_LOAD_PAD) & ~DTC_PAD_MASK; - if (mpc8544_load_device_tree(dt_base, ram_size, + if (mpc8544_load_device_tree(env, dt_base, ram_size, initrd_base, initrd_size, kernel_cmdline) < 0) { fprintf(stderr, "couldn't load device tree\n"); exit(1); diff --git a/pc-bios/mpc8544ds.dtb b/pc-bios/mpc8544ds.dtb index 189224e5875e9dd0d9195c22624b85bfb29dd820..ae318b1fe83846cc2e133951a3666fcfcdf87f79 100644 GIT binary patch delta 47 zcmca8_*78f0`I@K3=AAk85kHW7#P?qCJKl%I&9Q1XXj?js4Pe=D$6X+FWMZ&Ud;#q DUc3%i delta 35 rcmaDVcu`Q`0`I@K3=A9>85kHW7#P@7CJKl%>TJ|7XWv}RUc?9h$W95# diff --git a/pc-bios/mpc8544ds.dts b/pc-bios/mpc8544ds.dts index fd792d5f88..a88b47c11b 100644 --- a/pc-bios/mpc8544ds.dts +++ b/pc-bios/mpc8544ds.dts @@ -125,4 +125,7 @@ chosen { linux,stdout-path = "/soc8544@e0000000/serial@4500"; }; + + hypervisor { + }; }; From a59106cbbb53917352b3e76c5551b65432878e19 Mon Sep 17 00:00:00 2001 From: Alexander Graf Date: Fri, 17 Jun 2011 02:56:56 +0200 Subject: [PATCH 005/209] PPC: update openbios firmware There were some changes upstream to account for broken usage of mtmsr, so before applying the mtmsr patch we need to update OpenBIOS, otherwise the PPC target would break. Signed-off-by: Alexander Graf --- pc-bios/README | 6 ++++-- pc-bios/openbios-ppc | Bin 729876 -> 750392 bytes 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/pc-bios/README b/pc-bios/README index fe221a940f..40568f82aa 100644 --- a/pc-bios/README +++ b/pc-bios/README @@ -10,8 +10,10 @@ - OpenBIOS (http://www.openbios.org/) is a free (GPL v2) portable firmware implementation. The goal is to implement a 100% IEEE 1275-1994 (referred to as Open Firmware) compliant firmware. - The included image for PowerPC (for 32 and 64 bit PPC CPUs), Sparc32 - and Sparc64 are built from OpenBIOS SVN revision 1018. + The included image for PowerPC (for 32 and 64 bit PPC CPUs), + + PowerPC is built from OpenBIOS SVN revision 1044 + Sparc32 and Sparc64 are built from OpenBIOS SVN revision 1018. - SLOF (Slimline Open Firmware) is a free IEEE 1275 Open Firmware implementation for certain IBM POWER hardware. The sources are at diff --git a/pc-bios/openbios-ppc b/pc-bios/openbios-ppc index ee6f5ae3b93a0570ff1eea5998716c361b15e989..6525a9199a312613f376fe9dbc492a186bebcf7b 100644 GIT binary patch literal 750392 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𝔱X{!d>vwZ)RrA6G**o#l8CGq$F`tnRHMEz?fq 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

kQ+u610yQq%)U%bAW3Csa?1?zs?-Zzh#i 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�s=1M(HCd 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 J{O_3F{{k(@i$nkb literal 729876 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 EzXA+_y#N3J From 8018dc63aab936f1a5cff6e707289116ea97c423 Mon Sep 17 00:00:00 2001 From: Alexander Graf Date: Thu, 16 Jun 2011 10:44:23 +0200 Subject: [PATCH 006/209] PPC: Only set lower 32bits with mtmsr As Nathan pointed out correctly, the mtmsr instruction does not modify the high 32 bits of MSR. It also doesn't matter if SF is set or not, the instruction always behaves the same. This patch moves it a bit closer to the spec. Reported-by: Nathan Whitehorn Signed-off-by: Alexander Graf --- target-ppc/translate.c | 17 ++++++----------- 1 file changed, 6 insertions(+), 11 deletions(-) diff --git a/target-ppc/translate.c b/target-ppc/translate.c index 59aef855d4..7e318e397a 100644 --- a/target-ppc/translate.c +++ b/target-ppc/translate.c @@ -3878,24 +3878,19 @@ static void gen_mtmsr(DisasContext *ctx) tcg_gen_or_tl(cpu_msr, cpu_msr, t0); tcg_temp_free(t0); } else { + TCGv msr = tcg_temp_new(); + /* XXX: we need to update nip before the store * if we enter power saving mode, we will exit the loop * directly from ppc_store_msr */ gen_update_nip(ctx, ctx->nip); #if defined(TARGET_PPC64) - if (!ctx->sf_mode) { - TCGv t0 = tcg_temp_new(); - TCGv t1 = tcg_temp_new(); - tcg_gen_andi_tl(t0, cpu_msr, 0xFFFFFFFF00000000ULL); - tcg_gen_ext32u_tl(t1, cpu_gpr[rS(ctx->opcode)]); - tcg_gen_or_tl(t0, t0, t1); - tcg_temp_free(t1); - gen_helper_store_msr(t0); - tcg_temp_free(t0); - } else + tcg_gen_deposit_tl(msr, cpu_msr, cpu_gpr[rS(ctx->opcode)], 0, 32); +#else + tcg_gen_mov_tl(msr, cpu_gpr[rS(ctx->opcode)]); #endif - gen_helper_store_msr(cpu_gpr[rS(ctx->opcode)]); + gen_helper_store_msr(msr); /* Must stop the translation as machine state (may have) changed */ /* Note that mtmsr is not always defined as context-synchronizing */ gen_stop_exception(ctx); From d1e256fe47be3dd43f38a8ec50f860506f975baf Mon Sep 17 00:00:00 2001 From: Alexander Graf Date: Thu, 16 Jun 2011 18:45:43 +0200 Subject: [PATCH 007/209] PPC: E500: Use MAS registers instead of internal TLB representation The natural format for e500 cores to do TLB manipulation with are the MAS registers. Instead of converting them into some internal representation and back again when the guest reads them, we can just keep the data identical to the way the guest passed it to us. The main advantage of this approach is that we're getting closer to being able to share MMU data with KVM using shared memory, so that we don't need to copy lots of MMU data back and forth all the time. For this to work however, another patch is required that gets rid of the TLB union, as that destroys our memory layout that needs to be identical with the kernel one. Signed-off-by: Alexander Graf --- hw/ppce500_mpc8544ds.c | 19 +++-- target-ppc/cpu.h | 32 ++++++--- target-ppc/helper.c | 157 ++++++++++++++++++++++++++++++++++++++--- target-ppc/op_helper.c | 148 +++++++++----------------------------- 4 files changed, 213 insertions(+), 143 deletions(-) diff --git a/hw/ppce500_mpc8544ds.c b/hw/ppce500_mpc8544ds.c index 073de3c052..b739ce27ed 100644 --- a/hw/ppce500_mpc8544ds.c +++ b/hw/ppce500_mpc8544ds.c @@ -185,18 +185,23 @@ out: } /* Create -kernel TLB entries for BookE, linearly spanning 256MB. */ +static inline target_phys_addr_t booke206_page_size_to_tlb(uint64_t size) +{ + return (ffs(size >> 10) - 1) >> 1; +} + static void mmubooke_create_initial_mapping(CPUState *env, target_ulong va, target_phys_addr_t pa) { - ppcemb_tlb_t *tlb = booke206_get_tlbe(env, 1, 0, 0); + ppcmas_tlb_t *tlb = booke206_get_tlbm(env, 1, 0, 0); + target_phys_addr_t size; - tlb->attr = 0; - tlb->prot = PAGE_VALID | ((PAGE_READ | PAGE_WRITE | PAGE_EXEC) << 4); - tlb->size = 256 * 1024 * 1024; - tlb->EPN = va & TARGET_PAGE_MASK; - tlb->RPN = pa & TARGET_PAGE_MASK; - tlb->PID = 0; + size = (booke206_page_size_to_tlb(256 * 1024 * 1024) << MAS1_TSIZE_SHIFT); + tlb->mas1 = MAS1_VALID | size; + tlb->mas2 = va & TARGET_PAGE_MASK; + tlb->mas7_3 = pa & TARGET_PAGE_MASK; + tlb->mas7_3 |= MAS3_UR | MAS3_UW | MAS3_UX | MAS3_SR | MAS3_SW | MAS3_SX; } static void mpc8544ds_cpu_reset(void *opaque) diff --git a/target-ppc/cpu.h b/target-ppc/cpu.h index 8e4582f6ab..758c5549af 100644 --- a/target-ppc/cpu.h +++ b/target-ppc/cpu.h @@ -360,9 +360,17 @@ struct ppcemb_tlb_t { uint32_t attr; /* Storage attributes */ }; +typedef struct ppcmas_tlb_t { + uint32_t mas8; + uint32_t mas1; + uint64_t mas2; + uint64_t mas7_3; +} ppcmas_tlb_t; + union ppc_tlb_t { ppc6xx_tlb_t tlb6; ppcemb_tlb_t tlbe; + ppcmas_tlb_t tlbm; }; #endif @@ -1075,9 +1083,13 @@ void store_40x_sler (CPUPPCState *env, uint32_t val); void store_booke_tcr (CPUPPCState *env, target_ulong val); void store_booke_tsr (CPUPPCState *env, target_ulong val); void booke206_flush_tlb(CPUState *env, int flags, const int check_iprot); +target_phys_addr_t booke206_tlb_to_page_size(CPUState *env, ppcmas_tlb_t *tlb); int ppcemb_tlb_check(CPUState *env, ppcemb_tlb_t *tlb, target_phys_addr_t *raddrp, target_ulong address, uint32_t pid, int ext, int i); +int ppcmas_tlb_check(CPUState *env, ppcmas_tlb_t *tlb, + target_phys_addr_t *raddrp, target_ulong address, + uint32_t pid); void ppc_tlb_invalidate_all (CPUPPCState *env); void ppc_tlb_invalidate_one (CPUPPCState *env, target_ulong addr); #if defined(TARGET_PPC64) @@ -1927,12 +1939,12 @@ static inline void cpu_set_tls(CPUState *env, target_ulong newtls) } #if !defined(CONFIG_USER_ONLY) -static inline int booke206_tlbe_id(CPUState *env, ppcemb_tlb_t *tlbe) +static inline int booke206_tlbm_id(CPUState *env, ppcmas_tlb_t *tlbm) { - uintptr_t tlbel = (uintptr_t)tlbe; + uintptr_t tlbml = (uintptr_t)tlbm; uintptr_t tlbl = (uintptr_t)env->tlb; - return (tlbel - tlbl) / sizeof(env->tlb[0]); + return (tlbml - tlbl) / sizeof(env->tlb[0]); } static inline int booke206_tlb_size(CPUState *env, int tlbn) @@ -1949,9 +1961,9 @@ static inline int booke206_tlb_ways(CPUState *env, int tlbn) return r; } -static inline int booke206_tlbe_to_tlbn(CPUState *env, ppcemb_tlb_t *tlbe) +static inline int booke206_tlbm_to_tlbn(CPUState *env, ppcmas_tlb_t *tlbm) { - int id = booke206_tlbe_id(env, tlbe); + int id = booke206_tlbm_id(env, tlbm); int end = 0; int i; @@ -1966,14 +1978,14 @@ static inline int booke206_tlbe_to_tlbn(CPUState *env, ppcemb_tlb_t *tlbe) return 0; } -static inline int booke206_tlbe_to_way(CPUState *env, ppcemb_tlb_t *tlb) +static inline int booke206_tlbm_to_way(CPUState *env, ppcmas_tlb_t *tlb) { - int tlbn = booke206_tlbe_to_tlbn(env, tlb); - int tlbid = booke206_tlbe_id(env, tlb); + int tlbn = booke206_tlbm_to_tlbn(env, tlb); + int tlbid = booke206_tlbm_id(env, tlb); return tlbid & (booke206_tlb_ways(env, tlbn) - 1); } -static inline ppcemb_tlb_t *booke206_get_tlbe(CPUState *env, const int tlbn, +static inline ppcmas_tlb_t *booke206_get_tlbm(CPUState *env, const int tlbn, target_ulong ea, int way) { int r; @@ -1992,7 +2004,7 @@ static inline ppcemb_tlb_t *booke206_get_tlbe(CPUState *env, const int tlbn, r += booke206_tlb_size(env, i); } - return &env->tlb[r].tlbe; + return &env->tlb[r].tlbm; } #endif diff --git a/target-ppc/helper.c b/target-ppc/helper.c index 2944b062a5..8cf9ee1182 100644 --- a/target-ppc/helper.c +++ b/target-ppc/helper.c @@ -1279,8 +1279,8 @@ void booke206_flush_tlb(CPUState *env, int flags, const int check_iprot) if (flags & (1 << i)) { tlb_size = booke206_tlb_size(env, i); for (j = 0; j < tlb_size; j++) { - if (!check_iprot || !(tlb[j].tlbe.attr & MAS1_IPROT)) { - tlb[j].tlbe.prot = 0; + if (!check_iprot || !(tlb[j].tlbm.mas1 & MAS1_IPROT)) { + tlb[j].tlbm.mas1 &= ~MAS1_VALID; } } } @@ -1290,11 +1290,148 @@ void booke206_flush_tlb(CPUState *env, int flags, const int check_iprot) tlb_flush(env, 1); } -static int mmubooke206_get_physical_address(CPUState *env, mmu_ctx_t *ctx, - target_ulong address, int rw, - int access_type) +target_phys_addr_t booke206_tlb_to_page_size(CPUState *env, ppcmas_tlb_t *tlb) { - ppcemb_tlb_t *tlb; + uint32_t tlbncfg; + int tlbn = booke206_tlbm_to_tlbn(env, tlb); + target_phys_addr_t tlbm_size; + + tlbncfg = env->spr[SPR_BOOKE_TLB0CFG + tlbn]; + + if (tlbncfg & TLBnCFG_AVAIL) { + tlbm_size = (tlb->mas1 & MAS1_TSIZE_MASK) >> MAS1_TSIZE_SHIFT; + } else { + tlbm_size = (tlbncfg & TLBnCFG_MINSIZE) >> TLBnCFG_MINSIZE_SHIFT; + } + + return (1 << (tlbm_size << 1)) << 10; +} + +/* TLB check function for MAS based SoftTLBs */ +int ppcmas_tlb_check(CPUState *env, ppcmas_tlb_t *tlb, + target_phys_addr_t *raddrp, + target_ulong address, uint32_t pid) +{ + target_ulong mask; + uint32_t tlb_pid; + + /* Check valid flag */ + if (!(tlb->mas1 & MAS1_VALID)) { + return -1; + } + + mask = ~(booke206_tlb_to_page_size(env, tlb) - 1); + LOG_SWTLB("%s: TLB ADDR=0x" TARGET_FMT_lx " PID=0x%x MAS1=0x%x MAS2=0x%" + PRIx64 " mask=0x" TARGET_FMT_lx " MAS7_3=0x%" PRIx64 " MAS8=%x\n", + __func__, address, pid, tlb->mas1, tlb->mas2, mask, tlb->mas7_3, + tlb->mas8); + + /* Check PID */ + tlb_pid = (tlb->mas1 & MAS1_TID_MASK) >> MAS1_TID_SHIFT; + if (tlb_pid != 0 && tlb_pid != pid) { + return -1; + } + + /* Check effective address */ + if ((address & mask) != (tlb->mas2 & MAS2_EPN_MASK)) { + return -1; + } + *raddrp = (tlb->mas7_3 & mask) | (address & ~mask); + + return 0; +} + +static int mmubooke206_check_tlb(CPUState *env, ppcmas_tlb_t *tlb, + target_phys_addr_t *raddr, int *prot, + target_ulong address, int rw, + int access_type) +{ + int ret; + int _prot = 0; + + if (ppcmas_tlb_check(env, tlb, raddr, address, + env->spr[SPR_BOOKE_PID]) >= 0) { + goto found_tlb; + } + + if (env->spr[SPR_BOOKE_PID1] && + ppcmas_tlb_check(env, tlb, raddr, address, + env->spr[SPR_BOOKE_PID1]) >= 0) { + goto found_tlb; + } + + if (env->spr[SPR_BOOKE_PID2] && + ppcmas_tlb_check(env, tlb, raddr, address, + env->spr[SPR_BOOKE_PID2]) >= 0) { + goto found_tlb; + } + + LOG_SWTLB("%s: TLB entry not found\n", __func__); + return -1; + +found_tlb: + + if (msr_pr != 0) { + if (tlb->mas7_3 & MAS3_UR) { + _prot |= PAGE_READ; + } + if (tlb->mas7_3 & MAS3_UW) { + _prot |= PAGE_WRITE; + } + if (tlb->mas7_3 & MAS3_UX) { + _prot |= PAGE_EXEC; + } + } else { + if (tlb->mas7_3 & MAS3_SR) { + _prot |= PAGE_READ; + } + if (tlb->mas7_3 & MAS3_SW) { + _prot |= PAGE_WRITE; + } + if (tlb->mas7_3 & MAS3_SX) { + _prot |= PAGE_EXEC; + } + } + + /* Check the address space and permissions */ + if (access_type == ACCESS_CODE) { + if (msr_ir != ((tlb->mas1 & MAS1_TS) >> MAS1_TS_SHIFT)) { + LOG_SWTLB("%s: AS doesn't match\n", __func__); + return -1; + } + + *prot = _prot; + if (_prot & PAGE_EXEC) { + LOG_SWTLB("%s: good TLB!\n", __func__); + return 0; + } + + LOG_SWTLB("%s: no PAGE_EXEC: %x\n", __func__, _prot); + ret = -3; + } else { + if (msr_dr != ((tlb->mas1 & MAS1_TS) >> MAS1_TS_SHIFT)) { + LOG_SWTLB("%s: AS doesn't match\n", __func__); + return -1; + } + + *prot = _prot; + if ((!rw && _prot & PAGE_READ) || (rw && (_prot & PAGE_WRITE))) { + LOG_SWTLB("%s: found TLB!\n", __func__); + return 0; + } + + LOG_SWTLB("%s: PAGE_READ/WRITE doesn't match: %x\n", __func__, _prot); + ret = -2; + } + + return ret; +} + +static int mmubooke206_get_physical_address(CPUState *env, mmu_ctx_t *ctx, + target_ulong address, int rw, + int access_type) +{ + ppcmas_tlb_t *tlb; target_phys_addr_t raddr; int i, j, ret; @@ -1305,9 +1442,9 @@ static int mmubooke206_get_physical_address(CPUState *env, mmu_ctx_t *ctx, int ways = booke206_tlb_ways(env, i); for (j = 0; j < ways; j++) { - tlb = booke206_get_tlbe(env, i, address, j); - ret = mmubooke_check_tlb(env, tlb, &raddr, &ctx->prot, address, rw, - access_type, j); + tlb = booke206_get_tlbm(env, i, address, j); + ret = mmubooke206_check_tlb(env, tlb, &raddr, &ctx->prot, address, + rw, access_type); if (ret != -1) { goto found_tlb; } @@ -1412,7 +1549,7 @@ int get_physical_address (CPUState *env, mmu_ctx_t *ctx, target_ulong eaddr, rw, access_type); } else if (env->mmu_model == POWERPC_MMU_BOOKE206) { ret = mmubooke206_get_physical_address(env, ctx, eaddr, rw, - access_type); + access_type); } else { /* No address translation. */ ret = check_physical(env, ctx, eaddr, rw); diff --git a/target-ppc/op_helper.c b/target-ppc/op_helper.c index 15d9222c72..135211dee6 100644 --- a/target-ppc/op_helper.c +++ b/target-ppc/op_helper.c @@ -4196,7 +4196,7 @@ target_ulong helper_440_tlbsx (target_ulong address) /* PowerPC BookE 2.06 TLB management */ -static ppcemb_tlb_t *booke206_cur_tlb(CPUState *env) +static ppcmas_tlb_t *booke206_cur_tlb(CPUState *env) { uint32_t tlbncfg = 0; int esel = (env->spr[SPR_BOOKE_MAS0] & MAS0_ESEL_MASK) >> MAS0_ESEL_SHIFT; @@ -4210,17 +4210,7 @@ static ppcemb_tlb_t *booke206_cur_tlb(CPUState *env) cpu_abort(env, "we don't support HES yet\n"); } - return booke206_get_tlbe(env, tlb, ea, esel); -} - -static inline target_phys_addr_t booke206_tlb_to_page_size(int size) -{ - return (1 << (size << 1)) << 10; -} - -static inline target_phys_addr_t booke206_page_size_to_tlb(uint64_t size) -{ - return (ffs(size >> 10) - 1) >> 1; + return booke206_get_tlbm(env, tlb, ea, esel); } void helper_booke_setpid(uint32_t pidn, target_ulong pid) @@ -4233,9 +4223,7 @@ void helper_booke_setpid(uint32_t pidn, target_ulong pid) void helper_booke206_tlbwe(void) { uint32_t tlbncfg, tlbn; - ppcemb_tlb_t *tlb; - target_phys_addr_t rpn; - int tlbe_size; + ppcmas_tlb_t *tlb; switch (env->spr[SPR_BOOKE_MAS0] & MAS0_WQ_MASK) { case MAS0_WQ_ALWAYS: @@ -4269,116 +4257,43 @@ void helper_booke206_tlbwe(void) if (msr_gs) { cpu_abort(env, "missing HV implementation\n"); - } else { - rpn = ((uint64_t)env->spr[SPR_BOOKE_MAS7] << 32) | - (env->spr[SPR_BOOKE_MAS3] & 0xfffff000); } - tlb->RPN = rpn; + tlb->mas7_3 = ((uint64_t)env->spr[SPR_BOOKE_MAS7] << 32) | + env->spr[SPR_BOOKE_MAS3]; + tlb->mas1 = env->spr[SPR_BOOKE_MAS1]; + /* XXX needs to change when supporting 64-bit e500 */ + tlb->mas2 = env->spr[SPR_BOOKE_MAS2] & 0xffffffff; - tlb->PID = (env->spr[SPR_BOOKE_MAS1] & MAS1_TID_MASK) >> MAS1_TID_SHIFT; - if (tlbncfg & TLBnCFG_AVAIL) { - tlbe_size = (env->spr[SPR_BOOKE_MAS1] & MAS1_TSIZE_MASK) - >> MAS1_TSIZE_SHIFT; - } else { - tlbe_size = (tlbncfg & TLBnCFG_MINSIZE) >> TLBnCFG_MINSIZE_SHIFT; + if (!(tlbncfg & TLBnCFG_IPROT)) { + /* no IPROT supported by TLB */ + tlb->mas1 &= ~MAS1_IPROT; } - tlb->size = booke206_tlb_to_page_size(tlbe_size); - tlb->EPN = (uint32_t)(env->spr[SPR_BOOKE_MAS2] & MAS2_EPN_MASK); - tlb->attr = env->spr[SPR_BOOKE_MAS2] & (MAS2_ACM | MAS2_VLE | MAS2_W | - MAS2_I | MAS2_M | MAS2_G | MAS2_E) - << 1; - - if (tlbncfg & TLBnCFG_IPROT) { - tlb->attr |= env->spr[SPR_BOOKE_MAS1] & MAS1_IPROT; - } - tlb->attr |= (env->spr[SPR_BOOKE_MAS3] & - ((MAS3_U0 | MAS3_U1 | MAS3_U2 | MAS3_U3)) << 8); - if (env->spr[SPR_BOOKE_MAS1] & MAS1_TS) { - tlb->attr |= 1; - } - - tlb->prot = 0; - - if (env->spr[SPR_BOOKE_MAS1] & MAS1_VALID) { - tlb->prot |= PAGE_VALID; - } - if (env->spr[SPR_BOOKE_MAS3] & MAS3_UX) { - tlb->prot |= PAGE_EXEC; - } - if (env->spr[SPR_BOOKE_MAS3] & MAS3_SX) { - tlb->prot |= PAGE_EXEC << 4; - } - if (env->spr[SPR_BOOKE_MAS3] & MAS3_UW) { - tlb->prot |= PAGE_WRITE; - } - if (env->spr[SPR_BOOKE_MAS3] & MAS3_SW) { - tlb->prot |= PAGE_WRITE << 4; - } - if (env->spr[SPR_BOOKE_MAS3] & MAS3_UR) { - tlb->prot |= PAGE_READ; - } - if (env->spr[SPR_BOOKE_MAS3] & MAS3_SR) { - tlb->prot |= PAGE_READ << 4; - } - - if (tlb->size == TARGET_PAGE_SIZE) { - tlb_flush_page(env, tlb->EPN); + if (booke206_tlb_to_page_size(env, tlb) == TARGET_PAGE_SIZE) { + tlb_flush_page(env, tlb->mas2 & MAS2_EPN_MASK); } else { tlb_flush(env, 1); } } -static inline void booke206_tlb_to_mas(CPUState *env, ppcemb_tlb_t *tlb) +static inline void booke206_tlb_to_mas(CPUState *env, ppcmas_tlb_t *tlb) { - int tlbn = booke206_tlbe_to_tlbn(env, tlb); - int way = booke206_tlbe_to_way(env, tlb); + int tlbn = booke206_tlbm_to_tlbn(env, tlb); + int way = booke206_tlbm_to_way(env, tlb); env->spr[SPR_BOOKE_MAS0] = tlbn << MAS0_TLBSEL_SHIFT; env->spr[SPR_BOOKE_MAS0] |= way << MAS0_ESEL_SHIFT; - - env->spr[SPR_BOOKE_MAS1] = MAS1_VALID; - env->spr[SPR_BOOKE_MAS2] = 0; - - env->spr[SPR_BOOKE_MAS7] = (uint64_t)tlb->RPN >> 32; - env->spr[SPR_BOOKE_MAS3] = tlb->RPN; - env->spr[SPR_BOOKE_MAS1] |= tlb->PID << MAS1_TID_SHIFT; - env->spr[SPR_BOOKE_MAS1] |= booke206_page_size_to_tlb(tlb->size) - << MAS1_TSIZE_SHIFT; - env->spr[SPR_BOOKE_MAS1] |= tlb->attr & MAS1_IPROT; - if (tlb->attr & 1) { - env->spr[SPR_BOOKE_MAS1] |= MAS1_TS; - } - - env->spr[SPR_BOOKE_MAS2] = tlb->EPN; - env->spr[SPR_BOOKE_MAS2] |= (tlb->attr >> 1) & - (MAS2_ACM | MAS2_VLE | MAS2_W | MAS2_I | MAS2_M | MAS2_G | MAS2_E); - - if (tlb->prot & PAGE_EXEC) { - env->spr[SPR_BOOKE_MAS3] |= MAS3_UX; - } - if (tlb->prot & (PAGE_EXEC << 4)) { - env->spr[SPR_BOOKE_MAS3] |= MAS3_SX; - } - if (tlb->prot & PAGE_WRITE) { - env->spr[SPR_BOOKE_MAS3] |= MAS3_UW; - } - if (tlb->prot & (PAGE_WRITE << 4)) { - env->spr[SPR_BOOKE_MAS3] |= MAS3_SW; - } - if (tlb->prot & PAGE_READ) { - env->spr[SPR_BOOKE_MAS3] |= MAS3_UR; - } - if (tlb->prot & (PAGE_READ << 4)) { - env->spr[SPR_BOOKE_MAS3] |= MAS3_SR; - } - env->spr[SPR_BOOKE_MAS0] |= env->last_way << MAS0_NV_SHIFT; + + env->spr[SPR_BOOKE_MAS1] = tlb->mas1; + env->spr[SPR_BOOKE_MAS2] = tlb->mas2; + env->spr[SPR_BOOKE_MAS3] = tlb->mas7_3; + env->spr[SPR_BOOKE_MAS7] = tlb->mas7_3 >> 32; } void helper_booke206_tlbre(void) { - ppcemb_tlb_t *tlb = NULL; + ppcmas_tlb_t *tlb = NULL; tlb = booke206_cur_tlb(env); booke206_tlb_to_mas(env, tlb); @@ -4386,7 +4301,7 @@ void helper_booke206_tlbre(void) void helper_booke206_tlbsx(target_ulong address) { - ppcemb_tlb_t *tlb = NULL; + ppcmas_tlb_t *tlb = NULL; int i, j; target_phys_addr_t raddr; uint32_t spid, sas; @@ -4398,13 +4313,13 @@ void helper_booke206_tlbsx(target_ulong address) int ways = booke206_tlb_ways(env, i); for (j = 0; j < ways; j++) { - tlb = booke206_get_tlbe(env, i, address, j); + tlb = booke206_get_tlbm(env, i, address, j); - if (ppcemb_tlb_check(env, tlb, &raddr, address, spid, 0, j)) { + if (ppcmas_tlb_check(env, tlb, &raddr, address, spid)) { continue; } - if (sas != (tlb->attr & MAS6_SAS)) { + if (sas != ((tlb->mas1 & MAS1_TS) >> MAS1_TS_SHIFT)) { continue; } @@ -4439,13 +4354,14 @@ static inline void booke206_invalidate_ea_tlb(CPUState *env, int tlbn, { int i; int ways = booke206_tlb_ways(env, tlbn); + target_ulong mask; for (i = 0; i < ways; i++) { - ppcemb_tlb_t *tlb = booke206_get_tlbe(env, tlbn, ea, i); - target_phys_addr_t masked_ea = ea & ~(tlb->size - 1); - if ((tlb->EPN == (masked_ea >> MAS2_EPN_SHIFT)) && - !(tlb->attr & MAS1_IPROT)) { - tlb->prot = 0; + ppcmas_tlb_t *tlb = booke206_get_tlbm(env, tlbn, ea, i); + mask = ~(booke206_tlb_to_page_size(env, tlb) - 1); + if (((tlb->mas2 & MAS2_EPN_MASK) == (ea & mask)) && + !(tlb->mas1 & MAS1_IPROT)) { + tlb->mas1 &= ~MAS1_VALID; } } } From 0dd4bc7dd45de7afa88662d24bd50a3aafdbab64 Mon Sep 17 00:00:00 2001 From: Alexander Graf Date: Thu, 16 Jun 2011 18:49:22 +0200 Subject: [PATCH 008/209] PPC: 440: Use 440 style MMU as default, so Qemu knows the MMU type We have some KVM interaction code in Qemu that tries to be clever and ignore some capabilities when running on BookE style MMUs. Unfortunately, the default CPU bamboo was defaulting to was not a BookE-style MMU, resulting in the check to fail. With this patch, guests can run again on 440 with -enable-kvm. Signed-off-by: Alexander Graf --- hw/ppc440.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/hw/ppc440.c b/hw/ppc440.c index 1ed001a031..90abc91929 100644 --- a/hw/ppc440.c +++ b/hw/ppc440.c @@ -45,8 +45,9 @@ CPUState *ppc440ep_init(ram_addr_t *ram_size, PCIBus **pcip, qemu_irq *irqs; qemu_irq *pci_irqs; - if (cpu_model == NULL) - cpu_model = "405"; // XXX: should be 440EP + if (cpu_model == NULL) { + cpu_model = "440-Xilinx"; // XXX: should be 440EP + } env = cpu_init(cpu_model); if (!env) { fprintf(stderr, "Unable to initialize CPU!\n"); From 1c53accceeb01246aea0ec361e1efd15cac6db0f Mon Sep 17 00:00:00 2001 From: Alexander Graf Date: Fri, 17 Jun 2011 01:00:28 +0200 Subject: [PATCH 009/209] PPC: move TLBs to their own arrays Until now, we've created a union over multiple different TLB types and allocated that union. While it's a waste of memory (and cache) to allocate TLB information for a TLB type with much information when you only need little, it also inflicts another issue. With the new KVM API, we can now share the TLB between KVM and qemu, but for that to work we need to have both be in the same layout. We can't just stretch it over to fit some internal different TLB representation. Hence this patch moves all TLB types to their own array, allowing us to only address and allocate exactly the boundaries required for the specific TLB type at hand. Signed-off-by: Alexander Graf --- hw/virtex_ml507.c | 4 ++-- target-ppc/cpu.h | 21 ++++++++++++++------- target-ppc/helper.c | 26 +++++++++++++------------- target-ppc/machine.c | 16 ++++++++-------- target-ppc/op_helper.c | 12 ++++++------ target-ppc/translate_init.c | 27 ++++++++++++++++++++++++++- 6 files changed, 69 insertions(+), 37 deletions(-) diff --git a/hw/virtex_ml507.c b/hw/virtex_ml507.c index fa605158e7..7bde8c79de 100644 --- a/hw/virtex_ml507.c +++ b/hw/virtex_ml507.c @@ -60,7 +60,7 @@ static void mmubooke_create_initial_mapping(CPUState *env, target_ulong va, target_phys_addr_t pa) { - ppcemb_tlb_t *tlb = &env->tlb[0].tlbe; + ppcemb_tlb_t *tlb = &env->tlb.tlbe[0]; tlb->attr = 0; tlb->prot = PAGE_VALID | ((PAGE_READ | PAGE_WRITE | PAGE_EXEC) << 4); @@ -69,7 +69,7 @@ static void mmubooke_create_initial_mapping(CPUState *env, tlb->RPN = pa & TARGET_PAGE_MASK; tlb->PID = 0; - tlb = &env->tlb[1].tlbe; + tlb = &env->tlb.tlbe[1]; tlb->attr = 0; tlb->prot = PAGE_VALID | ((PAGE_READ | PAGE_WRITE | PAGE_EXEC) << 4); tlb->size = 1 << 31; /* up to 0xffffffff */ diff --git a/target-ppc/cpu.h b/target-ppc/cpu.h index 758c5549af..46d86be4d7 100644 --- a/target-ppc/cpu.h +++ b/target-ppc/cpu.h @@ -368,10 +368,16 @@ typedef struct ppcmas_tlb_t { } ppcmas_tlb_t; union ppc_tlb_t { - ppc6xx_tlb_t tlb6; - ppcemb_tlb_t tlbe; - ppcmas_tlb_t tlbm; + ppc6xx_tlb_t *tlb6; + ppcemb_tlb_t *tlbe; + ppcmas_tlb_t *tlbm; }; + +/* possible TLB variants */ +#define TLB_NONE 0 +#define TLB_6XX 1 +#define TLB_EMB 2 +#define TLB_MAS 3 #endif #define SDR_32_HTABORG 0xFFFF0000UL @@ -911,7 +917,8 @@ struct CPUPPCState { int last_way; /* Last used way used to allocate TLB in a LRU way */ int id_tlbs; /* If 1, MMU has separated TLBs for instructions & data */ int nb_pids; /* Number of available PID registers */ - ppc_tlb_t *tlb; /* TLB is optional. Allocate them only if needed */ + int tlb_type; /* Type of TLB we're dealing with */ + ppc_tlb_t tlb; /* TLB is optional. Allocate them only if needed */ /* 403 dedicated access protection registers */ target_ulong pb[4]; #endif @@ -1942,9 +1949,9 @@ static inline void cpu_set_tls(CPUState *env, target_ulong newtls) static inline int booke206_tlbm_id(CPUState *env, ppcmas_tlb_t *tlbm) { uintptr_t tlbml = (uintptr_t)tlbm; - uintptr_t tlbl = (uintptr_t)env->tlb; + uintptr_t tlbl = (uintptr_t)env->tlb.tlbm; - return (tlbml - tlbl) / sizeof(env->tlb[0]); + return (tlbml - tlbl) / sizeof(env->tlb.tlbm[0]); } static inline int booke206_tlb_size(CPUState *env, int tlbn) @@ -2004,7 +2011,7 @@ static inline ppcmas_tlb_t *booke206_get_tlbm(CPUState *env, const int tlbn, r += booke206_tlb_size(env, i); } - return &env->tlb[r].tlbm; + return &env->tlb.tlbm[r]; } #endif diff --git a/target-ppc/helper.c b/target-ppc/helper.c index 8cf9ee1182..38849768c5 100644 --- a/target-ppc/helper.c +++ b/target-ppc/helper.c @@ -323,7 +323,7 @@ static inline void ppc6xx_tlb_invalidate_all(CPUState *env) if (env->id_tlbs == 1) max *= 2; for (nr = 0; nr < max; nr++) { - tlb = &env->tlb[nr].tlb6; + tlb = &env->tlb.tlb6[nr]; pte_invalidate(&tlb->pte0); } tlb_flush(env, 1); @@ -340,7 +340,7 @@ static inline void __ppc6xx_tlb_invalidate_virt(CPUState *env, /* Invalidate ITLB + DTLB, all ways */ for (way = 0; way < env->nb_ways; way++) { nr = ppc6xx_tlb_getnum(env, eaddr, way, is_code); - tlb = &env->tlb[nr].tlb6; + tlb = &env->tlb.tlb6[nr]; if (pte_is_valid(tlb->pte0) && (match_epn == 0 || eaddr == tlb->EPN)) { LOG_SWTLB("TLB invalidate %d/%d " TARGET_FMT_lx "\n", nr, env->nb_tlb, eaddr); @@ -367,7 +367,7 @@ void ppc6xx_tlb_store (CPUState *env, target_ulong EPN, int way, int is_code, int nr; nr = ppc6xx_tlb_getnum(env, EPN, way, is_code); - tlb = &env->tlb[nr].tlb6; + tlb = &env->tlb.tlb6[nr]; LOG_SWTLB("Set TLB %d/%d EPN " TARGET_FMT_lx " PTE0 " TARGET_FMT_lx " PTE1 " TARGET_FMT_lx "\n", nr, env->nb_tlb, EPN, pte0, pte1); /* Invalidate any pending reference in Qemu for this virtual address */ @@ -391,7 +391,7 @@ static inline int ppc6xx_tlb_check(CPUState *env, mmu_ctx_t *ctx, for (way = 0; way < env->nb_ways; way++) { nr = ppc6xx_tlb_getnum(env, eaddr, way, access_type == ACCESS_CODE ? 1 : 0); - tlb = &env->tlb[nr].tlb6; + tlb = &env->tlb.tlb6[nr]; /* This test "emulates" the PTE index match for hardware TLBs */ if ((eaddr & TARGET_PAGE_MASK) != tlb->EPN) { LOG_SWTLB("TLB %d/%d %s [" TARGET_FMT_lx " " TARGET_FMT_lx @@ -434,7 +434,7 @@ static inline int ppc6xx_tlb_check(CPUState *env, mmu_ctx_t *ctx, LOG_SWTLB("found TLB at addr " TARGET_FMT_plx " prot=%01x ret=%d\n", ctx->raddr & TARGET_PAGE_MASK, ctx->prot, ret); /* Update page flags */ - pte_update_flags(ctx, &env->tlb[best].tlb6.pte1, ret, rw); + pte_update_flags(ctx, &env->tlb.tlb6[best].pte1, ret, rw); } return ret; @@ -1049,7 +1049,7 @@ int ppcemb_tlb_search (CPUPPCState *env, target_ulong address, uint32_t pid) /* Default return value is no match */ ret = -1; for (i = 0; i < env->nb_tlb; i++) { - tlb = &env->tlb[i].tlbe; + tlb = &env->tlb.tlbe[i]; if (ppcemb_tlb_check(env, tlb, &raddr, address, pid, 0, i) == 0) { ret = i; break; @@ -1066,7 +1066,7 @@ static inline void ppc4xx_tlb_invalidate_all(CPUState *env) int i; for (i = 0; i < env->nb_tlb; i++) { - tlb = &env->tlb[i].tlbe; + tlb = &env->tlb.tlbe[i]; tlb->prot &= ~PAGE_VALID; } tlb_flush(env, 1); @@ -1082,7 +1082,7 @@ static inline void ppc4xx_tlb_invalidate_virt(CPUState *env, int i; for (i = 0; i < env->nb_tlb; i++) { - tlb = &env->tlb[i].tlbe; + tlb = &env->tlb.tlbe[i]; if (ppcemb_tlb_check(env, tlb, &raddr, eaddr, pid, 0, i) == 0) { end = tlb->EPN + tlb->size; for (page = tlb->EPN; page < end; page += TARGET_PAGE_SIZE) @@ -1107,7 +1107,7 @@ static int mmu40x_get_physical_address (CPUState *env, mmu_ctx_t *ctx, raddr = (target_phys_addr_t)-1ULL; pr = msr_pr; for (i = 0; i < env->nb_tlb; i++) { - tlb = &env->tlb[i].tlbe; + tlb = &env->tlb.tlbe[i]; if (ppcemb_tlb_check(env, tlb, &raddr, address, env->spr[SPR_40x_PID], 0, i) < 0) continue; @@ -1248,7 +1248,7 @@ static int mmubooke_get_physical_address (CPUState *env, mmu_ctx_t *ctx, ret = -1; raddr = (target_phys_addr_t)-1ULL; for (i = 0; i < env->nb_tlb; i++) { - tlb = &env->tlb[i].tlbe; + tlb = &env->tlb.tlbe[i]; ret = mmubooke_check_tlb(env, tlb, &raddr, &ctx->prot, address, rw, access_type, i); if (!ret) { @@ -1273,14 +1273,14 @@ void booke206_flush_tlb(CPUState *env, int flags, const int check_iprot) { int tlb_size; int i, j; - ppc_tlb_t *tlb = env->tlb; + ppcmas_tlb_t *tlb = env->tlb.tlbm; for (i = 0; i < BOOKE206_MAX_TLBN; i++) { if (flags & (1 << i)) { tlb_size = booke206_tlb_size(env, i); for (j = 0; j < tlb_size; j++) { - if (!check_iprot || !(tlb[j].tlbm.mas1 & MAS1_IPROT)) { - tlb[j].tlbm.mas1 &= ~MAS1_VALID; + if (!check_iprot || !(tlb[j].mas1 & MAS1_IPROT)) { + tlb[j].mas1 &= ~MAS1_VALID; } } } diff --git a/target-ppc/machine.c b/target-ppc/machine.c index 0c1986e528..1c40d4358a 100644 --- a/target-ppc/machine.c +++ b/target-ppc/machine.c @@ -52,12 +52,12 @@ void cpu_save(QEMUFile *f, void *opaque) qemu_put_sbe32s(f, &env->last_way); qemu_put_sbe32s(f, &env->id_tlbs); qemu_put_sbe32s(f, &env->nb_pids); - if (env->tlb) { + if (env->tlb.tlb6) { // XXX assumes 6xx for (i = 0; i < env->nb_tlb; i++) { - qemu_put_betls(f, &env->tlb[i].tlb6.pte0); - qemu_put_betls(f, &env->tlb[i].tlb6.pte1); - qemu_put_betls(f, &env->tlb[i].tlb6.EPN); + qemu_put_betls(f, &env->tlb.tlb6[i].pte0); + qemu_put_betls(f, &env->tlb.tlb6[i].pte1); + qemu_put_betls(f, &env->tlb.tlb6[i].EPN); } } for (i = 0; i < 4; i++) @@ -140,12 +140,12 @@ int cpu_load(QEMUFile *f, void *opaque, int version_id) qemu_get_sbe32s(f, &env->last_way); qemu_get_sbe32s(f, &env->id_tlbs); qemu_get_sbe32s(f, &env->nb_pids); - if (env->tlb) { + if (env->tlb.tlb6) { // XXX assumes 6xx for (i = 0; i < env->nb_tlb; i++) { - qemu_get_betls(f, &env->tlb[i].tlb6.pte0); - qemu_get_betls(f, &env->tlb[i].tlb6.pte1); - qemu_get_betls(f, &env->tlb[i].tlb6.EPN); + qemu_get_betls(f, &env->tlb.tlb6[i].pte0); + qemu_get_betls(f, &env->tlb.tlb6[i].pte1); + qemu_get_betls(f, &env->tlb.tlb6[i].EPN); } } for (i = 0; i < 4; i++) diff --git a/target-ppc/op_helper.c b/target-ppc/op_helper.c index 135211dee6..82b6651925 100644 --- a/target-ppc/op_helper.c +++ b/target-ppc/op_helper.c @@ -3959,7 +3959,7 @@ target_ulong helper_4xx_tlbre_hi (target_ulong entry) int size; entry &= PPC4XX_TLB_ENTRY_MASK; - tlb = &env->tlb[entry].tlbe; + tlb = &env->tlb.tlbe[entry]; ret = tlb->EPN; if (tlb->prot & PAGE_VALID) { ret |= PPC4XX_TLBHI_V; @@ -3979,7 +3979,7 @@ target_ulong helper_4xx_tlbre_lo (target_ulong entry) target_ulong ret; entry &= PPC4XX_TLB_ENTRY_MASK; - tlb = &env->tlb[entry].tlbe; + tlb = &env->tlb.tlbe[entry]; ret = tlb->RPN; if (tlb->prot & PAGE_EXEC) { ret |= PPC4XX_TLBLO_EX; @@ -3998,7 +3998,7 @@ void helper_4xx_tlbwe_hi (target_ulong entry, target_ulong val) LOG_SWTLB("%s entry %d val " TARGET_FMT_lx "\n", __func__, (int)entry, val); entry &= PPC4XX_TLB_ENTRY_MASK; - tlb = &env->tlb[entry].tlbe; + tlb = &env->tlb.tlbe[entry]; /* Invalidate previous TLB (if it's valid) */ if (tlb->prot & PAGE_VALID) { end = tlb->EPN + tlb->size; @@ -4056,7 +4056,7 @@ void helper_4xx_tlbwe_lo (target_ulong entry, target_ulong val) LOG_SWTLB("%s entry %i val " TARGET_FMT_lx "\n", __func__, (int)entry, val); entry &= PPC4XX_TLB_ENTRY_MASK; - tlb = &env->tlb[entry].tlbe; + tlb = &env->tlb.tlbe[entry]; tlb->attr = val & PPC4XX_TLBLO_ATTR_MASK; tlb->RPN = val & PPC4XX_TLBLO_RPN_MASK; tlb->prot = PAGE_READ; @@ -4091,7 +4091,7 @@ void helper_440_tlbwe (uint32_t word, target_ulong entry, target_ulong value) __func__, word, (int)entry, value); do_flush_tlbs = 0; entry &= 0x3F; - tlb = &env->tlb[entry].tlbe; + tlb = &env->tlb.tlbe[entry]; switch (word) { default: /* Just here to please gcc */ @@ -4150,7 +4150,7 @@ target_ulong helper_440_tlbre (uint32_t word, target_ulong entry) int size; entry &= 0x3F; - tlb = &env->tlb[entry].tlbe; + tlb = &env->tlb.tlbe[entry]; switch (word) { default: /* Just here to please gcc */ diff --git a/target-ppc/translate_init.c b/target-ppc/translate_init.c index fc50ae3cd2..f542b8e4f3 100644 --- a/target-ppc/translate_init.c +++ b/target-ppc/translate_init.c @@ -844,6 +844,7 @@ static void gen_6xx_7xx_soft_tlb (CPUPPCState *env, int nb_tlbs, int nb_ways) env->nb_tlb = nb_tlbs; env->nb_ways = nb_ways; env->id_tlbs = 1; + env->tlb_type = TLB_6XX; spr_register(env, SPR_DMISS, "DMISS", SPR_NOACCESS, SPR_NOACCESS, &spr_read_generic, SPR_NOACCESS, @@ -1337,6 +1338,7 @@ static void gen_74xx_soft_tlb (CPUPPCState *env, int nb_tlbs, int nb_ways) env->nb_tlb = nb_tlbs; env->nb_ways = nb_ways; env->id_tlbs = 1; + env->tlb_type = TLB_6XX; /* XXX : not implemented */ spr_register(env, SPR_PTEHI, "PTEHI", SPR_NOACCESS, SPR_NOACCESS, @@ -3282,6 +3284,7 @@ static void init_proc_401x2 (CPUPPCState *env) env->nb_tlb = 64; env->nb_ways = 1; env->id_tlbs = 0; + env->tlb_type = TLB_EMB; #endif init_excp_4xx_softmmu(env); env->dcache_line_size = 32; @@ -3352,6 +3355,7 @@ static void init_proc_IOP480 (CPUPPCState *env) env->nb_tlb = 64; env->nb_ways = 1; env->id_tlbs = 0; + env->tlb_type = TLB_EMB; #endif init_excp_4xx_softmmu(env); env->dcache_line_size = 32; @@ -3431,6 +3435,7 @@ static void init_proc_403GCX (CPUPPCState *env) env->nb_tlb = 64; env->nb_ways = 1; env->id_tlbs = 0; + env->tlb_type = TLB_EMB; #endif init_excp_4xx_softmmu(env); env->dcache_line_size = 32; @@ -3479,6 +3484,7 @@ static void init_proc_405 (CPUPPCState *env) env->nb_tlb = 64; env->nb_ways = 1; env->id_tlbs = 0; + env->tlb_type = TLB_EMB; #endif init_excp_4xx_softmmu(env); env->dcache_line_size = 32; @@ -3561,6 +3567,7 @@ static void init_proc_440EP (CPUPPCState *env) env->nb_tlb = 64; env->nb_ways = 1; env->id_tlbs = 0; + env->tlb_type = TLB_EMB; #endif init_excp_BookE(env); env->dcache_line_size = 32; @@ -3624,6 +3631,7 @@ static void init_proc_440GP (CPUPPCState *env) env->nb_tlb = 64; env->nb_ways = 1; env->id_tlbs = 0; + env->tlb_type = TLB_EMB; #endif init_excp_BookE(env); env->dcache_line_size = 32; @@ -3687,6 +3695,7 @@ static void init_proc_440x4 (CPUPPCState *env) env->nb_tlb = 64; env->nb_ways = 1; env->id_tlbs = 0; + env->tlb_type = TLB_EMB; #endif init_excp_BookE(env); env->dcache_line_size = 32; @@ -3767,6 +3776,7 @@ static void init_proc_440x5 (CPUPPCState *env) env->nb_tlb = 64; env->nb_ways = 1; env->id_tlbs = 0; + env->tlb_type = TLB_EMB; #endif init_excp_BookE(env); env->dcache_line_size = 32; @@ -3854,6 +3864,7 @@ static void init_proc_460 (CPUPPCState *env) env->nb_tlb = 64; env->nb_ways = 1; env->id_tlbs = 0; + env->tlb_type = TLB_EMB; #endif init_excp_BookE(env); env->dcache_line_size = 32; @@ -3944,6 +3955,7 @@ static void init_proc_460F (CPUPPCState *env) env->nb_tlb = 64; env->nb_ways = 1; env->id_tlbs = 0; + env->tlb_type = TLB_EMB; #endif init_excp_BookE(env); env->dcache_line_size = 32; @@ -4251,6 +4263,7 @@ static void init_proc_e200 (CPUPPCState *env) env->nb_tlb = 64; env->nb_ways = 1; env->id_tlbs = 0; + env->tlb_type = TLB_EMB; #endif init_excp_e200(env); env->dcache_line_size = 32; @@ -4464,6 +4477,7 @@ static void init_proc_e500 (CPUPPCState *env, int version) #if !defined(CONFIG_USER_ONLY) env->nb_tlb = 0; + env->tlb_type = TLB_MAS; for (i = 0; i < BOOKE206_MAX_TLBN; i++) { env->nb_tlb += booke206_tlb_size(env, i); } @@ -9186,6 +9200,7 @@ static void init_ppc_proc (CPUPPCState *env, const ppc_def_t *def) env->nb_BATs = 0; env->nb_tlb = 0; env->nb_ways = 0; + env->tlb_type = TLB_NONE; #endif /* Register SPR common to all PowerPC implementations */ gen_spr_generic(env); @@ -9310,7 +9325,17 @@ static void init_ppc_proc (CPUPPCState *env, const ppc_def_t *def) int nb_tlb = env->nb_tlb; if (env->id_tlbs != 0) nb_tlb *= 2; - env->tlb = qemu_mallocz(nb_tlb * sizeof(ppc_tlb_t)); + switch (env->tlb_type) { + case TLB_6XX: + env->tlb.tlb6 = qemu_mallocz(nb_tlb * sizeof(ppc6xx_tlb_t)); + break; + case TLB_EMB: + env->tlb.tlbe = qemu_mallocz(nb_tlb * sizeof(ppcemb_tlb_t)); + break; + case TLB_MAS: + env->tlb.tlbm = qemu_mallocz(nb_tlb * sizeof(ppcmas_tlb_t)); + break; + } /* Pre-compute some useful values */ env->tlb_per_way = env->nb_tlb / env->nb_ways; } From 477955bd55e032374a41689bab77be5d4b0fb27a Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Wed, 25 May 2011 13:22:31 +0000 Subject: [PATCH 010/209] Revert "target-arm: Use global env in iwmmxt_helper.c helpers" This reverts commit 947a2fa21b61703802a660a938cabd7b3600ee79, returning the iwmmxt helpers to passing env in as a parameter. Signed-off-by: Peter Maydell --- target-arm/helper.h | 108 ++++++++++++++++----------------- target-arm/iwmmxt_helper.c | 80 ++++++++++++++----------- target-arm/translate.c | 119 ++++++++++++++++++++----------------- 3 files changed, 163 insertions(+), 144 deletions(-) diff --git a/target-arm/helper.h b/target-arm/helper.h index 7d5533f613..44800b1614 100644 --- a/target-arm/helper.h +++ b/target-arm/helper.h @@ -375,47 +375,47 @@ DEF_HELPER_2(iwmmxt_macsw, i64, i64, i64) DEF_HELPER_2(iwmmxt_macuw, i64, i64, i64) DEF_HELPER_1(iwmmxt_setpsr_nz, i32, i64) -#define DEF_IWMMXT_HELPER_SIZE(name) \ -DEF_HELPER_2(iwmmxt_##name##b, i64, i64, i64) \ -DEF_HELPER_2(iwmmxt_##name##w, i64, i64, i64) \ -DEF_HELPER_2(iwmmxt_##name##l, i64, i64, i64) \ +#define DEF_IWMMXT_HELPER_SIZE_ENV(name) \ +DEF_HELPER_3(iwmmxt_##name##b, i64, env, i64, i64) \ +DEF_HELPER_3(iwmmxt_##name##w, i64, env, i64, i64) \ +DEF_HELPER_3(iwmmxt_##name##l, i64, env, i64, i64) \ -DEF_IWMMXT_HELPER_SIZE(unpackl) -DEF_IWMMXT_HELPER_SIZE(unpackh) +DEF_IWMMXT_HELPER_SIZE_ENV(unpackl) +DEF_IWMMXT_HELPER_SIZE_ENV(unpackh) -DEF_HELPER_1(iwmmxt_unpacklub, i64, i64) -DEF_HELPER_1(iwmmxt_unpackluw, i64, i64) -DEF_HELPER_1(iwmmxt_unpacklul, i64, i64) -DEF_HELPER_1(iwmmxt_unpackhub, i64, i64) -DEF_HELPER_1(iwmmxt_unpackhuw, i64, i64) -DEF_HELPER_1(iwmmxt_unpackhul, i64, i64) -DEF_HELPER_1(iwmmxt_unpacklsb, i64, i64) -DEF_HELPER_1(iwmmxt_unpacklsw, i64, i64) -DEF_HELPER_1(iwmmxt_unpacklsl, i64, i64) -DEF_HELPER_1(iwmmxt_unpackhsb, i64, i64) -DEF_HELPER_1(iwmmxt_unpackhsw, i64, i64) -DEF_HELPER_1(iwmmxt_unpackhsl, i64, i64) +DEF_HELPER_2(iwmmxt_unpacklub, i64, env, i64) +DEF_HELPER_2(iwmmxt_unpackluw, i64, env, i64) +DEF_HELPER_2(iwmmxt_unpacklul, i64, env, i64) +DEF_HELPER_2(iwmmxt_unpackhub, i64, env, i64) +DEF_HELPER_2(iwmmxt_unpackhuw, i64, env, i64) +DEF_HELPER_2(iwmmxt_unpackhul, i64, env, i64) +DEF_HELPER_2(iwmmxt_unpacklsb, i64, env, i64) +DEF_HELPER_2(iwmmxt_unpacklsw, i64, env, i64) +DEF_HELPER_2(iwmmxt_unpacklsl, i64, env, i64) +DEF_HELPER_2(iwmmxt_unpackhsb, i64, env, i64) +DEF_HELPER_2(iwmmxt_unpackhsw, i64, env, i64) +DEF_HELPER_2(iwmmxt_unpackhsl, i64, env, i64) -DEF_IWMMXT_HELPER_SIZE(cmpeq) -DEF_IWMMXT_HELPER_SIZE(cmpgtu) -DEF_IWMMXT_HELPER_SIZE(cmpgts) +DEF_IWMMXT_HELPER_SIZE_ENV(cmpeq) +DEF_IWMMXT_HELPER_SIZE_ENV(cmpgtu) +DEF_IWMMXT_HELPER_SIZE_ENV(cmpgts) -DEF_IWMMXT_HELPER_SIZE(mins) -DEF_IWMMXT_HELPER_SIZE(minu) -DEF_IWMMXT_HELPER_SIZE(maxs) -DEF_IWMMXT_HELPER_SIZE(maxu) +DEF_IWMMXT_HELPER_SIZE_ENV(mins) +DEF_IWMMXT_HELPER_SIZE_ENV(minu) +DEF_IWMMXT_HELPER_SIZE_ENV(maxs) +DEF_IWMMXT_HELPER_SIZE_ENV(maxu) -DEF_IWMMXT_HELPER_SIZE(subn) -DEF_IWMMXT_HELPER_SIZE(addn) -DEF_IWMMXT_HELPER_SIZE(subu) -DEF_IWMMXT_HELPER_SIZE(addu) -DEF_IWMMXT_HELPER_SIZE(subs) -DEF_IWMMXT_HELPER_SIZE(adds) +DEF_IWMMXT_HELPER_SIZE_ENV(subn) +DEF_IWMMXT_HELPER_SIZE_ENV(addn) +DEF_IWMMXT_HELPER_SIZE_ENV(subu) +DEF_IWMMXT_HELPER_SIZE_ENV(addu) +DEF_IWMMXT_HELPER_SIZE_ENV(subs) +DEF_IWMMXT_HELPER_SIZE_ENV(adds) -DEF_HELPER_2(iwmmxt_avgb0, i64, i64, i64) -DEF_HELPER_2(iwmmxt_avgb1, i64, i64, i64) -DEF_HELPER_2(iwmmxt_avgw0, i64, i64, i64) -DEF_HELPER_2(iwmmxt_avgw1, i64, i64, i64) +DEF_HELPER_3(iwmmxt_avgb0, i64, env, i64, i64) +DEF_HELPER_3(iwmmxt_avgb1, i64, env, i64, i64) +DEF_HELPER_3(iwmmxt_avgw0, i64, env, i64, i64) +DEF_HELPER_3(iwmmxt_avgw1, i64, env, i64, i64) DEF_HELPER_2(iwmmxt_msadb, i64, i64, i64) @@ -434,26 +434,26 @@ DEF_HELPER_1(iwmmxt_msbb, i32, i64) DEF_HELPER_1(iwmmxt_msbw, i32, i64) DEF_HELPER_1(iwmmxt_msbl, i32, i64) -DEF_HELPER_2(iwmmxt_srlw, i64, i64, i32) -DEF_HELPER_2(iwmmxt_srll, i64, i64, i32) -DEF_HELPER_2(iwmmxt_srlq, i64, i64, i32) -DEF_HELPER_2(iwmmxt_sllw, i64, i64, i32) -DEF_HELPER_2(iwmmxt_slll, i64, i64, i32) -DEF_HELPER_2(iwmmxt_sllq, i64, i64, i32) -DEF_HELPER_2(iwmmxt_sraw, i64, i64, i32) -DEF_HELPER_2(iwmmxt_sral, i64, i64, i32) -DEF_HELPER_2(iwmmxt_sraq, i64, i64, i32) -DEF_HELPER_2(iwmmxt_rorw, i64, i64, i32) -DEF_HELPER_2(iwmmxt_rorl, i64, i64, i32) -DEF_HELPER_2(iwmmxt_rorq, i64, i64, i32) -DEF_HELPER_2(iwmmxt_shufh, i64, i64, i32) +DEF_HELPER_3(iwmmxt_srlw, i64, env, i64, i32) +DEF_HELPER_3(iwmmxt_srll, i64, env, i64, i32) +DEF_HELPER_3(iwmmxt_srlq, i64, env, i64, i32) +DEF_HELPER_3(iwmmxt_sllw, i64, env, i64, i32) +DEF_HELPER_3(iwmmxt_slll, i64, env, i64, i32) +DEF_HELPER_3(iwmmxt_sllq, i64, env, i64, i32) +DEF_HELPER_3(iwmmxt_sraw, i64, env, i64, i32) +DEF_HELPER_3(iwmmxt_sral, i64, env, i64, i32) +DEF_HELPER_3(iwmmxt_sraq, i64, env, i64, i32) +DEF_HELPER_3(iwmmxt_rorw, i64, env, i64, i32) +DEF_HELPER_3(iwmmxt_rorl, i64, env, i64, i32) +DEF_HELPER_3(iwmmxt_rorq, i64, env, i64, i32) +DEF_HELPER_3(iwmmxt_shufh, i64, env, i64, i32) -DEF_HELPER_2(iwmmxt_packuw, i64, i64, i64) -DEF_HELPER_2(iwmmxt_packul, i64, i64, i64) -DEF_HELPER_2(iwmmxt_packuq, i64, i64, i64) -DEF_HELPER_2(iwmmxt_packsw, i64, i64, i64) -DEF_HELPER_2(iwmmxt_packsl, i64, i64, i64) -DEF_HELPER_2(iwmmxt_packsq, i64, i64, i64) +DEF_HELPER_3(iwmmxt_packuw, i64, env, i64, i64) +DEF_HELPER_3(iwmmxt_packul, i64, env, i64, i64) +DEF_HELPER_3(iwmmxt_packuq, i64, env, i64, i64) +DEF_HELPER_3(iwmmxt_packsw, i64, env, i64, i64) +DEF_HELPER_3(iwmmxt_packsl, i64, env, i64, i64) +DEF_HELPER_3(iwmmxt_packsq, i64, env, i64, i64) DEF_HELPER_3(iwmmxt_muladdsl, i64, i64, i32, i32) DEF_HELPER_3(iwmmxt_muladdsw, i64, i64, i32, i32) diff --git a/target-arm/iwmmxt_helper.c b/target-arm/iwmmxt_helper.c index ebe6eb9fae..843994d8d5 100644 --- a/target-arm/iwmmxt_helper.c +++ b/target-arm/iwmmxt_helper.c @@ -23,7 +23,7 @@ #include #include "cpu.h" -#include "exec.h" +#include "exec-all.h" #include "helper.h" /* iwMMXt macros extracted from GNU gdb. */ @@ -162,7 +162,8 @@ uint64_t HELPER(iwmmxt_macuw)(uint64_t a, uint64_t b) SIMD64_SET(NBIT64(x), SIMD_NBIT) | \ SIMD64_SET(ZBIT64(x), SIMD_ZBIT) #define IWMMXT_OP_UNPACK(S, SH0, SH1, SH2, SH3) \ -uint64_t HELPER(glue(iwmmxt_unpack, glue(S, b)))(uint64_t a, uint64_t b) \ +uint64_t HELPER(glue(iwmmxt_unpack, glue(S, b)))(CPUState *env, \ + uint64_t a, uint64_t b) \ { \ a = \ (((a >> SH0) & 0xff) << 0) | (((b >> SH0) & 0xff) << 8) | \ @@ -176,7 +177,8 @@ uint64_t HELPER(glue(iwmmxt_unpack, glue(S, b)))(uint64_t a, uint64_t b) \ NZBIT8(a >> 48, 6) | NZBIT8(a >> 56, 7); \ return a; \ } \ -uint64_t HELPER(glue(iwmmxt_unpack, glue(S, w)))(uint64_t a, uint64_t b) \ +uint64_t HELPER(glue(iwmmxt_unpack, glue(S, w)))(CPUState *env, \ + uint64_t a, uint64_t b) \ { \ a = \ (((a >> SH0) & 0xffff) << 0) | \ @@ -188,7 +190,8 @@ uint64_t HELPER(glue(iwmmxt_unpack, glue(S, w)))(uint64_t a, uint64_t b) \ NZBIT8(a >> 32, 2) | NZBIT8(a >> 48, 3); \ return a; \ } \ -uint64_t HELPER(glue(iwmmxt_unpack, glue(S, l)))(uint64_t a, uint64_t b) \ +uint64_t HELPER(glue(iwmmxt_unpack, glue(S, l)))(CPUState *env, \ + uint64_t a, uint64_t b) \ { \ a = \ (((a >> SH0) & 0xffffffff) << 0) | \ @@ -197,7 +200,8 @@ uint64_t HELPER(glue(iwmmxt_unpack, glue(S, l)))(uint64_t a, uint64_t b) \ NZBIT32(a >> 0, 0) | NZBIT32(a >> 32, 1); \ return a; \ } \ -uint64_t HELPER(glue(iwmmxt_unpack, glue(S, ub)))(uint64_t x) \ +uint64_t HELPER(glue(iwmmxt_unpack, glue(S, ub)))(CPUState *env, \ + uint64_t x) \ { \ x = \ (((x >> SH0) & 0xff) << 0) | \ @@ -209,7 +213,8 @@ uint64_t HELPER(glue(iwmmxt_unpack, glue(S, ub)))(uint64_t x) \ NZBIT16(x >> 32, 2) | NZBIT16(x >> 48, 3); \ return x; \ } \ -uint64_t HELPER(glue(iwmmxt_unpack, glue(S, uw)))(uint64_t x) \ +uint64_t HELPER(glue(iwmmxt_unpack, glue(S, uw)))(CPUState *env, \ + uint64_t x) \ { \ x = \ (((x >> SH0) & 0xffff) << 0) | \ @@ -218,13 +223,15 @@ uint64_t HELPER(glue(iwmmxt_unpack, glue(S, uw)))(uint64_t x) \ NZBIT32(x >> 0, 0) | NZBIT32(x >> 32, 1); \ return x; \ } \ -uint64_t HELPER(glue(iwmmxt_unpack, glue(S, ul)))(uint64_t x) \ +uint64_t HELPER(glue(iwmmxt_unpack, glue(S, ul)))(CPUState *env, \ + uint64_t x) \ { \ x = (((x >> SH0) & 0xffffffff) << 0); \ env->iwmmxt.cregs[ARM_IWMMXT_wCASF] = NZBIT64(x >> 0); \ return x; \ } \ -uint64_t HELPER(glue(iwmmxt_unpack, glue(S, sb)))(uint64_t x) \ +uint64_t HELPER(glue(iwmmxt_unpack, glue(S, sb)))(CPUState *env, \ + uint64_t x) \ { \ x = \ ((uint64_t) EXTEND8H((x >> SH0) & 0xff) << 0) | \ @@ -236,7 +243,8 @@ uint64_t HELPER(glue(iwmmxt_unpack, glue(S, sb)))(uint64_t x) \ NZBIT16(x >> 32, 2) | NZBIT16(x >> 48, 3); \ return x; \ } \ -uint64_t HELPER(glue(iwmmxt_unpack, glue(S, sw)))(uint64_t x) \ +uint64_t HELPER(glue(iwmmxt_unpack, glue(S, sw)))(CPUState *env, \ + uint64_t x) \ { \ x = \ ((uint64_t) EXTEND16((x >> SH0) & 0xffff) << 0) | \ @@ -245,7 +253,8 @@ uint64_t HELPER(glue(iwmmxt_unpack, glue(S, sw)))(uint64_t x) \ NZBIT32(x >> 0, 0) | NZBIT32(x >> 32, 1); \ return x; \ } \ -uint64_t HELPER(glue(iwmmxt_unpack, glue(S, sl)))(uint64_t x) \ +uint64_t HELPER(glue(iwmmxt_unpack, glue(S, sl)))(CPUState *env, \ + uint64_t x) \ { \ x = EXTEND32((x >> SH0) & 0xffffffff); \ env->iwmmxt.cregs[ARM_IWMMXT_wCASF] = NZBIT64(x >> 0); \ @@ -255,7 +264,8 @@ IWMMXT_OP_UNPACK(l, 0, 8, 16, 24) IWMMXT_OP_UNPACK(h, 32, 40, 48, 56) #define IWMMXT_OP_CMP(SUFF, Tb, Tw, Tl, O) \ -uint64_t HELPER(glue(iwmmxt_, glue(SUFF, b)))(uint64_t a, uint64_t b) \ +uint64_t HELPER(glue(iwmmxt_, glue(SUFF, b)))(CPUState *env, \ + uint64_t a, uint64_t b) \ { \ a = \ CMP(0, Tb, O, 0xff) | CMP(8, Tb, O, 0xff) | \ @@ -269,7 +279,8 @@ uint64_t HELPER(glue(iwmmxt_, glue(SUFF, b)))(uint64_t a, uint64_t b) \ NZBIT8(a >> 48, 6) | NZBIT8(a >> 56, 7); \ return a; \ } \ -uint64_t HELPER(glue(iwmmxt_, glue(SUFF, w)))(uint64_t a, uint64_t b) \ +uint64_t HELPER(glue(iwmmxt_, glue(SUFF, w)))(CPUState *env, \ + uint64_t a, uint64_t b) \ { \ a = CMP(0, Tw, O, 0xffff) | CMP(16, Tw, O, 0xffff) | \ CMP(32, Tw, O, 0xffff) | CMP(48, Tw, O, 0xffff); \ @@ -278,7 +289,8 @@ uint64_t HELPER(glue(iwmmxt_, glue(SUFF, w)))(uint64_t a, uint64_t b) \ NZBIT16(a >> 32, 2) | NZBIT16(a >> 48, 3); \ return a; \ } \ -uint64_t HELPER(glue(iwmmxt_, glue(SUFF, l)))(uint64_t a, uint64_t b) \ +uint64_t HELPER(glue(iwmmxt_, glue(SUFF, l)))(CPUState *env, \ + uint64_t a, uint64_t b) \ { \ a = CMP(0, Tl, O, 0xffffffff) | \ CMP(32, Tl, O, 0xffffffff); \ @@ -317,7 +329,7 @@ IWMMXT_OP_CMP(adds, int8_t, int16_t, int32_t, +) #define AVGB(SHR) ((( \ ((a >> SHR) & 0xff) + ((b >> SHR) & 0xff) + round) >> 1) << SHR) #define IWMMXT_OP_AVGB(r) \ -uint64_t HELPER(iwmmxt_avgb##r)(uint64_t a, uint64_t b) \ +uint64_t HELPER(iwmmxt_avgb##r)(CPUState *env, uint64_t a, uint64_t b) \ { \ const int round = r; \ a = AVGB(0) | AVGB(8) | AVGB(16) | AVGB(24) | \ @@ -341,7 +353,7 @@ IWMMXT_OP_AVGB(1) #define AVGW(SHR) ((( \ ((a >> SHR) & 0xffff) + ((b >> SHR) & 0xffff) + round) >> 1) << SHR) #define IWMMXT_OP_AVGW(r) \ -uint64_t HELPER(iwmmxt_avgw##r)(uint64_t a, uint64_t b) \ +uint64_t HELPER(iwmmxt_avgw##r)(CPUState *env, uint64_t a, uint64_t b) \ { \ const int round = r; \ a = AVGW(0) | AVGW(16) | AVGW(32) | AVGW(48); \ @@ -452,7 +464,7 @@ uint32_t HELPER(iwmmxt_msbl)(uint64_t x) } /* FIXME: Split wCASF setting into a separate op to avoid env use. */ -uint64_t HELPER(iwmmxt_srlw)(uint64_t x, uint32_t n) +uint64_t HELPER(iwmmxt_srlw)(CPUState *env, uint64_t x, uint32_t n) { x = (((x & (0xffffll << 0)) >> n) & (0xffffll << 0)) | (((x & (0xffffll << 16)) >> n) & (0xffffll << 16)) | @@ -464,7 +476,7 @@ uint64_t HELPER(iwmmxt_srlw)(uint64_t x, uint32_t n) return x; } -uint64_t HELPER(iwmmxt_srll)(uint64_t x, uint32_t n) +uint64_t HELPER(iwmmxt_srll)(CPUState *env, uint64_t x, uint32_t n) { x = ((x & (0xffffffffll << 0)) >> n) | ((x >> n) & (0xffffffffll << 32)); @@ -473,14 +485,14 @@ uint64_t HELPER(iwmmxt_srll)(uint64_t x, uint32_t n) return x; } -uint64_t HELPER(iwmmxt_srlq)(uint64_t x, uint32_t n) +uint64_t HELPER(iwmmxt_srlq)(CPUState *env, uint64_t x, uint32_t n) { x >>= n; env->iwmmxt.cregs[ARM_IWMMXT_wCASF] = NZBIT64(x); return x; } -uint64_t HELPER(iwmmxt_sllw)(uint64_t x, uint32_t n) +uint64_t HELPER(iwmmxt_sllw)(CPUState *env, uint64_t x, uint32_t n) { x = (((x & (0xffffll << 0)) << n) & (0xffffll << 0)) | (((x & (0xffffll << 16)) << n) & (0xffffll << 16)) | @@ -492,7 +504,7 @@ uint64_t HELPER(iwmmxt_sllw)(uint64_t x, uint32_t n) return x; } -uint64_t HELPER(iwmmxt_slll)(uint64_t x, uint32_t n) +uint64_t HELPER(iwmmxt_slll)(CPUState *env, uint64_t x, uint32_t n) { x = ((x << n) & (0xffffffffll << 0)) | ((x & (0xffffffffll << 32)) << n); @@ -501,14 +513,14 @@ uint64_t HELPER(iwmmxt_slll)(uint64_t x, uint32_t n) return x; } -uint64_t HELPER(iwmmxt_sllq)(uint64_t x, uint32_t n) +uint64_t HELPER(iwmmxt_sllq)(CPUState *env, uint64_t x, uint32_t n) { x <<= n; env->iwmmxt.cregs[ARM_IWMMXT_wCASF] = NZBIT64(x); return x; } -uint64_t HELPER(iwmmxt_sraw)(uint64_t x, uint32_t n) +uint64_t HELPER(iwmmxt_sraw)(CPUState *env, uint64_t x, uint32_t n) { x = ((uint64_t) ((EXTEND16(x >> 0) >> n) & 0xffff) << 0) | ((uint64_t) ((EXTEND16(x >> 16) >> n) & 0xffff) << 16) | @@ -520,7 +532,7 @@ uint64_t HELPER(iwmmxt_sraw)(uint64_t x, uint32_t n) return x; } -uint64_t HELPER(iwmmxt_sral)(uint64_t x, uint32_t n) +uint64_t HELPER(iwmmxt_sral)(CPUState *env, uint64_t x, uint32_t n) { x = (((EXTEND32(x >> 0) >> n) & 0xffffffff) << 0) | (((EXTEND32(x >> 32) >> n) & 0xffffffff) << 32); @@ -529,14 +541,14 @@ uint64_t HELPER(iwmmxt_sral)(uint64_t x, uint32_t n) return x; } -uint64_t HELPER(iwmmxt_sraq)(uint64_t x, uint32_t n) +uint64_t HELPER(iwmmxt_sraq)(CPUState *env, uint64_t x, uint32_t n) { x = (int64_t) x >> n; env->iwmmxt.cregs[ARM_IWMMXT_wCASF] = NZBIT64(x); return x; } -uint64_t HELPER(iwmmxt_rorw)(uint64_t x, uint32_t n) +uint64_t HELPER(iwmmxt_rorw)(CPUState *env, uint64_t x, uint32_t n) { x = ((((x & (0xffffll << 0)) >> n) | ((x & (0xffffll << 0)) << (16 - n))) & (0xffffll << 0)) | @@ -552,7 +564,7 @@ uint64_t HELPER(iwmmxt_rorw)(uint64_t x, uint32_t n) return x; } -uint64_t HELPER(iwmmxt_rorl)(uint64_t x, uint32_t n) +uint64_t HELPER(iwmmxt_rorl)(CPUState *env, uint64_t x, uint32_t n) { x = ((x & (0xffffffffll << 0)) >> n) | ((x >> n) & (0xffffffffll << 32)) | @@ -563,14 +575,14 @@ uint64_t HELPER(iwmmxt_rorl)(uint64_t x, uint32_t n) return x; } -uint64_t HELPER(iwmmxt_rorq)(uint64_t x, uint32_t n) +uint64_t HELPER(iwmmxt_rorq)(CPUState *env, uint64_t x, uint32_t n) { x = (x >> n) | (x << (64 - n)); env->iwmmxt.cregs[ARM_IWMMXT_wCASF] = NZBIT64(x); return x; } -uint64_t HELPER(iwmmxt_shufh)(uint64_t x, uint32_t n) +uint64_t HELPER(iwmmxt_shufh)(CPUState *env, uint64_t x, uint32_t n) { x = (((x >> ((n << 4) & 0x30)) & 0xffff) << 0) | (((x >> ((n << 2) & 0x30)) & 0xffff) << 16) | @@ -583,7 +595,7 @@ uint64_t HELPER(iwmmxt_shufh)(uint64_t x, uint32_t n) } /* TODO: Unsigned-Saturation */ -uint64_t HELPER(iwmmxt_packuw)(uint64_t a, uint64_t b) +uint64_t HELPER(iwmmxt_packuw)(CPUState *env, uint64_t a, uint64_t b) { a = (((a >> 0) & 0xff) << 0) | (((a >> 16) & 0xff) << 8) | (((a >> 32) & 0xff) << 16) | (((a >> 48) & 0xff) << 24) | @@ -597,7 +609,7 @@ uint64_t HELPER(iwmmxt_packuw)(uint64_t a, uint64_t b) return a; } -uint64_t HELPER(iwmmxt_packul)(uint64_t a, uint64_t b) +uint64_t HELPER(iwmmxt_packul)(CPUState *env, uint64_t a, uint64_t b) { a = (((a >> 0) & 0xffff) << 0) | (((a >> 32) & 0xffff) << 16) | (((b >> 0) & 0xffff) << 32) | (((b >> 32) & 0xffff) << 48); @@ -607,7 +619,7 @@ uint64_t HELPER(iwmmxt_packul)(uint64_t a, uint64_t b) return a; } -uint64_t HELPER(iwmmxt_packuq)(uint64_t a, uint64_t b) +uint64_t HELPER(iwmmxt_packuq)(CPUState *env, uint64_t a, uint64_t b) { a = (a & 0xffffffff) | ((b & 0xffffffff) << 32); env->iwmmxt.cregs[ARM_IWMMXT_wCASF] = @@ -616,7 +628,7 @@ uint64_t HELPER(iwmmxt_packuq)(uint64_t a, uint64_t b) } /* TODO: Signed-Saturation */ -uint64_t HELPER(iwmmxt_packsw)(uint64_t a, uint64_t b) +uint64_t HELPER(iwmmxt_packsw)(CPUState *env, uint64_t a, uint64_t b) { a = (((a >> 0) & 0xff) << 0) | (((a >> 16) & 0xff) << 8) | (((a >> 32) & 0xff) << 16) | (((a >> 48) & 0xff) << 24) | @@ -630,7 +642,7 @@ uint64_t HELPER(iwmmxt_packsw)(uint64_t a, uint64_t b) return a; } -uint64_t HELPER(iwmmxt_packsl)(uint64_t a, uint64_t b) +uint64_t HELPER(iwmmxt_packsl)(CPUState *env, uint64_t a, uint64_t b) { a = (((a >> 0) & 0xffff) << 0) | (((a >> 32) & 0xffff) << 16) | (((b >> 0) & 0xffff) << 32) | (((b >> 32) & 0xffff) << 48); @@ -640,7 +652,7 @@ uint64_t HELPER(iwmmxt_packsl)(uint64_t a, uint64_t b) return a; } -uint64_t HELPER(iwmmxt_packsq)(uint64_t a, uint64_t b) +uint64_t HELPER(iwmmxt_packsq)(CPUState *env, uint64_t a, uint64_t b) { a = (a & 0xffffffff) | ((b & 0xffffffff) << 32); env->iwmmxt.cregs[ARM_IWMMXT_wCASF] = diff --git a/target-arm/translate.c b/target-arm/translate.c index f5507ec3b6..34993c43c3 100644 --- a/target-arm/translate.c +++ b/target-arm/translate.c @@ -1207,15 +1207,22 @@ static inline void gen_op_iwmmxt_##name##_M0_wRn(int rn) \ gen_helper_iwmmxt_##name(cpu_M0, cpu_M0, cpu_V1); \ } -#define IWMMXT_OP_SIZE(name) \ -IWMMXT_OP(name##b) \ -IWMMXT_OP(name##w) \ -IWMMXT_OP(name##l) +#define IWMMXT_OP_ENV(name) \ +static inline void gen_op_iwmmxt_##name##_M0_wRn(int rn) \ +{ \ + iwmmxt_load_reg(cpu_V1, rn); \ + gen_helper_iwmmxt_##name(cpu_M0, cpu_env, cpu_M0, cpu_V1); \ +} -#define IWMMXT_OP_1(name) \ +#define IWMMXT_OP_ENV_SIZE(name) \ +IWMMXT_OP_ENV(name##b) \ +IWMMXT_OP_ENV(name##w) \ +IWMMXT_OP_ENV(name##l) + +#define IWMMXT_OP_ENV1(name) \ static inline void gen_op_iwmmxt_##name##_M0(void) \ { \ - gen_helper_iwmmxt_##name(cpu_M0, cpu_M0); \ + gen_helper_iwmmxt_##name(cpu_M0, cpu_env, cpu_M0); \ } IWMMXT_OP(maddsq) @@ -1229,51 +1236,51 @@ IWMMXT_OP(muluhw) IWMMXT_OP(macsw) IWMMXT_OP(macuw) -IWMMXT_OP_SIZE(unpackl) -IWMMXT_OP_SIZE(unpackh) +IWMMXT_OP_ENV_SIZE(unpackl) +IWMMXT_OP_ENV_SIZE(unpackh) -IWMMXT_OP_1(unpacklub) -IWMMXT_OP_1(unpackluw) -IWMMXT_OP_1(unpacklul) -IWMMXT_OP_1(unpackhub) -IWMMXT_OP_1(unpackhuw) -IWMMXT_OP_1(unpackhul) -IWMMXT_OP_1(unpacklsb) -IWMMXT_OP_1(unpacklsw) -IWMMXT_OP_1(unpacklsl) -IWMMXT_OP_1(unpackhsb) -IWMMXT_OP_1(unpackhsw) -IWMMXT_OP_1(unpackhsl) +IWMMXT_OP_ENV1(unpacklub) +IWMMXT_OP_ENV1(unpackluw) +IWMMXT_OP_ENV1(unpacklul) +IWMMXT_OP_ENV1(unpackhub) +IWMMXT_OP_ENV1(unpackhuw) +IWMMXT_OP_ENV1(unpackhul) +IWMMXT_OP_ENV1(unpacklsb) +IWMMXT_OP_ENV1(unpacklsw) +IWMMXT_OP_ENV1(unpacklsl) +IWMMXT_OP_ENV1(unpackhsb) +IWMMXT_OP_ENV1(unpackhsw) +IWMMXT_OP_ENV1(unpackhsl) -IWMMXT_OP_SIZE(cmpeq) -IWMMXT_OP_SIZE(cmpgtu) -IWMMXT_OP_SIZE(cmpgts) +IWMMXT_OP_ENV_SIZE(cmpeq) +IWMMXT_OP_ENV_SIZE(cmpgtu) +IWMMXT_OP_ENV_SIZE(cmpgts) -IWMMXT_OP_SIZE(mins) -IWMMXT_OP_SIZE(minu) -IWMMXT_OP_SIZE(maxs) -IWMMXT_OP_SIZE(maxu) +IWMMXT_OP_ENV_SIZE(mins) +IWMMXT_OP_ENV_SIZE(minu) +IWMMXT_OP_ENV_SIZE(maxs) +IWMMXT_OP_ENV_SIZE(maxu) -IWMMXT_OP_SIZE(subn) -IWMMXT_OP_SIZE(addn) -IWMMXT_OP_SIZE(subu) -IWMMXT_OP_SIZE(addu) -IWMMXT_OP_SIZE(subs) -IWMMXT_OP_SIZE(adds) +IWMMXT_OP_ENV_SIZE(subn) +IWMMXT_OP_ENV_SIZE(addn) +IWMMXT_OP_ENV_SIZE(subu) +IWMMXT_OP_ENV_SIZE(addu) +IWMMXT_OP_ENV_SIZE(subs) +IWMMXT_OP_ENV_SIZE(adds) -IWMMXT_OP(avgb0) -IWMMXT_OP(avgb1) -IWMMXT_OP(avgw0) -IWMMXT_OP(avgw1) +IWMMXT_OP_ENV(avgb0) +IWMMXT_OP_ENV(avgb1) +IWMMXT_OP_ENV(avgw0) +IWMMXT_OP_ENV(avgw1) IWMMXT_OP(msadb) -IWMMXT_OP(packuw) -IWMMXT_OP(packul) -IWMMXT_OP(packuq) -IWMMXT_OP(packsw) -IWMMXT_OP(packsl) -IWMMXT_OP(packsq) +IWMMXT_OP_ENV(packuw) +IWMMXT_OP_ENV(packul) +IWMMXT_OP_ENV(packuq) +IWMMXT_OP_ENV(packsw) +IWMMXT_OP_ENV(packsl) +IWMMXT_OP_ENV(packsq) static void gen_op_iwmmxt_set_mup(void) { @@ -2007,13 +2014,13 @@ static int disas_iwmmxt_insn(CPUState *env, DisasContext *s, uint32_t insn) } switch ((insn >> 22) & 3) { case 1: - gen_helper_iwmmxt_srlw(cpu_M0, cpu_M0, tmp); + gen_helper_iwmmxt_srlw(cpu_M0, cpu_env, cpu_M0, tmp); break; case 2: - gen_helper_iwmmxt_srll(cpu_M0, cpu_M0, tmp); + gen_helper_iwmmxt_srll(cpu_M0, cpu_env, cpu_M0, tmp); break; case 3: - gen_helper_iwmmxt_srlq(cpu_M0, cpu_M0, tmp); + gen_helper_iwmmxt_srlq(cpu_M0, cpu_env, cpu_M0, tmp); break; } tcg_temp_free_i32(tmp); @@ -2035,13 +2042,13 @@ static int disas_iwmmxt_insn(CPUState *env, DisasContext *s, uint32_t insn) } switch ((insn >> 22) & 3) { case 1: - gen_helper_iwmmxt_sraw(cpu_M0, cpu_M0, tmp); + gen_helper_iwmmxt_sraw(cpu_M0, cpu_env, cpu_M0, tmp); break; case 2: - gen_helper_iwmmxt_sral(cpu_M0, cpu_M0, tmp); + gen_helper_iwmmxt_sral(cpu_M0, cpu_env, cpu_M0, tmp); break; case 3: - gen_helper_iwmmxt_sraq(cpu_M0, cpu_M0, tmp); + gen_helper_iwmmxt_sraq(cpu_M0, cpu_env, cpu_M0, tmp); break; } tcg_temp_free_i32(tmp); @@ -2063,13 +2070,13 @@ static int disas_iwmmxt_insn(CPUState *env, DisasContext *s, uint32_t insn) } switch ((insn >> 22) & 3) { case 1: - gen_helper_iwmmxt_sllw(cpu_M0, cpu_M0, tmp); + gen_helper_iwmmxt_sllw(cpu_M0, cpu_env, cpu_M0, tmp); break; case 2: - gen_helper_iwmmxt_slll(cpu_M0, cpu_M0, tmp); + gen_helper_iwmmxt_slll(cpu_M0, cpu_env, cpu_M0, tmp); break; case 3: - gen_helper_iwmmxt_sllq(cpu_M0, cpu_M0, tmp); + gen_helper_iwmmxt_sllq(cpu_M0, cpu_env, cpu_M0, tmp); break; } tcg_temp_free_i32(tmp); @@ -2091,21 +2098,21 @@ static int disas_iwmmxt_insn(CPUState *env, DisasContext *s, uint32_t insn) tcg_temp_free_i32(tmp); return 1; } - gen_helper_iwmmxt_rorw(cpu_M0, cpu_M0, tmp); + gen_helper_iwmmxt_rorw(cpu_M0, cpu_env, cpu_M0, tmp); break; case 2: if (gen_iwmmxt_shift(insn, 0x1f, tmp)) { tcg_temp_free_i32(tmp); return 1; } - gen_helper_iwmmxt_rorl(cpu_M0, cpu_M0, tmp); + gen_helper_iwmmxt_rorl(cpu_M0, cpu_env, cpu_M0, tmp); break; case 3: if (gen_iwmmxt_shift(insn, 0x3f, tmp)) { tcg_temp_free_i32(tmp); return 1; } - gen_helper_iwmmxt_rorq(cpu_M0, cpu_M0, tmp); + gen_helper_iwmmxt_rorq(cpu_M0, cpu_env, cpu_M0, tmp); break; } tcg_temp_free_i32(tmp); @@ -2239,7 +2246,7 @@ static int disas_iwmmxt_insn(CPUState *env, DisasContext *s, uint32_t insn) rd0 = (insn >> 16) & 0xf; gen_op_iwmmxt_movq_M0_wRn(rd0); tmp = tcg_const_i32(((insn >> 16) & 0xf0) | (insn & 0x0f)); - gen_helper_iwmmxt_shufh(cpu_M0, cpu_M0, tmp); + gen_helper_iwmmxt_shufh(cpu_M0, cpu_env, cpu_M0, tmp); tcg_temp_free(tmp); gen_op_iwmmxt_movq_wRn_M0(wrd); gen_op_iwmmxt_set_mup(); From 5aaebd13da29a7157b757590284664dc42ea6a69 Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Wed, 25 May 2011 15:16:10 +0000 Subject: [PATCH 011/209] target-arm: Add helper function to generate code to get fpstatus pointer Add and use a helper function which returns a TCGv which is a pointer to the fp_status for either Neon or VFP operations. Signed-off-by: Peter Maydell --- target-arm/translate.c | 40 ++++++++++++++++------------------------ 1 file changed, 16 insertions(+), 24 deletions(-) diff --git a/target-arm/translate.c b/target-arm/translate.c index 34993c43c3..e9d1549224 100644 --- a/target-arm/translate.c +++ b/target-arm/translate.c @@ -893,6 +893,19 @@ static inline void gen_add_datah_offset(DisasContext *s, unsigned int insn, } } +static TCGv_ptr get_fpstatus_ptr(int neon) +{ + TCGv_ptr statusptr = tcg_temp_new_ptr(); + int offset; + if (neon) { + offset = offsetof(CPUState, vfp.standard_fp_status); + } else { + offset = offsetof(CPUState, vfp.fp_status); + } + tcg_gen_addi_ptr(statusptr, cpu_env, offset); + return statusptr; +} + #define VFP_OP2(name) \ static inline void gen_vfp_##name(int dp) \ { \ @@ -980,14 +993,7 @@ static inline void gen_vfp_F1_ld0(int dp) #define VFP_GEN_ITOF(name) \ static inline void gen_vfp_##name(int dp, int neon) \ { \ - TCGv_ptr statusptr = tcg_temp_new_ptr(); \ - int offset; \ - if (neon) { \ - offset = offsetof(CPUState, vfp.standard_fp_status); \ - } else { \ - offset = offsetof(CPUState, vfp.fp_status); \ - } \ - tcg_gen_addi_ptr(statusptr, cpu_env, offset); \ + TCGv_ptr statusptr = get_fpstatus_ptr(neon); \ if (dp) { \ gen_helper_vfp_##name##d(cpu_F0d, cpu_F0s, statusptr); \ } else { \ @@ -1003,14 +1009,7 @@ VFP_GEN_ITOF(sito) #define VFP_GEN_FTOI(name) \ static inline void gen_vfp_##name(int dp, int neon) \ { \ - TCGv_ptr statusptr = tcg_temp_new_ptr(); \ - int offset; \ - if (neon) { \ - offset = offsetof(CPUState, vfp.standard_fp_status); \ - } else { \ - offset = offsetof(CPUState, vfp.fp_status); \ - } \ - tcg_gen_addi_ptr(statusptr, cpu_env, offset); \ + TCGv_ptr statusptr = get_fpstatus_ptr(neon); \ if (dp) { \ gen_helper_vfp_##name##d(cpu_F0s, cpu_F0d, statusptr); \ } else { \ @@ -1029,14 +1028,7 @@ VFP_GEN_FTOI(tosiz) static inline void gen_vfp_##name(int dp, int shift, int neon) \ { \ TCGv tmp_shift = tcg_const_i32(shift); \ - TCGv_ptr statusptr = tcg_temp_new_ptr(); \ - int offset; \ - if (neon) { \ - offset = offsetof(CPUState, vfp.standard_fp_status); \ - } else { \ - offset = offsetof(CPUState, vfp.fp_status); \ - } \ - tcg_gen_addi_ptr(statusptr, cpu_env, offset); \ + TCGv_ptr statusptr = get_fpstatus_ptr(neon); \ if (dp) { \ gen_helper_vfp_##name##d(cpu_F0d, cpu_F0d, tmp_shift, statusptr); \ } else { \ From ae1857eca22b58d430941730bd097e95a484652c Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Wed, 25 May 2011 14:51:48 +0000 Subject: [PATCH 012/209] target-arm: Make VFP binop helpers take pointer to fpstatus, not CPUState Make the VFP binop helper functions take a pointer to the fp status, not the entire CPUState. This will allow us to use them for Neon operations too. Signed-off-by: Peter Maydell --- target-arm/helper.c | 10 ++++++---- target-arm/helper.h | 16 ++++++++-------- target-arm/translate.c | 17 +++++++++++------ 3 files changed, 25 insertions(+), 18 deletions(-) diff --git a/target-arm/helper.c b/target-arm/helper.c index 12084167d6..9f14781d5d 100644 --- a/target-arm/helper.c +++ b/target-arm/helper.c @@ -2453,13 +2453,15 @@ void vfp_set_fpscr(CPUState *env, uint32_t val) #define VFP_HELPER(name, p) HELPER(glue(glue(vfp_,name),p)) #define VFP_BINOP(name) \ -float32 VFP_HELPER(name, s)(float32 a, float32 b, CPUState *env) \ +float32 VFP_HELPER(name, s)(float32 a, float32 b, void *fpstp) \ { \ - return float32_ ## name (a, b, &env->vfp.fp_status); \ + float_status *fpst = fpstp; \ + return float32_ ## name(a, b, fpst); \ } \ -float64 VFP_HELPER(name, d)(float64 a, float64 b, CPUState *env) \ +float64 VFP_HELPER(name, d)(float64 a, float64 b, void *fpstp) \ { \ - return float64_ ## name (a, b, &env->vfp.fp_status); \ + float_status *fpst = fpstp; \ + return float64_ ## name(a, b, fpst); \ } VFP_BINOP(add) VFP_BINOP(sub) diff --git a/target-arm/helper.h b/target-arm/helper.h index 44800b1614..40b46770d8 100644 --- a/target-arm/helper.h +++ b/target-arm/helper.h @@ -74,14 +74,14 @@ DEF_HELPER_2(set_user_reg, void, i32, i32) DEF_HELPER_1(vfp_get_fpscr, i32, env) DEF_HELPER_2(vfp_set_fpscr, void, env, i32) -DEF_HELPER_3(vfp_adds, f32, f32, f32, env) -DEF_HELPER_3(vfp_addd, f64, f64, f64, env) -DEF_HELPER_3(vfp_subs, f32, f32, f32, env) -DEF_HELPER_3(vfp_subd, f64, f64, f64, env) -DEF_HELPER_3(vfp_muls, f32, f32, f32, env) -DEF_HELPER_3(vfp_muld, f64, f64, f64, env) -DEF_HELPER_3(vfp_divs, f32, f32, f32, env) -DEF_HELPER_3(vfp_divd, f64, f64, f64, env) +DEF_HELPER_3(vfp_adds, f32, f32, f32, ptr) +DEF_HELPER_3(vfp_addd, f64, f64, f64, ptr) +DEF_HELPER_3(vfp_subs, f32, f32, f32, ptr) +DEF_HELPER_3(vfp_subd, f64, f64, f64, ptr) +DEF_HELPER_3(vfp_muls, f32, f32, f32, ptr) +DEF_HELPER_3(vfp_muld, f64, f64, f64, ptr) +DEF_HELPER_3(vfp_divs, f32, f32, f32, ptr) +DEF_HELPER_3(vfp_divd, f64, f64, f64, ptr) DEF_HELPER_1(vfp_negs, f32, f32) DEF_HELPER_1(vfp_negd, f64, f64) DEF_HELPER_1(vfp_abss, f32, f32) diff --git a/target-arm/translate.c b/target-arm/translate.c index e9d1549224..9ed747f36d 100644 --- a/target-arm/translate.c +++ b/target-arm/translate.c @@ -909,10 +909,13 @@ static TCGv_ptr get_fpstatus_ptr(int neon) #define VFP_OP2(name) \ static inline void gen_vfp_##name(int dp) \ { \ - if (dp) \ - gen_helper_vfp_##name##d(cpu_F0d, cpu_F0d, cpu_F1d, cpu_env); \ - else \ - gen_helper_vfp_##name##s(cpu_F0s, cpu_F0s, cpu_F1s, cpu_env); \ + TCGv_ptr fpst = get_fpstatus_ptr(0); \ + if (dp) { \ + gen_helper_vfp_##name##d(cpu_F0d, cpu_F0d, cpu_F1d, fpst); \ + } else { \ + gen_helper_vfp_##name##s(cpu_F0s, cpu_F0s, cpu_F1s, fpst); \ + } \ + tcg_temp_free_ptr(fpst); \ } VFP_OP2(add) @@ -925,11 +928,13 @@ VFP_OP2(div) static inline void gen_vfp_F1_mul(int dp) { /* Like gen_vfp_mul() but put result in F1 */ + TCGv_ptr fpst = get_fpstatus_ptr(0); if (dp) { - gen_helper_vfp_muld(cpu_F1d, cpu_F0d, cpu_F1d, cpu_env); + gen_helper_vfp_muld(cpu_F1d, cpu_F0d, cpu_F1d, fpst); } else { - gen_helper_vfp_muls(cpu_F1s, cpu_F0s, cpu_F1s, cpu_env); + gen_helper_vfp_muls(cpu_F1s, cpu_F0s, cpu_F1s, fpst); } + tcg_temp_free_ptr(fpst); } static inline void gen_vfp_F1_neg(int dp) From aa47cfdd178d8ea96b6e2f550742ddbb445fdacb Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Wed, 25 May 2011 13:49:19 +0000 Subject: [PATCH 013/209] target-arm: Pass fp status pointer explicitly to neon fp helpers Make the Neon helpers for various floating point operations take an explicit pointer to the float_status they use, so they don't rely on the global environment pointer any more. This also allows us to drop the mul/sub/add helpers completely and just use the vfp versions. Signed-off-by: Peter Maydell --- target-arm/helper.h | 19 +++---- target-arm/neon_helper.c | 57 +++++++++----------- target-arm/translate.c | 111 ++++++++++++++++++++++++++++----------- 3 files changed, 113 insertions(+), 74 deletions(-) diff --git a/target-arm/helper.h b/target-arm/helper.h index 40b46770d8..c29efd575f 100644 --- a/target-arm/helper.h +++ b/target-arm/helper.h @@ -350,17 +350,14 @@ DEF_HELPER_1(neon_qneg_s8, i32, i32) DEF_HELPER_1(neon_qneg_s16, i32, i32) DEF_HELPER_1(neon_qneg_s32, i32, i32) -DEF_HELPER_2(neon_min_f32, i32, i32, i32) -DEF_HELPER_2(neon_max_f32, i32, i32, i32) -DEF_HELPER_2(neon_abd_f32, i32, i32, i32) -DEF_HELPER_2(neon_add_f32, i32, i32, i32) -DEF_HELPER_2(neon_sub_f32, i32, i32, i32) -DEF_HELPER_2(neon_mul_f32, i32, i32, i32) -DEF_HELPER_2(neon_ceq_f32, i32, i32, i32) -DEF_HELPER_2(neon_cge_f32, i32, i32, i32) -DEF_HELPER_2(neon_cgt_f32, i32, i32, i32) -DEF_HELPER_2(neon_acge_f32, i32, i32, i32) -DEF_HELPER_2(neon_acgt_f32, i32, i32, i32) +DEF_HELPER_3(neon_min_f32, i32, i32, i32, ptr) +DEF_HELPER_3(neon_max_f32, i32, i32, i32, ptr) +DEF_HELPER_3(neon_abd_f32, i32, i32, i32, ptr) +DEF_HELPER_3(neon_ceq_f32, i32, i32, i32, ptr) +DEF_HELPER_3(neon_cge_f32, i32, i32, i32, ptr) +DEF_HELPER_3(neon_cgt_f32, i32, i32, i32, ptr) +DEF_HELPER_3(neon_acge_f32, i32, i32, i32, ptr) +DEF_HELPER_3(neon_acgt_f32, i32, i32, i32, ptr) /* iwmmxt_helper.c */ DEF_HELPER_2(iwmmxt_maddsq, i64, i64, i64) diff --git a/target-arm/neon_helper.c b/target-arm/neon_helper.c index 9165519236..fda4021a59 100644 --- a/target-arm/neon_helper.c +++ b/target-arm/neon_helper.c @@ -18,8 +18,6 @@ #define SET_QC() env->vfp.xregs[ARM_VFP_FPSCR] = CPSR_Q -#define NFS (&env->vfp.standard_fp_status) - #define NEON_TYPE1(name, type) \ typedef struct \ { \ @@ -1770,69 +1768,62 @@ uint32_t HELPER(neon_qneg_s32)(uint32_t x) } /* NEON Float helpers. */ -uint32_t HELPER(neon_min_f32)(uint32_t a, uint32_t b) +uint32_t HELPER(neon_min_f32)(uint32_t a, uint32_t b, void *fpstp) { - return float32_val(float32_min(make_float32(a), make_float32(b), NFS)); + float_status *fpst = fpstp; + return float32_val(float32_min(make_float32(a), make_float32(b), fpst)); } -uint32_t HELPER(neon_max_f32)(uint32_t a, uint32_t b) +uint32_t HELPER(neon_max_f32)(uint32_t a, uint32_t b, void *fpstp) { - return float32_val(float32_max(make_float32(a), make_float32(b), NFS)); + float_status *fpst = fpstp; + return float32_val(float32_max(make_float32(a), make_float32(b), fpst)); } -uint32_t HELPER(neon_abd_f32)(uint32_t a, uint32_t b) +uint32_t HELPER(neon_abd_f32)(uint32_t a, uint32_t b, void *fpstp) { + float_status *fpst = fpstp; float32 f0 = make_float32(a); float32 f1 = make_float32(b); - return float32_val(float32_abs(float32_sub(f0, f1, NFS))); -} - -uint32_t HELPER(neon_add_f32)(uint32_t a, uint32_t b) -{ - return float32_val(float32_add(make_float32(a), make_float32(b), NFS)); -} - -uint32_t HELPER(neon_sub_f32)(uint32_t a, uint32_t b) -{ - return float32_val(float32_sub(make_float32(a), make_float32(b), NFS)); -} - -uint32_t HELPER(neon_mul_f32)(uint32_t a, uint32_t b) -{ - return float32_val(float32_mul(make_float32(a), make_float32(b), NFS)); + return float32_val(float32_abs(float32_sub(f0, f1, fpst))); } /* Floating point comparisons produce an integer result. * Note that EQ doesn't signal InvalidOp for QNaNs but GE and GT do. * Softfloat routines return 0/1, which we convert to the 0/-1 Neon requires. */ -uint32_t HELPER(neon_ceq_f32)(uint32_t a, uint32_t b) +uint32_t HELPER(neon_ceq_f32)(uint32_t a, uint32_t b, void *fpstp) { - return -float32_eq_quiet(make_float32(a), make_float32(b), NFS); + float_status *fpst = fpstp; + return -float32_eq_quiet(make_float32(a), make_float32(b), fpst); } -uint32_t HELPER(neon_cge_f32)(uint32_t a, uint32_t b) +uint32_t HELPER(neon_cge_f32)(uint32_t a, uint32_t b, void *fpstp) { - return -float32_le(make_float32(b), make_float32(a), NFS); + float_status *fpst = fpstp; + return -float32_le(make_float32(b), make_float32(a), fpst); } -uint32_t HELPER(neon_cgt_f32)(uint32_t a, uint32_t b) +uint32_t HELPER(neon_cgt_f32)(uint32_t a, uint32_t b, void *fpstp) { - return -float32_lt(make_float32(b), make_float32(a), NFS); + float_status *fpst = fpstp; + return -float32_lt(make_float32(b), make_float32(a), fpst); } -uint32_t HELPER(neon_acge_f32)(uint32_t a, uint32_t b) +uint32_t HELPER(neon_acge_f32)(uint32_t a, uint32_t b, void *fpstp) { + float_status *fpst = fpstp; float32 f0 = float32_abs(make_float32(a)); float32 f1 = float32_abs(make_float32(b)); - return -float32_le(f1, f0, NFS); + return -float32_le(f1, f0, fpst); } -uint32_t HELPER(neon_acgt_f32)(uint32_t a, uint32_t b) +uint32_t HELPER(neon_acgt_f32)(uint32_t a, uint32_t b, void *fpstp) { + float_status *fpst = fpstp; float32 f0 = float32_abs(make_float32(a)); float32 f1 = float32_abs(make_float32(b)); - return -float32_lt(f1, f0, NFS); + return -float32_lt(f1, f0, fpst); } #define ELEM(V, N, SIZE) (((V) >> ((N) * (SIZE))) & ((1ull << (SIZE)) - 1)) diff --git a/target-arm/translate.c b/target-arm/translate.c index 9ed747f36d..27d988733c 100644 --- a/target-arm/translate.c +++ b/target-arm/translate.c @@ -4857,57 +4857,78 @@ static int disas_neon_data_insn(CPUState * env, DisasContext *s, uint32_t insn) } break; case NEON_3R_FLOAT_ARITH: /* Floating point arithmetic. */ + { + TCGv_ptr fpstatus = get_fpstatus_ptr(1); switch ((u << 2) | size) { case 0: /* VADD */ - gen_helper_neon_add_f32(tmp, tmp, tmp2); + case 4: /* VPADD */ + gen_helper_vfp_adds(tmp, tmp, tmp2, fpstatus); break; case 2: /* VSUB */ - gen_helper_neon_sub_f32(tmp, tmp, tmp2); - break; - case 4: /* VPADD */ - gen_helper_neon_add_f32(tmp, tmp, tmp2); + gen_helper_vfp_subs(tmp, tmp, tmp2, fpstatus); break; case 6: /* VABD */ - gen_helper_neon_abd_f32(tmp, tmp, tmp2); + gen_helper_neon_abd_f32(tmp, tmp, tmp2, fpstatus); break; default: abort(); } + tcg_temp_free_ptr(fpstatus); break; + } case NEON_3R_FLOAT_MULTIPLY: - gen_helper_neon_mul_f32(tmp, tmp, tmp2); + { + TCGv_ptr fpstatus = get_fpstatus_ptr(1); + gen_helper_vfp_muls(tmp, tmp, tmp2, fpstatus); if (!u) { tcg_temp_free_i32(tmp2); tmp2 = neon_load_reg(rd, pass); if (size == 0) { - gen_helper_neon_add_f32(tmp, tmp, tmp2); + gen_helper_vfp_adds(tmp, tmp, tmp2, fpstatus); } else { - gen_helper_neon_sub_f32(tmp, tmp2, tmp); + gen_helper_vfp_subs(tmp, tmp2, tmp, fpstatus); } } + tcg_temp_free_ptr(fpstatus); break; + } case NEON_3R_FLOAT_CMP: + { + TCGv_ptr fpstatus = get_fpstatus_ptr(1); if (!u) { - gen_helper_neon_ceq_f32(tmp, tmp, tmp2); + gen_helper_neon_ceq_f32(tmp, tmp, tmp2, fpstatus); } else { - if (size == 0) - gen_helper_neon_cge_f32(tmp, tmp, tmp2); - else - gen_helper_neon_cgt_f32(tmp, tmp, tmp2); + if (size == 0) { + gen_helper_neon_cge_f32(tmp, tmp, tmp2, fpstatus); + } else { + gen_helper_neon_cgt_f32(tmp, tmp, tmp2, fpstatus); + } } + tcg_temp_free_ptr(fpstatus); break; + } case NEON_3R_FLOAT_ACMP: - if (size == 0) - gen_helper_neon_acge_f32(tmp, tmp, tmp2); - else - gen_helper_neon_acgt_f32(tmp, tmp, tmp2); + { + TCGv_ptr fpstatus = get_fpstatus_ptr(1); + if (size == 0) { + gen_helper_neon_acge_f32(tmp, tmp, tmp2, fpstatus); + } else { + gen_helper_neon_acgt_f32(tmp, tmp, tmp2, fpstatus); + } + tcg_temp_free_ptr(fpstatus); break; + } case NEON_3R_FLOAT_MINMAX: - if (size == 0) - gen_helper_neon_max_f32(tmp, tmp, tmp2); - else - gen_helper_neon_min_f32(tmp, tmp, tmp2); + { + TCGv_ptr fpstatus = get_fpstatus_ptr(1); + if (size == 0) { + gen_helper_neon_max_f32(tmp, tmp, tmp2, fpstatus); + } else { + gen_helper_neon_min_f32(tmp, tmp, tmp2, fpstatus); + } + tcg_temp_free_ptr(fpstatus); break; + } case NEON_3R_VRECPS_VRSQRTS: if (size == 0) gen_helper_recps_f32(tmp, tmp, tmp2, cpu_env); @@ -5606,7 +5627,9 @@ static int disas_neon_data_insn(CPUState * env, DisasContext *s, uint32_t insn) gen_helper_neon_qrdmulh_s32(tmp, tmp, tmp2); } } else if (op & 1) { - gen_helper_neon_mul_f32(tmp, tmp, tmp2); + TCGv_ptr fpstatus = get_fpstatus_ptr(1); + gen_helper_vfp_muls(tmp, tmp, tmp2, fpstatus); + tcg_temp_free_ptr(fpstatus); } else { switch (size) { case 0: gen_helper_neon_mul_u8(tmp, tmp, tmp2); break; @@ -5624,14 +5647,22 @@ static int disas_neon_data_insn(CPUState * env, DisasContext *s, uint32_t insn) gen_neon_add(size, tmp, tmp2); break; case 1: - gen_helper_neon_add_f32(tmp, tmp, tmp2); + { + TCGv_ptr fpstatus = get_fpstatus_ptr(1); + gen_helper_vfp_adds(tmp, tmp, tmp2, fpstatus); + tcg_temp_free_ptr(fpstatus); break; + } case 4: gen_neon_rsb(size, tmp, tmp2); break; case 5: - gen_helper_neon_sub_f32(tmp, tmp2, tmp); + { + TCGv_ptr fpstatus = get_fpstatus_ptr(1); + gen_helper_vfp_subs(tmp, tmp2, tmp, fpstatus); + tcg_temp_free_ptr(fpstatus); break; + } default: abort(); } @@ -6029,30 +6060,50 @@ static int disas_neon_data_insn(CPUState * env, DisasContext *s, uint32_t insn) tcg_temp_free(tmp2); break; case NEON_2RM_VCGT0_F: + { + TCGv_ptr fpstatus = get_fpstatus_ptr(1); tmp2 = tcg_const_i32(0); - gen_helper_neon_cgt_f32(tmp, tmp, tmp2); + gen_helper_neon_cgt_f32(tmp, tmp, tmp2, fpstatus); tcg_temp_free(tmp2); + tcg_temp_free_ptr(fpstatus); break; + } case NEON_2RM_VCGE0_F: + { + TCGv_ptr fpstatus = get_fpstatus_ptr(1); tmp2 = tcg_const_i32(0); - gen_helper_neon_cge_f32(tmp, tmp, tmp2); + gen_helper_neon_cge_f32(tmp, tmp, tmp2, fpstatus); tcg_temp_free(tmp2); + tcg_temp_free_ptr(fpstatus); break; + } case NEON_2RM_VCEQ0_F: + { + TCGv_ptr fpstatus = get_fpstatus_ptr(1); tmp2 = tcg_const_i32(0); - gen_helper_neon_ceq_f32(tmp, tmp, tmp2); + gen_helper_neon_ceq_f32(tmp, tmp, tmp2, fpstatus); tcg_temp_free(tmp2); + tcg_temp_free_ptr(fpstatus); break; + } case NEON_2RM_VCLE0_F: + { + TCGv_ptr fpstatus = get_fpstatus_ptr(1); tmp2 = tcg_const_i32(0); - gen_helper_neon_cge_f32(tmp, tmp2, tmp); + gen_helper_neon_cge_f32(tmp, tmp2, tmp, fpstatus); tcg_temp_free(tmp2); + tcg_temp_free_ptr(fpstatus); break; + } case NEON_2RM_VCLT0_F: + { + TCGv_ptr fpstatus = get_fpstatus_ptr(1); tmp2 = tcg_const_i32(0); - gen_helper_neon_cgt_f32(tmp, tmp2, tmp); + gen_helper_neon_cgt_f32(tmp, tmp2, tmp, fpstatus); tcg_temp_free(tmp2); + tcg_temp_free_ptr(fpstatus); break; + } case NEON_2RM_VABS_F: gen_vfp_abs(0); break; From 02da0b2d7152f736b1fba95902f55db4fd7ea3a5 Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Wed, 25 May 2011 13:31:02 +0000 Subject: [PATCH 014/209] Revert "target-arm: Use global env in neon_helper.c helpers" This effectively reverts commit 2a3f75b42ac255be09ec2939b96c549ec830efd3 so that we return to passing CPUState to helpers as an explicit parameter. (There were a number of conflicts in target-arm/translate.c which had to be resolved by hand so it is not a pure revert.) Signed-off-by: Peter Maydell --- target-arm/helper.h | 134 ++++++++++++++++++------------------ target-arm/neon_helper.c | 144 ++++++++++++++++++++------------------- target-arm/translate.c | 140 ++++++++++++++++++++++--------------- 3 files changed, 227 insertions(+), 191 deletions(-) diff --git a/target-arm/helper.h b/target-arm/helper.h index c29efd575f..3ad1cb0881 100644 --- a/target-arm/helper.h +++ b/target-arm/helper.h @@ -154,22 +154,22 @@ DEF_HELPER_2(sar_cc, i32, i32, i32) DEF_HELPER_2(ror_cc, i32, i32, i32) /* neon_helper.c */ -DEF_HELPER_2(neon_qadd_u8, i32, i32, i32) -DEF_HELPER_2(neon_qadd_s8, i32, i32, i32) -DEF_HELPER_2(neon_qadd_u16, i32, i32, i32) -DEF_HELPER_2(neon_qadd_s16, i32, i32, i32) -DEF_HELPER_2(neon_qadd_u32, i32, i32, i32) -DEF_HELPER_2(neon_qadd_s32, i32, i32, i32) -DEF_HELPER_2(neon_qsub_u8, i32, i32, i32) -DEF_HELPER_2(neon_qsub_s8, i32, i32, i32) -DEF_HELPER_2(neon_qsub_u16, i32, i32, i32) -DEF_HELPER_2(neon_qsub_s16, i32, i32, i32) -DEF_HELPER_2(neon_qsub_u32, i32, i32, i32) -DEF_HELPER_2(neon_qsub_s32, i32, i32, i32) -DEF_HELPER_2(neon_qadd_u64, i64, i64, i64) -DEF_HELPER_2(neon_qadd_s64, i64, i64, i64) -DEF_HELPER_2(neon_qsub_u64, i64, i64, i64) -DEF_HELPER_2(neon_qsub_s64, i64, i64, i64) +DEF_HELPER_3(neon_qadd_u8, i32, env, i32, i32) +DEF_HELPER_3(neon_qadd_s8, i32, env, i32, i32) +DEF_HELPER_3(neon_qadd_u16, i32, env, i32, i32) +DEF_HELPER_3(neon_qadd_s16, i32, env, i32, i32) +DEF_HELPER_3(neon_qadd_u32, i32, env, i32, i32) +DEF_HELPER_3(neon_qadd_s32, i32, env, i32, i32) +DEF_HELPER_3(neon_qsub_u8, i32, env, i32, i32) +DEF_HELPER_3(neon_qsub_s8, i32, env, i32, i32) +DEF_HELPER_3(neon_qsub_u16, i32, env, i32, i32) +DEF_HELPER_3(neon_qsub_s16, i32, env, i32, i32) +DEF_HELPER_3(neon_qsub_u32, i32, env, i32, i32) +DEF_HELPER_3(neon_qsub_s32, i32, env, i32, i32) +DEF_HELPER_3(neon_qadd_u64, i64, env, i64, i64) +DEF_HELPER_3(neon_qadd_s64, i64, env, i64, i64) +DEF_HELPER_3(neon_qsub_u64, i64, env, i64, i64) +DEF_HELPER_3(neon_qsub_s64, i64, env, i64, i64) DEF_HELPER_2(neon_hadd_s8, i32, i32, i32) DEF_HELPER_2(neon_hadd_u8, i32, i32, i32) @@ -247,26 +247,26 @@ DEF_HELPER_2(neon_rshl_u32, i32, i32, i32) DEF_HELPER_2(neon_rshl_s32, i32, i32, i32) DEF_HELPER_2(neon_rshl_u64, i64, i64, i64) DEF_HELPER_2(neon_rshl_s64, i64, i64, i64) -DEF_HELPER_2(neon_qshl_u8, i32, i32, i32) -DEF_HELPER_2(neon_qshl_s8, i32, i32, i32) -DEF_HELPER_2(neon_qshl_u16, i32, i32, i32) -DEF_HELPER_2(neon_qshl_s16, i32, i32, i32) -DEF_HELPER_2(neon_qshl_u32, i32, i32, i32) -DEF_HELPER_2(neon_qshl_s32, i32, i32, i32) -DEF_HELPER_2(neon_qshl_u64, i64, i64, i64) -DEF_HELPER_2(neon_qshl_s64, i64, i64, i64) -DEF_HELPER_2(neon_qshlu_s8, i32, i32, i32); -DEF_HELPER_2(neon_qshlu_s16, i32, i32, i32); -DEF_HELPER_2(neon_qshlu_s32, i32, i32, i32); -DEF_HELPER_2(neon_qshlu_s64, i64, i64, i64); -DEF_HELPER_2(neon_qrshl_u8, i32, i32, i32) -DEF_HELPER_2(neon_qrshl_s8, i32, i32, i32) -DEF_HELPER_2(neon_qrshl_u16, i32, i32, i32) -DEF_HELPER_2(neon_qrshl_s16, i32, i32, i32) -DEF_HELPER_2(neon_qrshl_u32, i32, i32, i32) -DEF_HELPER_2(neon_qrshl_s32, i32, i32, i32) -DEF_HELPER_2(neon_qrshl_u64, i64, i64, i64) -DEF_HELPER_2(neon_qrshl_s64, i64, i64, i64) +DEF_HELPER_3(neon_qshl_u8, i32, env, i32, i32) +DEF_HELPER_3(neon_qshl_s8, i32, env, i32, i32) +DEF_HELPER_3(neon_qshl_u16, i32, env, i32, i32) +DEF_HELPER_3(neon_qshl_s16, i32, env, i32, i32) +DEF_HELPER_3(neon_qshl_u32, i32, env, i32, i32) +DEF_HELPER_3(neon_qshl_s32, i32, env, i32, i32) +DEF_HELPER_3(neon_qshl_u64, i64, env, i64, i64) +DEF_HELPER_3(neon_qshl_s64, i64, env, i64, i64) +DEF_HELPER_3(neon_qshlu_s8, i32, env, i32, i32); +DEF_HELPER_3(neon_qshlu_s16, i32, env, i32, i32); +DEF_HELPER_3(neon_qshlu_s32, i32, env, i32, i32); +DEF_HELPER_3(neon_qshlu_s64, i64, env, i64, i64); +DEF_HELPER_3(neon_qrshl_u8, i32, env, i32, i32) +DEF_HELPER_3(neon_qrshl_s8, i32, env, i32, i32) +DEF_HELPER_3(neon_qrshl_u16, i32, env, i32, i32) +DEF_HELPER_3(neon_qrshl_s16, i32, env, i32, i32) +DEF_HELPER_3(neon_qrshl_u32, i32, env, i32, i32) +DEF_HELPER_3(neon_qrshl_s32, i32, env, i32, i32) +DEF_HELPER_3(neon_qrshl_u64, i64, env, i64, i64) +DEF_HELPER_3(neon_qrshl_s64, i64, env, i64, i64) DEF_HELPER_2(neon_add_u8, i32, i32, i32) DEF_HELPER_2(neon_add_u16, i32, i32, i32) @@ -295,22 +295,22 @@ DEF_HELPER_1(neon_cls_s16, i32, i32) DEF_HELPER_1(neon_cls_s32, i32, i32) DEF_HELPER_1(neon_cnt_u8, i32, i32) -DEF_HELPER_2(neon_qdmulh_s16, i32, i32, i32) -DEF_HELPER_2(neon_qrdmulh_s16, i32, i32, i32) -DEF_HELPER_2(neon_qdmulh_s32, i32, i32, i32) -DEF_HELPER_2(neon_qrdmulh_s32, i32, i32, i32) +DEF_HELPER_3(neon_qdmulh_s16, i32, env, i32, i32) +DEF_HELPER_3(neon_qrdmulh_s16, i32, env, i32, i32) +DEF_HELPER_3(neon_qdmulh_s32, i32, env, i32, i32) +DEF_HELPER_3(neon_qrdmulh_s32, i32, env, i32, i32) DEF_HELPER_1(neon_narrow_u8, i32, i64) DEF_HELPER_1(neon_narrow_u16, i32, i64) -DEF_HELPER_1(neon_unarrow_sat8, i32, i64) -DEF_HELPER_1(neon_narrow_sat_u8, i32, i64) -DEF_HELPER_1(neon_narrow_sat_s8, i32, i64) -DEF_HELPER_1(neon_unarrow_sat16, i32, i64) -DEF_HELPER_1(neon_narrow_sat_u16, i32, i64) -DEF_HELPER_1(neon_narrow_sat_s16, i32, i64) -DEF_HELPER_1(neon_unarrow_sat32, i32, i64) -DEF_HELPER_1(neon_narrow_sat_u32, i32, i64) -DEF_HELPER_1(neon_narrow_sat_s32, i32, i64) +DEF_HELPER_2(neon_unarrow_sat8, i32, env, i64) +DEF_HELPER_2(neon_narrow_sat_u8, i32, env, i64) +DEF_HELPER_2(neon_narrow_sat_s8, i32, env, i64) +DEF_HELPER_2(neon_unarrow_sat16, i32, env, i64) +DEF_HELPER_2(neon_narrow_sat_u16, i32, env, i64) +DEF_HELPER_2(neon_narrow_sat_s16, i32, env, i64) +DEF_HELPER_2(neon_unarrow_sat32, i32, env, i64) +DEF_HELPER_2(neon_narrow_sat_u32, i32, env, i64) +DEF_HELPER_2(neon_narrow_sat_s32, i32, env, i64) DEF_HELPER_1(neon_narrow_high_u8, i32, i64) DEF_HELPER_1(neon_narrow_high_u16, i32, i64) DEF_HELPER_1(neon_narrow_round_high_u8, i32, i64) @@ -326,8 +326,8 @@ DEF_HELPER_2(neon_paddl_u16, i64, i64, i64) DEF_HELPER_2(neon_paddl_u32, i64, i64, i64) DEF_HELPER_2(neon_subl_u16, i64, i64, i64) DEF_HELPER_2(neon_subl_u32, i64, i64, i64) -DEF_HELPER_2(neon_addl_saturate_s32, i64, i64, i64) -DEF_HELPER_2(neon_addl_saturate_s64, i64, i64, i64) +DEF_HELPER_3(neon_addl_saturate_s32, i64, env, i64, i64) +DEF_HELPER_3(neon_addl_saturate_s64, i64, env, i64, i64) DEF_HELPER_2(neon_abdl_u16, i64, i32, i32) DEF_HELPER_2(neon_abdl_s16, i64, i32, i32) DEF_HELPER_2(neon_abdl_u32, i64, i32, i32) @@ -343,12 +343,12 @@ DEF_HELPER_1(neon_negl_u16, i64, i64) DEF_HELPER_1(neon_negl_u32, i64, i64) DEF_HELPER_1(neon_negl_u64, i64, i64) -DEF_HELPER_1(neon_qabs_s8, i32, i32) -DEF_HELPER_1(neon_qabs_s16, i32, i32) -DEF_HELPER_1(neon_qabs_s32, i32, i32) -DEF_HELPER_1(neon_qneg_s8, i32, i32) -DEF_HELPER_1(neon_qneg_s16, i32, i32) -DEF_HELPER_1(neon_qneg_s32, i32, i32) +DEF_HELPER_2(neon_qabs_s8, i32, env, i32) +DEF_HELPER_2(neon_qabs_s16, i32, env, i32) +DEF_HELPER_2(neon_qabs_s32, i32, env, i32) +DEF_HELPER_2(neon_qneg_s8, i32, env, i32) +DEF_HELPER_2(neon_qneg_s16, i32, env, i32) +DEF_HELPER_2(neon_qneg_s32, i32, env, i32) DEF_HELPER_3(neon_min_f32, i32, i32, i32, ptr) DEF_HELPER_3(neon_max_f32, i32, i32, i32, ptr) @@ -458,15 +458,15 @@ DEF_HELPER_3(iwmmxt_muladdswl, i64, i64, i32, i32) DEF_HELPER_2(set_teecr, void, env, i32) -DEF_HELPER_2(neon_unzip8, void, i32, i32) -DEF_HELPER_2(neon_unzip16, void, i32, i32) -DEF_HELPER_2(neon_qunzip8, void, i32, i32) -DEF_HELPER_2(neon_qunzip16, void, i32, i32) -DEF_HELPER_2(neon_qunzip32, void, i32, i32) -DEF_HELPER_2(neon_zip8, void, i32, i32) -DEF_HELPER_2(neon_zip16, void, i32, i32) -DEF_HELPER_2(neon_qzip8, void, i32, i32) -DEF_HELPER_2(neon_qzip16, void, i32, i32) -DEF_HELPER_2(neon_qzip32, void, i32, i32) +DEF_HELPER_3(neon_unzip8, void, env, i32, i32) +DEF_HELPER_3(neon_unzip16, void, env, i32, i32) +DEF_HELPER_3(neon_qunzip8, void, env, i32, i32) +DEF_HELPER_3(neon_qunzip16, void, env, i32, i32) +DEF_HELPER_3(neon_qunzip32, void, env, i32, i32) +DEF_HELPER_3(neon_zip8, void, env, i32, i32) +DEF_HELPER_3(neon_zip16, void, env, i32, i32) +DEF_HELPER_3(neon_qzip8, void, env, i32, i32) +DEF_HELPER_3(neon_qzip16, void, env, i32, i32) +DEF_HELPER_3(neon_qzip32, void, env, i32, i32) #include "def-helper.h" diff --git a/target-arm/neon_helper.c b/target-arm/neon_helper.c index fda4021a59..28306279a8 100644 --- a/target-arm/neon_helper.c +++ b/target-arm/neon_helper.c @@ -10,7 +10,7 @@ #include #include "cpu.h" -#include "exec.h" +#include "exec-all.h" #include "helper.h" #define SIGNBIT (uint32_t)0x80000000 @@ -113,6 +113,10 @@ NEON_TYPE1(u32, uint32_t) uint32_t HELPER(glue(neon_,name))(uint32_t arg1, uint32_t arg2) \ NEON_VOP_BODY(vtype, n) +#define NEON_VOP_ENV(name, vtype, n) \ +uint32_t HELPER(glue(neon_,name))(CPUState *env, uint32_t arg1, uint32_t arg2) \ +NEON_VOP_BODY(vtype, n) + /* Pairwise operations. */ /* For 32-bit elements each segment only contains a single element, so the elementwise and pairwise operations are the same. */ @@ -161,14 +165,14 @@ uint32_t HELPER(glue(neon_,name))(uint32_t arg) \ dest = tmp; \ }} while(0) #define NEON_FN(dest, src1, src2) NEON_USAT(dest, src1, src2, uint8_t) -NEON_VOP(qadd_u8, neon_u8, 4) +NEON_VOP_ENV(qadd_u8, neon_u8, 4) #undef NEON_FN #define NEON_FN(dest, src1, src2) NEON_USAT(dest, src1, src2, uint16_t) -NEON_VOP(qadd_u16, neon_u16, 2) +NEON_VOP_ENV(qadd_u16, neon_u16, 2) #undef NEON_FN #undef NEON_USAT -uint32_t HELPER(neon_qadd_u32)(uint32_t a, uint32_t b) +uint32_t HELPER(neon_qadd_u32)(CPUState *env, uint32_t a, uint32_t b) { uint32_t res = a + b; if (res < a) { @@ -178,7 +182,7 @@ uint32_t HELPER(neon_qadd_u32)(uint32_t a, uint32_t b) return res; } -uint64_t HELPER(neon_qadd_u64)(uint64_t src1, uint64_t src2) +uint64_t HELPER(neon_qadd_u64)(CPUState *env, uint64_t src1, uint64_t src2) { uint64_t res; @@ -203,14 +207,14 @@ uint64_t HELPER(neon_qadd_u64)(uint64_t src1, uint64_t src2) dest = tmp; \ } while(0) #define NEON_FN(dest, src1, src2) NEON_SSAT(dest, src1, src2, int8_t) -NEON_VOP(qadd_s8, neon_s8, 4) +NEON_VOP_ENV(qadd_s8, neon_s8, 4) #undef NEON_FN #define NEON_FN(dest, src1, src2) NEON_SSAT(dest, src1, src2, int16_t) -NEON_VOP(qadd_s16, neon_s16, 2) +NEON_VOP_ENV(qadd_s16, neon_s16, 2) #undef NEON_FN #undef NEON_SSAT -uint32_t HELPER(neon_qadd_s32)(uint32_t a, uint32_t b) +uint32_t HELPER(neon_qadd_s32)(CPUState *env, uint32_t a, uint32_t b) { uint32_t res = a + b; if (((res ^ a) & SIGNBIT) && !((a ^ b) & SIGNBIT)) { @@ -220,7 +224,7 @@ uint32_t HELPER(neon_qadd_s32)(uint32_t a, uint32_t b) return res; } -uint64_t HELPER(neon_qadd_s64)(uint64_t src1, uint64_t src2) +uint64_t HELPER(neon_qadd_s64)(CPUState *env, uint64_t src1, uint64_t src2) { uint64_t res; @@ -241,14 +245,14 @@ uint64_t HELPER(neon_qadd_s64)(uint64_t src1, uint64_t src2) dest = tmp; \ }} while(0) #define NEON_FN(dest, src1, src2) NEON_USAT(dest, src1, src2, uint8_t) -NEON_VOP(qsub_u8, neon_u8, 4) +NEON_VOP_ENV(qsub_u8, neon_u8, 4) #undef NEON_FN #define NEON_FN(dest, src1, src2) NEON_USAT(dest, src1, src2, uint16_t) -NEON_VOP(qsub_u16, neon_u16, 2) +NEON_VOP_ENV(qsub_u16, neon_u16, 2) #undef NEON_FN #undef NEON_USAT -uint32_t HELPER(neon_qsub_u32)(uint32_t a, uint32_t b) +uint32_t HELPER(neon_qsub_u32)(CPUState *env, uint32_t a, uint32_t b) { uint32_t res = a - b; if (res > a) { @@ -258,7 +262,7 @@ uint32_t HELPER(neon_qsub_u32)(uint32_t a, uint32_t b) return res; } -uint64_t HELPER(neon_qsub_u64)(uint64_t src1, uint64_t src2) +uint64_t HELPER(neon_qsub_u64)(CPUState *env, uint64_t src1, uint64_t src2) { uint64_t res; @@ -284,14 +288,14 @@ uint64_t HELPER(neon_qsub_u64)(uint64_t src1, uint64_t src2) dest = tmp; \ } while(0) #define NEON_FN(dest, src1, src2) NEON_SSAT(dest, src1, src2, int8_t) -NEON_VOP(qsub_s8, neon_s8, 4) +NEON_VOP_ENV(qsub_s8, neon_s8, 4) #undef NEON_FN #define NEON_FN(dest, src1, src2) NEON_SSAT(dest, src1, src2, int16_t) -NEON_VOP(qsub_s16, neon_s16, 2) +NEON_VOP_ENV(qsub_s16, neon_s16, 2) #undef NEON_FN #undef NEON_SSAT -uint32_t HELPER(neon_qsub_s32)(uint32_t a, uint32_t b) +uint32_t HELPER(neon_qsub_s32)(CPUState *env, uint32_t a, uint32_t b) { uint32_t res = a - b; if (((res ^ a) & SIGNBIT) && ((a ^ b) & SIGNBIT)) { @@ -301,7 +305,7 @@ uint32_t HELPER(neon_qsub_s32)(uint32_t a, uint32_t b) return res; } -uint64_t HELPER(neon_qsub_s64)(uint64_t src1, uint64_t src2) +uint64_t HELPER(neon_qsub_s64)(CPUState *env, uint64_t src1, uint64_t src2) { uint64_t res; @@ -652,12 +656,12 @@ uint64_t HELPER(neon_rshl_u64)(uint64_t val, uint64_t shiftop) dest = ~0; \ } \ }} while (0) -NEON_VOP(qshl_u8, neon_u8, 4) -NEON_VOP(qshl_u16, neon_u16, 2) -NEON_VOP(qshl_u32, neon_u32, 1) +NEON_VOP_ENV(qshl_u8, neon_u8, 4) +NEON_VOP_ENV(qshl_u16, neon_u16, 2) +NEON_VOP_ENV(qshl_u32, neon_u32, 1) #undef NEON_FN -uint64_t HELPER(neon_qshl_u64)(uint64_t val, uint64_t shiftop) +uint64_t HELPER(neon_qshl_u64)(CPUState *env, uint64_t val, uint64_t shiftop) { int8_t shift = (int8_t)shiftop; if (shift >= 64) { @@ -707,12 +711,12 @@ uint64_t HELPER(neon_qshl_u64)(uint64_t val, uint64_t shiftop) } \ } \ }} while (0) -NEON_VOP(qshl_s8, neon_s8, 4) -NEON_VOP(qshl_s16, neon_s16, 2) -NEON_VOP(qshl_s32, neon_s32, 1) +NEON_VOP_ENV(qshl_s8, neon_s8, 4) +NEON_VOP_ENV(qshl_s16, neon_s16, 2) +NEON_VOP_ENV(qshl_s32, neon_s32, 1) #undef NEON_FN -uint64_t HELPER(neon_qshl_s64)(uint64_t valop, uint64_t shiftop) +uint64_t HELPER(neon_qshl_s64)(CPUState *env, uint64_t valop, uint64_t shiftop) { int8_t shift = (uint8_t)shiftop; int64_t val = valop; @@ -762,26 +766,26 @@ uint64_t HELPER(neon_qshl_s64)(uint64_t valop, uint64_t shiftop) } \ } \ }} while (0) -NEON_VOP(qshlu_s8, neon_u8, 4) -NEON_VOP(qshlu_s16, neon_u16, 2) +NEON_VOP_ENV(qshlu_s8, neon_u8, 4) +NEON_VOP_ENV(qshlu_s16, neon_u16, 2) #undef NEON_FN -uint32_t HELPER(neon_qshlu_s32)(uint32_t valop, uint32_t shiftop) +uint32_t HELPER(neon_qshlu_s32)(CPUState *env, uint32_t valop, uint32_t shiftop) { if ((int32_t)valop < 0) { SET_QC(); return 0; } - return helper_neon_qshl_u32(valop, shiftop); + return helper_neon_qshl_u32(env, valop, shiftop); } -uint64_t HELPER(neon_qshlu_s64)(uint64_t valop, uint64_t shiftop) +uint64_t HELPER(neon_qshlu_s64)(CPUState *env, uint64_t valop, uint64_t shiftop) { if ((int64_t)valop < 0) { SET_QC(); return 0; } - return helper_neon_qshl_u64(valop, shiftop); + return helper_neon_qshl_u64(env, valop, shiftop); } /* FIXME: This is wrong. */ @@ -808,13 +812,13 @@ uint64_t HELPER(neon_qshlu_s64)(uint64_t valop, uint64_t shiftop) dest = ~0; \ } \ }} while (0) -NEON_VOP(qrshl_u8, neon_u8, 4) -NEON_VOP(qrshl_u16, neon_u16, 2) +NEON_VOP_ENV(qrshl_u8, neon_u8, 4) +NEON_VOP_ENV(qrshl_u16, neon_u16, 2) #undef NEON_FN /* The addition of the rounding constant may overflow, so we use an * intermediate 64 bits accumulator. */ -uint32_t HELPER(neon_qrshl_u32)(uint32_t val, uint32_t shiftop) +uint32_t HELPER(neon_qrshl_u32)(CPUState *env, uint32_t val, uint32_t shiftop) { uint32_t dest; int8_t shift = (int8_t)shiftop; @@ -844,7 +848,7 @@ uint32_t HELPER(neon_qrshl_u32)(uint32_t val, uint32_t shiftop) /* Handling addition overflow with 64 bits inputs values is more * tricky than with 32 bits values. */ -uint64_t HELPER(neon_qrshl_u64)(uint64_t val, uint64_t shiftop) +uint64_t HELPER(neon_qrshl_u64)(CPUState *env, uint64_t val, uint64_t shiftop) { int8_t shift = (int8_t)shiftop; if (shift >= 64) { @@ -905,13 +909,13 @@ uint64_t HELPER(neon_qrshl_u64)(uint64_t val, uint64_t shiftop) } \ } \ }} while (0) -NEON_VOP(qrshl_s8, neon_s8, 4) -NEON_VOP(qrshl_s16, neon_s16, 2) +NEON_VOP_ENV(qrshl_s8, neon_s8, 4) +NEON_VOP_ENV(qrshl_s16, neon_s16, 2) #undef NEON_FN /* The addition of the rounding constant may overflow, so we use an * intermediate 64 bits accumulator. */ -uint32_t HELPER(neon_qrshl_s32)(uint32_t valop, uint32_t shiftop) +uint32_t HELPER(neon_qrshl_s32)(CPUState *env, uint32_t valop, uint32_t shiftop) { int32_t dest; int32_t val = (int32_t)valop; @@ -940,7 +944,7 @@ uint32_t HELPER(neon_qrshl_s32)(uint32_t valop, uint32_t shiftop) /* Handling addition overflow with 64 bits inputs values is more * tricky than with 32 bits values. */ -uint64_t HELPER(neon_qrshl_s64)(uint64_t valop, uint64_t shiftop) +uint64_t HELPER(neon_qrshl_s64)(CPUState *env, uint64_t valop, uint64_t shiftop) { int8_t shift = (uint8_t)shiftop; int64_t val = valop; @@ -1149,10 +1153,10 @@ uint32_t HELPER(neon_cnt_u8)(uint32_t x) dest = tmp >> 16; \ } while(0) #define NEON_FN(dest, src1, src2) NEON_QDMULH16(dest, src1, src2, 0) -NEON_VOP(qdmulh_s16, neon_s16, 2) +NEON_VOP_ENV(qdmulh_s16, neon_s16, 2) #undef NEON_FN #define NEON_FN(dest, src1, src2) NEON_QDMULH16(dest, src1, src2, 1) -NEON_VOP(qrdmulh_s16, neon_s16, 2) +NEON_VOP_ENV(qrdmulh_s16, neon_s16, 2) #undef NEON_FN #undef NEON_QDMULH16 @@ -1175,10 +1179,10 @@ NEON_VOP(qrdmulh_s16, neon_s16, 2) dest = tmp >> 32; \ } while(0) #define NEON_FN(dest, src1, src2) NEON_QDMULH32(dest, src1, src2, 0) -NEON_VOP(qdmulh_s32, neon_s32, 1) +NEON_VOP_ENV(qdmulh_s32, neon_s32, 1) #undef NEON_FN #define NEON_FN(dest, src1, src2) NEON_QDMULH32(dest, src1, src2, 1) -NEON_VOP(qrdmulh_s32, neon_s32, 1) +NEON_VOP_ENV(qrdmulh_s32, neon_s32, 1) #undef NEON_FN #undef NEON_QDMULH32 @@ -1219,7 +1223,7 @@ uint32_t HELPER(neon_narrow_round_high_u16)(uint64_t x) return ((x >> 16) & 0xffff) | ((x >> 32) & 0xffff0000); } -uint32_t HELPER(neon_unarrow_sat8)(uint64_t x) +uint32_t HELPER(neon_unarrow_sat8)(CPUState *env, uint64_t x) { uint16_t s; uint8_t d; @@ -1246,7 +1250,7 @@ uint32_t HELPER(neon_unarrow_sat8)(uint64_t x) return res; } -uint32_t HELPER(neon_narrow_sat_u8)(uint64_t x) +uint32_t HELPER(neon_narrow_sat_u8)(CPUState *env, uint64_t x) { uint16_t s; uint8_t d; @@ -1269,7 +1273,7 @@ uint32_t HELPER(neon_narrow_sat_u8)(uint64_t x) return res; } -uint32_t HELPER(neon_narrow_sat_s8)(uint64_t x) +uint32_t HELPER(neon_narrow_sat_s8)(CPUState *env, uint64_t x) { int16_t s; uint8_t d; @@ -1292,7 +1296,7 @@ uint32_t HELPER(neon_narrow_sat_s8)(uint64_t x) return res; } -uint32_t HELPER(neon_unarrow_sat16)(uint64_t x) +uint32_t HELPER(neon_unarrow_sat16)(CPUState *env, uint64_t x) { uint32_t high; uint32_t low; @@ -1315,7 +1319,7 @@ uint32_t HELPER(neon_unarrow_sat16)(uint64_t x) return low | (high << 16); } -uint32_t HELPER(neon_narrow_sat_u16)(uint64_t x) +uint32_t HELPER(neon_narrow_sat_u16)(CPUState *env, uint64_t x) { uint32_t high; uint32_t low; @@ -1332,7 +1336,7 @@ uint32_t HELPER(neon_narrow_sat_u16)(uint64_t x) return low | (high << 16); } -uint32_t HELPER(neon_narrow_sat_s16)(uint64_t x) +uint32_t HELPER(neon_narrow_sat_s16)(CPUState *env, uint64_t x) { int32_t low; int32_t high; @@ -1349,7 +1353,7 @@ uint32_t HELPER(neon_narrow_sat_s16)(uint64_t x) return (uint16_t)low | (high << 16); } -uint32_t HELPER(neon_unarrow_sat32)(uint64_t x) +uint32_t HELPER(neon_unarrow_sat32)(CPUState *env, uint64_t x) { if (x & 0x8000000000000000ull) { SET_QC(); @@ -1362,7 +1366,7 @@ uint32_t HELPER(neon_unarrow_sat32)(uint64_t x) return x; } -uint32_t HELPER(neon_narrow_sat_u32)(uint64_t x) +uint32_t HELPER(neon_narrow_sat_u32)(CPUState *env, uint64_t x) { if (x > 0xffffffffu) { SET_QC(); @@ -1371,7 +1375,7 @@ uint32_t HELPER(neon_narrow_sat_u32)(uint64_t x) return x; } -uint32_t HELPER(neon_narrow_sat_s32)(uint64_t x) +uint32_t HELPER(neon_narrow_sat_s32)(CPUState *env, uint64_t x) { if ((int64_t)x != (int32_t)x) { SET_QC(); @@ -1478,7 +1482,7 @@ uint64_t HELPER(neon_subl_u32)(uint64_t a, uint64_t b) return (a - b) ^ mask; } -uint64_t HELPER(neon_addl_saturate_s32)(uint64_t a, uint64_t b) +uint64_t HELPER(neon_addl_saturate_s32)(CPUState *env, uint64_t a, uint64_t b) { uint32_t x, y; uint32_t low, high; @@ -1500,7 +1504,7 @@ uint64_t HELPER(neon_addl_saturate_s32)(uint64_t a, uint64_t b) return low | ((uint64_t)high << 32); } -uint64_t HELPER(neon_addl_saturate_s64)(uint64_t a, uint64_t b) +uint64_t HELPER(neon_addl_saturate_s64)(CPUState *env, uint64_t a, uint64_t b) { uint64_t result; @@ -1676,7 +1680,7 @@ uint64_t HELPER(neon_negl_u64)(uint64_t x) } else if (x < 0) { \ x = -x; \ }} while (0) -uint32_t HELPER(neon_qabs_s8)(uint32_t x) +uint32_t HELPER(neon_qabs_s8)(CPUState *env, uint32_t x) { neon_s8 vec; NEON_UNPACK(neon_s8, vec, x); @@ -1696,7 +1700,7 @@ uint32_t HELPER(neon_qabs_s8)(uint32_t x) } else { \ x = -x; \ }} while (0) -uint32_t HELPER(neon_qneg_s8)(uint32_t x) +uint32_t HELPER(neon_qneg_s8)(CPUState *env, uint32_t x) { neon_s8 vec; NEON_UNPACK(neon_s8, vec, x); @@ -1716,7 +1720,7 @@ uint32_t HELPER(neon_qneg_s8)(uint32_t x) } else if (x < 0) { \ x = -x; \ }} while (0) -uint32_t HELPER(neon_qabs_s16)(uint32_t x) +uint32_t HELPER(neon_qabs_s16)(CPUState *env, uint32_t x) { neon_s16 vec; NEON_UNPACK(neon_s16, vec, x); @@ -1734,7 +1738,7 @@ uint32_t HELPER(neon_qabs_s16)(uint32_t x) } else { \ x = -x; \ }} while (0) -uint32_t HELPER(neon_qneg_s16)(uint32_t x) +uint32_t HELPER(neon_qneg_s16)(CPUState *env, uint32_t x) { neon_s16 vec; NEON_UNPACK(neon_s16, vec, x); @@ -1745,7 +1749,7 @@ uint32_t HELPER(neon_qneg_s16)(uint32_t x) } #undef DO_QNEG16 -uint32_t HELPER(neon_qabs_s32)(uint32_t x) +uint32_t HELPER(neon_qabs_s32)(CPUState *env, uint32_t x) { if (x == SIGNBIT) { SET_QC(); @@ -1756,7 +1760,7 @@ uint32_t HELPER(neon_qabs_s32)(uint32_t x) return x; } -uint32_t HELPER(neon_qneg_s32)(uint32_t x) +uint32_t HELPER(neon_qneg_s32)(CPUState *env, uint32_t x) { if (x == SIGNBIT) { SET_QC(); @@ -1828,7 +1832,7 @@ uint32_t HELPER(neon_acgt_f32)(uint32_t a, uint32_t b, void *fpstp) #define ELEM(V, N, SIZE) (((V) >> ((N) * (SIZE))) & ((1ull << (SIZE)) - 1)) -void HELPER(neon_qunzip8)(uint32_t rd, uint32_t rm) +void HELPER(neon_qunzip8)(CPUState *env, uint32_t rd, uint32_t rm) { uint64_t zm0 = float64_val(env->vfp.regs[rm]); uint64_t zm1 = float64_val(env->vfp.regs[rm + 1]); @@ -1856,7 +1860,7 @@ void HELPER(neon_qunzip8)(uint32_t rd, uint32_t rm) env->vfp.regs[rd + 1] = make_float64(d1); } -void HELPER(neon_qunzip16)(uint32_t rd, uint32_t rm) +void HELPER(neon_qunzip16)(CPUState *env, uint32_t rd, uint32_t rm) { uint64_t zm0 = float64_val(env->vfp.regs[rm]); uint64_t zm1 = float64_val(env->vfp.regs[rm + 1]); @@ -1876,7 +1880,7 @@ void HELPER(neon_qunzip16)(uint32_t rd, uint32_t rm) env->vfp.regs[rd + 1] = make_float64(d1); } -void HELPER(neon_qunzip32)(uint32_t rd, uint32_t rm) +void HELPER(neon_qunzip32)(CPUState *env, uint32_t rd, uint32_t rm) { uint64_t zm0 = float64_val(env->vfp.regs[rm]); uint64_t zm1 = float64_val(env->vfp.regs[rm + 1]); @@ -1892,7 +1896,7 @@ void HELPER(neon_qunzip32)(uint32_t rd, uint32_t rm) env->vfp.regs[rd + 1] = make_float64(d1); } -void HELPER(neon_unzip8)(uint32_t rd, uint32_t rm) +void HELPER(neon_unzip8)(CPUState *env, uint32_t rd, uint32_t rm) { uint64_t zm = float64_val(env->vfp.regs[rm]); uint64_t zd = float64_val(env->vfp.regs[rd]); @@ -1908,7 +1912,7 @@ void HELPER(neon_unzip8)(uint32_t rd, uint32_t rm) env->vfp.regs[rd] = make_float64(d0); } -void HELPER(neon_unzip16)(uint32_t rd, uint32_t rm) +void HELPER(neon_unzip16)(CPUState *env, uint32_t rd, uint32_t rm) { uint64_t zm = float64_val(env->vfp.regs[rm]); uint64_t zd = float64_val(env->vfp.regs[rd]); @@ -1920,7 +1924,7 @@ void HELPER(neon_unzip16)(uint32_t rd, uint32_t rm) env->vfp.regs[rd] = make_float64(d0); } -void HELPER(neon_qzip8)(uint32_t rd, uint32_t rm) +void HELPER(neon_qzip8)(CPUState *env, uint32_t rd, uint32_t rm) { uint64_t zm0 = float64_val(env->vfp.regs[rm]); uint64_t zm1 = float64_val(env->vfp.regs[rm + 1]); @@ -1948,7 +1952,7 @@ void HELPER(neon_qzip8)(uint32_t rd, uint32_t rm) env->vfp.regs[rd + 1] = make_float64(d1); } -void HELPER(neon_qzip16)(uint32_t rd, uint32_t rm) +void HELPER(neon_qzip16)(CPUState *env, uint32_t rd, uint32_t rm) { uint64_t zm0 = float64_val(env->vfp.regs[rm]); uint64_t zm1 = float64_val(env->vfp.regs[rm + 1]); @@ -1968,7 +1972,7 @@ void HELPER(neon_qzip16)(uint32_t rd, uint32_t rm) env->vfp.regs[rd + 1] = make_float64(d1); } -void HELPER(neon_qzip32)(uint32_t rd, uint32_t rm) +void HELPER(neon_qzip32)(CPUState *env, uint32_t rd, uint32_t rm) { uint64_t zm0 = float64_val(env->vfp.regs[rm]); uint64_t zm1 = float64_val(env->vfp.regs[rm + 1]); @@ -1984,7 +1988,7 @@ void HELPER(neon_qzip32)(uint32_t rd, uint32_t rm) env->vfp.regs[rd + 1] = make_float64(d1); } -void HELPER(neon_zip8)(uint32_t rd, uint32_t rm) +void HELPER(neon_zip8)(CPUState *env, uint32_t rd, uint32_t rm) { uint64_t zm = float64_val(env->vfp.regs[rm]); uint64_t zd = float64_val(env->vfp.regs[rd]); @@ -2000,7 +2004,7 @@ void HELPER(neon_zip8)(uint32_t rd, uint32_t rm) env->vfp.regs[rd] = make_float64(d0); } -void HELPER(neon_zip16)(uint32_t rd, uint32_t rm) +void HELPER(neon_zip16)(CPUState *env, uint32_t rd, uint32_t rm) { uint64_t zm = float64_val(env->vfp.regs[rm]); uint64_t zd = float64_val(env->vfp.regs[rd]); diff --git a/target-arm/translate.c b/target-arm/translate.c index 27d988733c..0273deb11a 100644 --- a/target-arm/translate.c +++ b/target-arm/translate.c @@ -3711,13 +3711,13 @@ static int gen_neon_unzip(int rd, int rm, int size, int q) if (q) { switch (size) { case 0: - gen_helper_neon_qunzip8(tmp, tmp2); + gen_helper_neon_qunzip8(cpu_env, tmp, tmp2); break; case 1: - gen_helper_neon_qunzip16(tmp, tmp2); + gen_helper_neon_qunzip16(cpu_env, tmp, tmp2); break; case 2: - gen_helper_neon_qunzip32(tmp, tmp2); + gen_helper_neon_qunzip32(cpu_env, tmp, tmp2); break; default: abort(); @@ -3725,10 +3725,10 @@ static int gen_neon_unzip(int rd, int rm, int size, int q) } else { switch (size) { case 0: - gen_helper_neon_unzip8(tmp, tmp2); + gen_helper_neon_unzip8(cpu_env, tmp, tmp2); break; case 1: - gen_helper_neon_unzip16(tmp, tmp2); + gen_helper_neon_unzip16(cpu_env, tmp, tmp2); break; default: abort(); @@ -3750,13 +3750,13 @@ static int gen_neon_zip(int rd, int rm, int size, int q) if (q) { switch (size) { case 0: - gen_helper_neon_qzip8(tmp, tmp2); + gen_helper_neon_qzip8(cpu_env, tmp, tmp2); break; case 1: - gen_helper_neon_qzip16(tmp, tmp2); + gen_helper_neon_qzip16(cpu_env, tmp, tmp2); break; case 2: - gen_helper_neon_qzip32(tmp, tmp2); + gen_helper_neon_qzip32(cpu_env, tmp, tmp2); break; default: abort(); @@ -3764,10 +3764,10 @@ static int gen_neon_zip(int rd, int rm, int size, int q) } else { switch (size) { case 0: - gen_helper_neon_zip8(tmp, tmp2); + gen_helper_neon_zip8(cpu_env, tmp, tmp2); break; case 1: - gen_helper_neon_zip16(tmp, tmp2); + gen_helper_neon_zip16(cpu_env, tmp, tmp2); break; default: abort(); @@ -4167,9 +4167,9 @@ static inline void gen_neon_narrow(int size, TCGv dest, TCGv_i64 src) static inline void gen_neon_narrow_sats(int size, TCGv dest, TCGv_i64 src) { switch (size) { - case 0: gen_helper_neon_narrow_sat_s8(dest, src); break; - case 1: gen_helper_neon_narrow_sat_s16(dest, src); break; - case 2: gen_helper_neon_narrow_sat_s32(dest, src); break; + case 0: gen_helper_neon_narrow_sat_s8(dest, cpu_env, src); break; + case 1: gen_helper_neon_narrow_sat_s16(dest, cpu_env, src); break; + case 2: gen_helper_neon_narrow_sat_s32(dest, cpu_env, src); break; default: abort(); } } @@ -4177,9 +4177,9 @@ static inline void gen_neon_narrow_sats(int size, TCGv dest, TCGv_i64 src) static inline void gen_neon_narrow_satu(int size, TCGv dest, TCGv_i64 src) { switch (size) { - case 0: gen_helper_neon_narrow_sat_u8(dest, src); break; - case 1: gen_helper_neon_narrow_sat_u16(dest, src); break; - case 2: gen_helper_neon_narrow_sat_u32(dest, src); break; + case 0: gen_helper_neon_narrow_sat_u8(dest, cpu_env, src); break; + case 1: gen_helper_neon_narrow_sat_u16(dest, cpu_env, src); break; + case 2: gen_helper_neon_narrow_sat_u32(dest, cpu_env, src); break; default: abort(); } } @@ -4187,9 +4187,9 @@ static inline void gen_neon_narrow_satu(int size, TCGv dest, TCGv_i64 src) static inline void gen_neon_unarrow_sats(int size, TCGv dest, TCGv_i64 src) { switch (size) { - case 0: gen_helper_neon_unarrow_sat8(dest, src); break; - case 1: gen_helper_neon_unarrow_sat16(dest, src); break; - case 2: gen_helper_neon_unarrow_sat32(dest, src); break; + case 0: gen_helper_neon_unarrow_sat8(dest, cpu_env, src); break; + case 1: gen_helper_neon_unarrow_sat16(dest, cpu_env, src); break; + case 2: gen_helper_neon_unarrow_sat32(dest, cpu_env, src); break; default: abort(); } } @@ -4281,8 +4281,8 @@ static inline void gen_neon_negl(TCGv_i64 var, int size) static inline void gen_neon_addl_saturate(TCGv_i64 op0, TCGv_i64 op1, int size) { switch (size) { - case 1: gen_helper_neon_addl_saturate_s32(op0, op0, op1); break; - case 2: gen_helper_neon_addl_saturate_s64(op0, op0, op1); break; + case 1: gen_helper_neon_addl_saturate_s32(op0, cpu_env, op0, op1); break; + case 2: gen_helper_neon_addl_saturate_s64(op0, cpu_env, op0, op1); break; default: abort(); } } @@ -4558,16 +4558,20 @@ static int disas_neon_data_insn(CPUState * env, DisasContext *s, uint32_t insn) switch (op) { case NEON_3R_VQADD: if (u) { - gen_helper_neon_qadd_u64(cpu_V0, cpu_V0, cpu_V1); + gen_helper_neon_qadd_u64(cpu_V0, cpu_env, + cpu_V0, cpu_V1); } else { - gen_helper_neon_qadd_s64(cpu_V0, cpu_V0, cpu_V1); + gen_helper_neon_qadd_s64(cpu_V0, cpu_env, + cpu_V0, cpu_V1); } break; case NEON_3R_VQSUB: if (u) { - gen_helper_neon_qsub_u64(cpu_V0, cpu_V0, cpu_V1); + gen_helper_neon_qsub_u64(cpu_V0, cpu_env, + cpu_V0, cpu_V1); } else { - gen_helper_neon_qsub_s64(cpu_V0, cpu_V0, cpu_V1); + gen_helper_neon_qsub_s64(cpu_V0, cpu_env, + cpu_V0, cpu_V1); } break; case NEON_3R_VSHL: @@ -4579,9 +4583,11 @@ static int disas_neon_data_insn(CPUState * env, DisasContext *s, uint32_t insn) break; case NEON_3R_VQSHL: if (u) { - gen_helper_neon_qshl_u64(cpu_V0, cpu_V1, cpu_V0); + gen_helper_neon_qshl_u64(cpu_V0, cpu_env, + cpu_V1, cpu_V0); } else { - gen_helper_neon_qshl_s64(cpu_V0, cpu_V1, cpu_V0); + gen_helper_neon_qshl_s64(cpu_V0, cpu_env, + cpu_V1, cpu_V0); } break; case NEON_3R_VRSHL: @@ -4593,9 +4599,11 @@ static int disas_neon_data_insn(CPUState * env, DisasContext *s, uint32_t insn) break; case NEON_3R_VQRSHL: if (u) { - gen_helper_neon_qrshl_u64(cpu_V0, cpu_V1, cpu_V0); + gen_helper_neon_qrshl_u64(cpu_V0, cpu_env, + cpu_V1, cpu_V0); } else { - gen_helper_neon_qrshl_s64(cpu_V0, cpu_V1, cpu_V0); + gen_helper_neon_qrshl_s64(cpu_V0, cpu_env, + cpu_V1, cpu_V0); } break; case NEON_3R_VADD_VSUB: @@ -4693,7 +4701,7 @@ static int disas_neon_data_insn(CPUState * env, DisasContext *s, uint32_t insn) GEN_NEON_INTEGER_OP(hadd); break; case NEON_3R_VQADD: - GEN_NEON_INTEGER_OP(qadd); + GEN_NEON_INTEGER_OP_ENV(qadd); break; case NEON_3R_VRHADD: GEN_NEON_INTEGER_OP(rhadd); @@ -4736,7 +4744,7 @@ static int disas_neon_data_insn(CPUState * env, DisasContext *s, uint32_t insn) GEN_NEON_INTEGER_OP(hsub); break; case NEON_3R_VQSUB: - GEN_NEON_INTEGER_OP(qsub); + GEN_NEON_INTEGER_OP_ENV(qsub); break; case NEON_3R_VCGT: GEN_NEON_INTEGER_OP(cgt); @@ -4748,13 +4756,13 @@ static int disas_neon_data_insn(CPUState * env, DisasContext *s, uint32_t insn) GEN_NEON_INTEGER_OP(shl); break; case NEON_3R_VQSHL: - GEN_NEON_INTEGER_OP(qshl); + GEN_NEON_INTEGER_OP_ENV(qshl); break; case NEON_3R_VRSHL: GEN_NEON_INTEGER_OP(rshl); break; case NEON_3R_VQRSHL: - GEN_NEON_INTEGER_OP(qrshl); + GEN_NEON_INTEGER_OP_ENV(qrshl); break; case NEON_3R_VMAX: GEN_NEON_INTEGER_OP(max); @@ -4836,14 +4844,22 @@ static int disas_neon_data_insn(CPUState * env, DisasContext *s, uint32_t insn) case NEON_3R_VQDMULH_VQRDMULH: /* Multiply high. */ if (!u) { /* VQDMULH */ switch (size) { - case 1: gen_helper_neon_qdmulh_s16(tmp, tmp, tmp2); break; - case 2: gen_helper_neon_qdmulh_s32(tmp, tmp, tmp2); break; + case 1: + gen_helper_neon_qdmulh_s16(tmp, cpu_env, tmp, tmp2); + break; + case 2: + gen_helper_neon_qdmulh_s32(tmp, cpu_env, tmp, tmp2); + break; default: abort(); } } else { /* VQRDMULH */ switch (size) { - case 1: gen_helper_neon_qrdmulh_s16(tmp, tmp, tmp2); break; - case 2: gen_helper_neon_qrdmulh_s32(tmp, tmp, tmp2); break; + case 1: + gen_helper_neon_qrdmulh_s16(tmp, cpu_env, tmp, tmp2); + break; + case 2: + gen_helper_neon_qrdmulh_s32(tmp, cpu_env, tmp, tmp2); + break; default: abort(); } } @@ -5035,14 +5051,15 @@ static int disas_neon_data_insn(CPUState * env, DisasContext *s, uint32_t insn) gen_helper_neon_shl_u64(cpu_V0, cpu_V0, cpu_V1); break; case 6: /* VQSHLU */ - gen_helper_neon_qshlu_s64(cpu_V0, cpu_V0, cpu_V1); + gen_helper_neon_qshlu_s64(cpu_V0, cpu_env, + cpu_V0, cpu_V1); break; case 7: /* VQSHL */ if (u) { - gen_helper_neon_qshl_u64(cpu_V0, + gen_helper_neon_qshl_u64(cpu_V0, cpu_env, cpu_V0, cpu_V1); } else { - gen_helper_neon_qshl_s64(cpu_V0, + gen_helper_neon_qshl_s64(cpu_V0, cpu_env, cpu_V0, cpu_V1); } break; @@ -5094,20 +5111,23 @@ static int disas_neon_data_insn(CPUState * env, DisasContext *s, uint32_t insn) case 6: /* VQSHLU */ switch (size) { case 0: - gen_helper_neon_qshlu_s8(tmp, tmp, tmp2); + gen_helper_neon_qshlu_s8(tmp, cpu_env, + tmp, tmp2); break; case 1: - gen_helper_neon_qshlu_s16(tmp, tmp, tmp2); + gen_helper_neon_qshlu_s16(tmp, cpu_env, + tmp, tmp2); break; case 2: - gen_helper_neon_qshlu_s32(tmp, tmp, tmp2); + gen_helper_neon_qshlu_s32(tmp, cpu_env, + tmp, tmp2); break; default: abort(); } break; case 7: /* VQSHL */ - GEN_NEON_INTEGER_OP(qshl); + GEN_NEON_INTEGER_OP_ENV(qshl); break; } tcg_temp_free_i32(tmp2); @@ -5616,15 +5636,15 @@ static int disas_neon_data_insn(CPUState * env, DisasContext *s, uint32_t insn) tmp2 = neon_load_reg(rn, pass); if (op == 12) { if (size == 1) { - gen_helper_neon_qdmulh_s16(tmp, tmp, tmp2); + gen_helper_neon_qdmulh_s16(tmp, cpu_env, tmp, tmp2); } else { - gen_helper_neon_qdmulh_s32(tmp, tmp, tmp2); + gen_helper_neon_qdmulh_s32(tmp, cpu_env, tmp, tmp2); } } else if (op == 13) { if (size == 1) { - gen_helper_neon_qrdmulh_s16(tmp, tmp, tmp2); + gen_helper_neon_qrdmulh_s16(tmp, cpu_env, tmp, tmp2); } else { - gen_helper_neon_qrdmulh_s32(tmp, tmp, tmp2); + gen_helper_neon_qrdmulh_s32(tmp, cpu_env, tmp, tmp2); } } else if (op & 1) { TCGv_ptr fpstatus = get_fpstatus_ptr(1); @@ -5996,17 +6016,29 @@ static int disas_neon_data_insn(CPUState * env, DisasContext *s, uint32_t insn) break; case NEON_2RM_VQABS: switch (size) { - case 0: gen_helper_neon_qabs_s8(tmp, tmp); break; - case 1: gen_helper_neon_qabs_s16(tmp, tmp); break; - case 2: gen_helper_neon_qabs_s32(tmp, tmp); break; + case 0: + gen_helper_neon_qabs_s8(tmp, cpu_env, tmp); + break; + case 1: + gen_helper_neon_qabs_s16(tmp, cpu_env, tmp); + break; + case 2: + gen_helper_neon_qabs_s32(tmp, cpu_env, tmp); + break; default: abort(); } break; case NEON_2RM_VQNEG: switch (size) { - case 0: gen_helper_neon_qneg_s8(tmp, tmp); break; - case 1: gen_helper_neon_qneg_s16(tmp, tmp); break; - case 2: gen_helper_neon_qneg_s32(tmp, tmp); break; + case 0: + gen_helper_neon_qneg_s8(tmp, cpu_env, tmp); + break; + case 1: + gen_helper_neon_qneg_s16(tmp, cpu_env, tmp); + break; + case 2: + gen_helper_neon_qneg_s32(tmp, cpu_env, tmp); + break; default: abort(); } break; From b501b5e461fbf3f5c6cd91c8c933e6f4de627bee Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Wed, 25 May 2011 13:14:56 +0000 Subject: [PATCH 015/209] Revert "Makefile.target: Allow target helpers to be in any *_helper.c file" Reverts commit 348883d4828d7434e1053407818598f7fb15e594, so the global env is no longer available to helper.c files other than op_helper.c. Signed-off-by: Peter Maydell --- Makefile.target | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Makefile.target b/Makefile.target index 03d3646b23..37bb28ed8a 100644 --- a/Makefile.target +++ b/Makefile.target @@ -91,7 +91,7 @@ tcg/tcg.o: cpu.h # HELPER_CFLAGS is used for all the code compiled with static register # variables -%_helper.o cpu-exec.o user-exec.o: QEMU_CFLAGS += $(HELPER_CFLAGS) +op_helper.o cpu-exec.o user-exec.o: QEMU_CFLAGS += $(HELPER_CFLAGS) # Note: this is a workaround. The real fix is to avoid compiling # cpu_signal_handler() in user-exec.c. From 74594c9d813e4d14e9c16cc71824d8905bedc19d Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Tue, 22 Mar 2011 12:16:16 +0000 Subject: [PATCH 016/209] target-arm: Minimal implementation of performance counters Newer Linux kernels assume the existence of the performance counter cp15 registers. Provide a minimal implementation of these registers. We support no events. This should be compliant with the ARM ARM, except that we don't implement the cycle counter. Signed-off-by: Peter Maydell --- target-arm/cpu.h | 8 +- target-arm/helper.c | 161 +++++++++++++++++++++++++++++++++++++---- target-arm/machine.c | 12 +++ target-arm/translate.c | 20 ++++- 4 files changed, 184 insertions(+), 17 deletions(-) diff --git a/target-arm/cpu.h b/target-arm/cpu.h index 01f5b57fbc..1276e6985a 100644 --- a/target-arm/cpu.h +++ b/target-arm/cpu.h @@ -133,6 +133,12 @@ typedef struct CPUARMState { uint32_t c7_par; /* Translation result. */ uint32_t c9_insn; /* Cache lockdown registers. */ uint32_t c9_data; + uint32_t c9_pmcr; /* performance monitor control register */ + uint32_t c9_pmcnten; /* perf monitor counter enables */ + uint32_t c9_pmovsr; /* perf monitor overflow status */ + uint32_t c9_pmxevtyper; /* perf monitor event type */ + uint32_t c9_pmuserenr; /* perf monitor user enable */ + uint32_t c9_pminten; /* perf monitor interrupt enables */ uint32_t c13_fcse; /* FCSE PID. */ uint32_t c13_context; /* Context ID. */ uint32_t c13_tls1; /* User RW Thread register. */ @@ -438,7 +444,7 @@ void cpu_arm_set_cp_io(CPUARMState *env, int cpnum, #define cpu_signal_handler cpu_arm_signal_handler #define cpu_list arm_cpu_list -#define CPU_SAVE_VERSION 3 +#define CPU_SAVE_VERSION 4 /* MMU modes definitions */ #define MMU_MODE0_SUFFIX _kernel diff --git a/target-arm/helper.c b/target-arm/helper.c index 9f14781d5d..9785cc5212 100644 --- a/target-arm/helper.c +++ b/target-arm/helper.c @@ -270,6 +270,10 @@ void cpu_reset(CPUARMState *env) } env->vfp.xregs[ARM_VFP_FPEXC] = 0; env->cp15.c2_base_mask = 0xffffc000u; + /* v7 performance monitor control register: same implementor + * field as main ID register, and we implement no event counters. + */ + env->cp15.c9_pmcr = (id & 0xff000000); #endif set_flush_to_zero(1, &env->vfp.standard_fp_status); set_flush_inputs_to_zero(1, &env->vfp.standard_fp_status); @@ -1588,6 +1592,81 @@ void HELPER(set_cp15)(CPUState *env, uint32_t insn, uint32_t val) case 1: /* TCM memory region registers. */ /* Not implemented. */ goto bad_reg; + case 12: /* Performance monitor control */ + /* Performance monitors are implementation defined in v7, + * but with an ARM recommended set of registers, which we + * follow (although we don't actually implement any counters) + */ + if (!arm_feature(env, ARM_FEATURE_V7)) { + goto bad_reg; + } + switch (op2) { + case 0: /* performance monitor control register */ + /* only the DP, X, D and E bits are writable */ + env->cp15.c9_pmcr &= ~0x39; + env->cp15.c9_pmcr |= (val & 0x39); + break; + case 1: /* Count enable set register */ + val &= (1 << 31); + env->cp15.c9_pmcnten |= val; + break; + case 2: /* Count enable clear */ + val &= (1 << 31); + env->cp15.c9_pmcnten &= ~val; + break; + case 3: /* Overflow flag status */ + env->cp15.c9_pmovsr &= ~val; + break; + case 4: /* Software increment */ + /* RAZ/WI since we don't implement the software-count event */ + break; + case 5: /* Event counter selection register */ + /* Since we don't implement any events, writing to this register + * is actually UNPREDICTABLE. So we choose to RAZ/WI. + */ + break; + default: + goto bad_reg; + } + break; + case 13: /* Performance counters */ + if (!arm_feature(env, ARM_FEATURE_V7)) { + goto bad_reg; + } + switch (op2) { + case 0: /* Cycle count register: not implemented, so RAZ/WI */ + break; + case 1: /* Event type select */ + env->cp15.c9_pmxevtyper = val & 0xff; + break; + case 2: /* Event count register */ + /* Unimplemented (we have no events), RAZ/WI */ + break; + default: + goto bad_reg; + } + break; + case 14: /* Performance monitor control */ + if (!arm_feature(env, ARM_FEATURE_V7)) { + goto bad_reg; + } + switch (op2) { + case 0: /* user enable */ + env->cp15.c9_pmuserenr = val & 1; + /* changes access rights for cp registers, so flush tbs */ + tb_flush(env); + break; + case 1: /* interrupt enable set */ + /* We have no event counters so only the C bit can be changed */ + val &= (1 << 31); + env->cp15.c9_pminten |= val; + break; + case 2: /* interrupt enable clear */ + val &= (1 << 31); + env->cp15.c9_pminten &= ~val; + break; + } + break; default: goto bad_reg; } @@ -1879,27 +1958,81 @@ uint32_t HELPER(get_cp15)(CPUState *env, uint32_t insn) return 0; case 8: /* MMU TLB control. */ goto bad_reg; - case 9: /* Cache lockdown. */ - switch (op1) { - case 0: /* L1 cache. */ - if (arm_feature(env, ARM_FEATURE_OMAPCP)) - return 0; - switch (op2) { - case 0: - return env->cp15.c9_data; - case 1: - return env->cp15.c9_insn; + case 9: + switch (crm) { + case 0: /* Cache lockdown */ + switch (op1) { + case 0: /* L1 cache. */ + if (arm_feature(env, ARM_FEATURE_OMAPCP)) { + return 0; + } + switch (op2) { + case 0: + return env->cp15.c9_data; + case 1: + return env->cp15.c9_insn; + default: + goto bad_reg; + } + case 1: /* L2 cache */ + if (crm != 0) { + goto bad_reg; + } + /* L2 Lockdown and Auxiliary control. */ + return 0; default: goto bad_reg; } - case 1: /* L2 cache */ - if (crm != 0) + break; + case 12: /* Performance monitor control */ + if (!arm_feature(env, ARM_FEATURE_V7)) { goto bad_reg; - /* L2 Lockdown and Auxiliary control. */ - return 0; + } + switch (op2) { + case 0: /* performance monitor control register */ + return env->cp15.c9_pmcr; + case 1: /* count enable set */ + case 2: /* count enable clear */ + return env->cp15.c9_pmcnten; + case 3: /* overflow flag status */ + return env->cp15.c9_pmovsr; + case 4: /* software increment */ + case 5: /* event counter selection register */ + return 0; /* Unimplemented, RAZ/WI */ + default: + goto bad_reg; + } + case 13: /* Performance counters */ + if (!arm_feature(env, ARM_FEATURE_V7)) { + goto bad_reg; + } + switch (op2) { + case 1: /* Event type select */ + return env->cp15.c9_pmxevtyper; + case 0: /* Cycle count register */ + case 2: /* Event count register */ + /* Unimplemented, so RAZ/WI */ + return 0; + default: + goto bad_reg; + } + case 14: /* Performance monitor control */ + if (!arm_feature(env, ARM_FEATURE_V7)) { + goto bad_reg; + } + switch (op2) { + case 0: /* user enable */ + return env->cp15.c9_pmuserenr; + case 1: /* interrupt enable set */ + case 2: /* interrupt enable clear */ + return env->cp15.c9_pminten; + default: + goto bad_reg; + } default: goto bad_reg; } + break; case 10: /* MMU TLB lockdown. */ /* ??? TLB lockdown not implemented. */ return 0; diff --git a/target-arm/machine.c b/target-arm/machine.c index a18b7dc67f..7d4fc545a6 100644 --- a/target-arm/machine.c +++ b/target-arm/machine.c @@ -44,6 +44,12 @@ void cpu_save(QEMUFile *f, void *opaque) qemu_put_be32(f, env->cp15.c7_par); qemu_put_be32(f, env->cp15.c9_insn); qemu_put_be32(f, env->cp15.c9_data); + qemu_put_be32(f, env->cp15.c9_pmcr); + qemu_put_be32(f, env->cp15.c9_pmcnten); + qemu_put_be32(f, env->cp15.c9_pmovsr); + qemu_put_be32(f, env->cp15.c9_pmxevtyper); + qemu_put_be32(f, env->cp15.c9_pmuserenr); + qemu_put_be32(f, env->cp15.c9_pminten); qemu_put_be32(f, env->cp15.c13_fcse); qemu_put_be32(f, env->cp15.c13_context); qemu_put_be32(f, env->cp15.c13_tls1); @@ -152,6 +158,12 @@ int cpu_load(QEMUFile *f, void *opaque, int version_id) env->cp15.c7_par = qemu_get_be32(f); env->cp15.c9_insn = qemu_get_be32(f); env->cp15.c9_data = qemu_get_be32(f); + env->cp15.c9_pmcr = qemu_get_be32(f); + env->cp15.c9_pmcnten = qemu_get_be32(f); + env->cp15.c9_pmovsr = qemu_get_be32(f); + env->cp15.c9_pmxevtyper = qemu_get_be32(f); + env->cp15.c9_pmuserenr = qemu_get_be32(f); + env->cp15.c9_pminten = qemu_get_be32(f); env->cp15.c13_fcse = qemu_get_be32(f); env->cp15.c13_context = qemu_get_be32(f); env->cp15.c13_tls1 = qemu_get_be32(f); diff --git a/target-arm/translate.c b/target-arm/translate.c index 0273deb11a..3e431e15cb 100644 --- a/target-arm/translate.c +++ b/target-arm/translate.c @@ -2472,12 +2472,28 @@ static int disas_cp_insn(CPUState *env, DisasContext *s, uint32_t insn) return 0; } -static int cp15_user_ok(uint32_t insn) +static int cp15_user_ok(CPUState *env, uint32_t insn) { int cpn = (insn >> 16) & 0xf; int cpm = insn & 0xf; int op = ((insn >> 5) & 7) | ((insn >> 18) & 0x38); + if (arm_feature(env, ARM_FEATURE_V7) && cpn == 9) { + /* Performance monitor registers fall into three categories: + * (a) always UNDEF in usermode + * (b) UNDEF only if PMUSERENR.EN is 0 + * (c) always read OK and UNDEF on write (PMUSERENR only) + */ + if ((cpm == 12 && (op < 6)) || + (cpm == 13 && (op < 3))) { + return env->cp15.c9_pmuserenr; + } else if (cpm == 14 && op == 0 && (insn & ARM_CP_RW_BIT)) { + /* PMUSERENR, read only */ + return 1; + } + return 0; + } + if (cpn == 13 && cpm == 0) { /* TLS register. */ if (op == 2 || (op == 3 && (insn & ARM_CP_RW_BIT))) @@ -2564,7 +2580,7 @@ static int disas_cp15_insn(CPUState *env, DisasContext *s, uint32_t insn) /* cdp */ return 1; } - if (IS_USER(s) && !cp15_user_ok(insn)) { + if (IS_USER(s) && !cp15_user_ok(env, insn)) { return 1; } From 82845826e89fdc02f6f000fca5d5019ec9be4ab3 Mon Sep 17 00:00:00 2001 From: Sebastian Huber Date: Sun, 29 May 2011 02:58:41 +0000 Subject: [PATCH 017/209] target-arm: Fix BASEPRI, BASEPRI_MAX, and FAULTMASK access Correct the decode of the register numbers for BASEPRI, BASEPRI_MAX and FAULTMASK, according to "ARMv7-M Architecture Reference Manual" issue D section "B5.2.3 MRS" and "B5.2.3 MSR". Signed-off-by: Sebastian Huber Signed-off-by: Peter Maydell --- target-arm/helper.c | 26 +++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/target-arm/helper.c b/target-arm/helper.c index 9785cc5212..a0f2314799 100644 --- a/target-arm/helper.c +++ b/target-arm/helper.c @@ -2128,11 +2128,11 @@ uint32_t HELPER(v7m_mrs)(CPUState *env, uint32_t reg) return env->v7m.current_sp ? env->regs[13] : env->v7m.other_sp; case 16: /* PRIMASK */ return (env->uncached_cpsr & CPSR_I) != 0; - case 17: /* FAULTMASK */ - return (env->uncached_cpsr & CPSR_F) != 0; - case 18: /* BASEPRI */ - case 19: /* BASEPRI_MAX */ + case 17: /* BASEPRI */ + case 18: /* BASEPRI_MAX */ return env->v7m.basepri; + case 19: /* FAULTMASK */ + return (env->uncached_cpsr & CPSR_F) != 0; case 20: /* CONTROL */ return env->v7m.control; default: @@ -2184,20 +2184,20 @@ void HELPER(v7m_msr)(CPUState *env, uint32_t reg, uint32_t val) else env->uncached_cpsr &= ~CPSR_I; break; - case 17: /* FAULTMASK */ + case 17: /* BASEPRI */ + env->v7m.basepri = val & 0xff; + break; + case 18: /* BASEPRI_MAX */ + val &= 0xff; + if (val != 0 && (val < env->v7m.basepri || env->v7m.basepri == 0)) + env->v7m.basepri = val; + break; + case 19: /* FAULTMASK */ if (val & 1) env->uncached_cpsr |= CPSR_F; else env->uncached_cpsr &= ~CPSR_F; break; - case 18: /* BASEPRI */ - env->v7m.basepri = val & 0xff; - break; - case 19: /* BASEPRI_MAX */ - val &= 0xff; - if (val != 0 && (val < env->v7m.basepri || env->v7m.basepri == 0)) - env->v7m.basepri = val; - break; case 20: /* CONTROL */ env->v7m.control = val & 3; switch_v7m_sp(env, (val & 2) != 0); From 3ab20e206ce74299e836bfec5ec27b7f261826be Mon Sep 17 00:00:00 2001 From: "Edgar E. Iglesias" Date: Tue, 28 Jun 2011 20:52:37 +0200 Subject: [PATCH 018/209] cris: Handle opcode zero It's a valid branch pc + 2. Signed-off-by: Edgar E. Iglesias --- target-cris/translate_v10.c | 3 --- 1 file changed, 3 deletions(-) diff --git a/target-cris/translate_v10.c b/target-cris/translate_v10.c index 5b14157d1a..637ac2084a 100644 --- a/target-cris/translate_v10.c +++ b/target-cris/translate_v10.c @@ -262,9 +262,6 @@ static unsigned int dec10_quick_imm(DisasContext *dc) break; case CRISV10_QIMM_BCC_R0: - if (!dc->ir) { - cpu_abort(dc->env, "opcode zero\n"); - } case CRISV10_QIMM_BCC_R1: case CRISV10_QIMM_BCC_R2: case CRISV10_QIMM_BCC_R3: From 1dfdcaa83f9ce34aded8bc0669e81753d94f1b7d Mon Sep 17 00:00:00 2001 From: "Edgar E. Iglesias" Date: Tue, 28 Jun 2011 20:57:09 +0200 Subject: [PATCH 019/209] user: Fix -d debug logging for usermode emulation Signed-off-by: Edgar E. Iglesias --- bsd-user/main.c | 2 +- darwin-user/main.c | 2 +- linux-user/main.c | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/bsd-user/main.c b/bsd-user/main.c index 5f790b255a..6018a419ed 100644 --- a/bsd-user/main.c +++ b/bsd-user/main.c @@ -866,7 +866,7 @@ int main(int argc, char **argv) int mask; const CPULogItem *item; - mask = cpu_str_to_log_mask(r); + mask = cpu_str_to_log_mask(log_mask); if (!mask) { printf("Log items (comma separated):\n"); for (item = cpu_log_items; item->mask != 0; item++) { diff --git a/darwin-user/main.c b/darwin-user/main.c index a6dc859219..35196a12cc 100644 --- a/darwin-user/main.c +++ b/darwin-user/main.c @@ -819,7 +819,7 @@ int main(int argc, char **argv) int mask; CPULogItem *item; - mask = cpu_str_to_log_mask(r); + mask = cpu_str_to_log_mask(log_mask); if (!mask) { printf("Log items (comma separated):\n"); for (item = cpu_log_items; item->mask != 0; item++) { diff --git a/linux-user/main.c b/linux-user/main.c index db5577bc50..289054b0b7 100644 --- a/linux-user/main.c +++ b/linux-user/main.c @@ -3030,7 +3030,7 @@ int main(int argc, char **argv, char **envp) int mask; const CPULogItem *item; - mask = cpu_str_to_log_mask(r); + mask = cpu_str_to_log_mask(log_mask); if (!mask) { printf("Log items (comma separated):\n"); for (item = cpu_log_items; item->mask != 0; item++) { From 20be39de592d883fc8b08d73349c70eb04e92f36 Mon Sep 17 00:00:00 2001 From: "Edgar E. Iglesias" Date: Thu, 30 Jun 2011 10:52:22 +0200 Subject: [PATCH 020/209] etraxfs-ser: Correct default value for RW_REC_CTRL Signed-off-by: Edgar E. Iglesias --- hw/etraxfs_ser.c | 24 +++++++++++++++++++----- 1 file changed, 19 insertions(+), 5 deletions(-) diff --git a/hw/etraxfs_ser.c b/hw/etraxfs_ser.c index 2787ebd5c8..b917d4db11 100644 --- a/hw/etraxfs_ser.c +++ b/hw/etraxfs_ser.c @@ -190,15 +190,23 @@ static void serial_event(void *opaque, int event) } -static int etraxfs_ser_init(SysBusDevice *dev) +static void etraxfs_ser_reset(DeviceState *d) { - struct etrax_serial *s = FROM_SYSBUS(typeof (*s), dev); - int ser_regs; + struct etrax_serial *s = container_of(d, typeof(*s), busdev.qdev); /* transmitter begins ready and idle. */ s->regs[RS_STAT_DIN] |= (1 << STAT_TR_RDY); s->regs[RS_STAT_DIN] |= (1 << STAT_TR_IDLE); + s->regs[RW_REC_CTRL] = 0x10000; + +} + +static int etraxfs_ser_init(SysBusDevice *dev) +{ + struct etrax_serial *s = FROM_SYSBUS(typeof (*s), dev); + int ser_regs; + sysbus_init_irq(dev, &s->irq); ser_regs = cpu_register_io_memory(ser_read, ser_write, s, DEVICE_NATIVE_ENDIAN); @@ -211,10 +219,16 @@ static int etraxfs_ser_init(SysBusDevice *dev) return 0; } +static SysBusDeviceInfo etraxfs_ser_info = { + .init = etraxfs_ser_init, + .qdev.name = "etraxfs,serial", + .qdev.size = sizeof(struct etrax_serial), + .qdev.reset = etraxfs_ser_reset, +}; + static void etraxfs_serial_register(void) { - sysbus_register_dev("etraxfs,serial", sizeof (struct etrax_serial), - etraxfs_ser_init); + sysbus_register_withprop(&etraxfs_ser_info); } device_init(etraxfs_serial_register) From a42bceec0939b2d5d0172733eb14afafe6cfabb3 Mon Sep 17 00:00:00 2001 From: Blue Swirl Date: Sat, 28 May 2011 07:49:35 +0000 Subject: [PATCH 021/209] TCG/HPPA: use TCG_REG_CALL_STACK instead of TCG_REG_SP Use TCG_REG_CALL_STACK instead of TCG_REG_SP for consistency. Acked-by: Richard Henderson Signed-off-by: Blue Swirl --- tcg/hppa/tcg-target.c | 30 ++++++++++++++++-------------- 1 file changed, 16 insertions(+), 14 deletions(-) diff --git a/tcg/hppa/tcg-target.c b/tcg/hppa/tcg-target.c index 79bca630c5..bd40954760 100644 --- a/tcg/hppa/tcg-target.c +++ b/tcg/hppa/tcg-target.c @@ -646,14 +646,14 @@ static void tcg_out_xmpyu(TCGContext *s, int retl, int reth, int arg1, int arg2) { /* Store both words into the stack for copy to the FPU. */ - tcg_out_ldst(s, arg1, TCG_REG_SP, STACK_TEMP_OFS, INSN_STW); - tcg_out_ldst(s, arg2, TCG_REG_SP, STACK_TEMP_OFS + 4, INSN_STW); + tcg_out_ldst(s, arg1, TCG_REG_CALL_STACK, STACK_TEMP_OFS, INSN_STW); + tcg_out_ldst(s, arg2, TCG_REG_CALL_STACK, STACK_TEMP_OFS + 4, INSN_STW); /* Load both words into the FPU at the same time. We get away with this because we can address the left and right half of the FPU registers individually once loaded. */ /* fldds stack_temp(sp),fr22 */ - tcg_out32(s, INSN_FLDDS | INSN_R2(TCG_REG_SP) + tcg_out32(s, INSN_FLDDS | INSN_R2(TCG_REG_CALL_STACK) | INSN_IM5(STACK_TEMP_OFS) | INSN_T(22)); /* xmpyu fr22r,fr22,fr22 */ @@ -661,15 +661,16 @@ static void tcg_out_xmpyu(TCGContext *s, int retl, int reth, /* Store the 64-bit result back into the stack. */ /* fstds stack_temp(sp),fr22 */ - tcg_out32(s, INSN_FSTDS | INSN_R2(TCG_REG_SP) + tcg_out32(s, INSN_FSTDS | INSN_R2(TCG_REG_CALL_STACK) | INSN_IM5(STACK_TEMP_OFS) | INSN_T(22)); /* Load the pieces of the result that the caller requested. */ if (reth) { - tcg_out_ldst(s, reth, TCG_REG_SP, STACK_TEMP_OFS, INSN_LDW); + tcg_out_ldst(s, reth, TCG_REG_CALL_STACK, STACK_TEMP_OFS, INSN_LDW); } if (retl) { - tcg_out_ldst(s, retl, TCG_REG_SP, STACK_TEMP_OFS + 4, INSN_LDW); + tcg_out_ldst(s, retl, TCG_REG_CALL_STACK, STACK_TEMP_OFS + 4, + INSN_LDW); } } @@ -1198,7 +1199,7 @@ static void tcg_out_qemu_st(TCGContext *s, const TCGArg *args, int opc) } tcg_out_mov(s, TCG_TYPE_I32, TCG_REG_R23, datahi_reg); tcg_out_mov(s, TCG_TYPE_I32, TCG_REG_R24, datalo_reg); - tcg_out_st(s, TCG_TYPE_I32, argreg, TCG_REG_SP, + tcg_out_st(s, TCG_TYPE_I32, argreg, TCG_REG_CALL_STACK, TCG_TARGET_CALL_STACK_OFFSET - 4); break; default: @@ -1616,16 +1617,16 @@ static void tcg_target_qemu_prologue(TCGContext *s) & -TCG_TARGET_STACK_ALIGN); /* The return address is stored in the caller's frame. */ - tcg_out_st(s, TCG_TYPE_PTR, TCG_REG_RP, TCG_REG_SP, -20); + tcg_out_st(s, TCG_TYPE_PTR, TCG_REG_RP, TCG_REG_CALL_STACK, -20); /* Allocate stack frame, saving the first register at the same time. */ tcg_out_ldst(s, tcg_target_callee_save_regs[0], - TCG_REG_SP, frame_size, INSN_STWM); + TCG_REG_CALL_STACK, frame_size, INSN_STWM); /* Save all callee saved registers. */ for (i = 1; i < ARRAY_SIZE(tcg_target_callee_save_regs); i++) { tcg_out_st(s, TCG_TYPE_PTR, tcg_target_callee_save_regs[i], - TCG_REG_SP, -frame_size + i * 4); + TCG_REG_CALL_STACK, -frame_size + i * 4); } #ifdef CONFIG_USE_GUEST_BASE @@ -1642,16 +1643,17 @@ static void tcg_target_qemu_prologue(TCGContext *s) tcg_out_mov(s, TCG_TYPE_I32, TCG_REG_R18, TCG_REG_R31); /* Restore callee saved registers. */ - tcg_out_ld(s, TCG_TYPE_PTR, TCG_REG_RP, TCG_REG_SP, -frame_size - 20); + tcg_out_ld(s, TCG_TYPE_PTR, TCG_REG_RP, TCG_REG_CALL_STACK, + -frame_size - 20); for (i = 1; i < ARRAY_SIZE(tcg_target_callee_save_regs); i++) { tcg_out_ld(s, TCG_TYPE_PTR, tcg_target_callee_save_regs[i], - TCG_REG_SP, -frame_size + i * 4); + TCG_REG_CALL_STACK, -frame_size + i * 4); } /* Deallocate stack frame and return. */ tcg_out32(s, INSN_BV | INSN_R2(TCG_REG_RP)); tcg_out_ldst(s, tcg_target_callee_save_regs[0], - TCG_REG_SP, -frame_size, INSN_LDWM); + TCG_REG_CALL_STACK, -frame_size, INSN_LDWM); } static void tcg_target_init(TCGContext *s) @@ -1678,7 +1680,7 @@ static void tcg_target_init(TCGContext *s) tcg_regset_set_reg(s->reserved_regs, TCG_REG_R19); /* clobbered w/o pic */ tcg_regset_set_reg(s->reserved_regs, TCG_REG_R20); /* reserved */ tcg_regset_set_reg(s->reserved_regs, TCG_REG_DP); /* data pointer */ - tcg_regset_set_reg(s->reserved_regs, TCG_REG_SP); /* stack pointer */ + tcg_regset_set_reg(s->reserved_regs, TCG_REG_CALL_STACK); /* stack pointer */ tcg_regset_set_reg(s->reserved_regs, TCG_REG_R31); /* ble link reg */ tcg_add_target_add_op_defs(hppa_op_defs); From 2a6a665f160e95b9acda89aedcf302d18872482d Mon Sep 17 00:00:00 2001 From: Blue Swirl Date: Sat, 28 May 2011 07:55:47 +0000 Subject: [PATCH 022/209] TCG/HPPA: use stack for TCG temps Use stack instead of temp_buf array in CPUState for TCG temps. Signed-off-by: Richard Henderson Signed-off-by: Blue Swirl --- tcg/hppa/tcg-target.c | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/tcg/hppa/tcg-target.c b/tcg/hppa/tcg-target.c index bd40954760..db33e792a4 100644 --- a/tcg/hppa/tcg-target.c +++ b/tcg/hppa/tcg-target.c @@ -1612,6 +1612,9 @@ static void tcg_target_qemu_prologue(TCGContext *s) /* Allocate space for the saved registers. */ frame_size += ARRAY_SIZE(tcg_target_callee_save_regs) * 4; + /* Allocate space for the TCG temps. */ + frame_size += CPU_TEMP_BUF_NLONGS * sizeof(long); + /* Align the allocated space. */ frame_size = ((frame_size + TCG_TARGET_STACK_ALIGN - 1) & -TCG_TARGET_STACK_ALIGN); @@ -1629,6 +1632,10 @@ static void tcg_target_qemu_prologue(TCGContext *s) TCG_REG_CALL_STACK, -frame_size + i * 4); } + /* Record the location of the TCG temps. */ + tcg_set_frame(s, TCG_REG_CALL_STACK, -frame_size + i * 4, + TCG_TEMP_BUF_NLONGS * sizeof(long)); + #ifdef CONFIG_USE_GUEST_BASE if (GUEST_BASE != 0) { tcg_out_movi(s, TCG_TYPE_PTR, TCG_GUEST_BASE_REG, GUEST_BASE); @@ -1684,6 +1691,4 @@ static void tcg_target_init(TCGContext *s) tcg_regset_set_reg(s->reserved_regs, TCG_REG_R31); /* ble link reg */ tcg_add_target_add_op_defs(hppa_op_defs); - tcg_set_frame(s, TCG_AREG0, offsetof(CPUState, temp_buf), - CPU_TEMP_BUF_NLONGS * sizeof(long)); } From 4d506cebd35be339a38df3f96e087fc761e4cdb4 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Mon, 20 Jun 2011 15:02:29 -0700 Subject: [PATCH 023/209] hppa: Fix printf warnings in hppa-dis.c. Signed-off-by: Richard Henderson Signed-off-by: Blue Swirl --- hppa-dis.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/hppa-dis.c b/hppa-dis.c index a5760a9584..435da73553 100644 --- a/hppa-dis.c +++ b/hppa-dis.c @@ -1771,13 +1771,13 @@ static const char *const add_compl_names[] = { 0, "", ",l", ",tsv" }; static void fput_reg (unsigned reg, disassemble_info *info) { - (*info->fprintf_func) (info->stream, reg ? reg_names[reg] : "r0"); + (*info->fprintf_func) (info->stream, "%s", reg ? reg_names[reg] : "r0"); } static void fput_fp_reg (unsigned reg, disassemble_info *info) { - (*info->fprintf_func) (info->stream, reg ? fp_reg_names[reg] : "fr0"); + (*info->fprintf_func) (info->stream, "%s", reg ? fp_reg_names[reg] : "fr0"); } static void @@ -1794,7 +1794,7 @@ fput_fp_reg_r (unsigned reg, disassemble_info *info) static void fput_creg (unsigned reg, disassemble_info *info) { - (*info->fprintf_func) (info->stream, control_reg[reg]); + (*info->fprintf_func) (info->stream, "%s", control_reg[reg]); } /* Print constants with sign. */ From ec1884298c96a8ce723880adb9e8ffe5e71fcf37 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Mon, 20 Jun 2011 15:10:49 -0700 Subject: [PATCH 024/209] tcg-hppa: Support deposit opcode. Signed-off-by: Richard Henderson Signed-off-by: Blue Swirl --- tcg/hppa/tcg-target.c | 24 ++++++++++++++++++++---- tcg/hppa/tcg-target.h | 1 + 2 files changed, 21 insertions(+), 4 deletions(-) diff --git a/tcg/hppa/tcg-target.c b/tcg/hppa/tcg-target.c index db33e792a4..222f33eb6d 100644 --- a/tcg/hppa/tcg-target.c +++ b/tcg/hppa/tcg-target.c @@ -467,6 +467,14 @@ static inline void tcg_out_dep(TCGContext *s, int ret, int arg, | INSN_SHDEP_CP(31 - ofs) | INSN_DEP_LEN(len)); } +static inline void tcg_out_depi(TCGContext *s, int ret, int arg, + unsigned ofs, unsigned len) +{ + assert(ofs < 32 && len <= 32 - ofs); + tcg_out32(s, INSN_DEPI | INSN_R2(ret) | INSN_IM5(arg) + | INSN_SHDEP_CP(31 - ofs) | INSN_DEP_LEN(len)); +} + static inline void tcg_out_shd(TCGContext *s, int ret, int hi, int lo, unsigned count) { @@ -499,8 +507,7 @@ static void tcg_out_ori(TCGContext *s, int ret, int arg, tcg_target_ulong m) assert(bs1 == 32 || (1ul << bs1) > m); tcg_out_mov(s, TCG_TYPE_I32, ret, arg); - tcg_out32(s, INSN_DEPI | INSN_R2(ret) | INSN_IM5(-1) - | INSN_SHDEP_CP(31 - bs0) | INSN_DEP_LEN(bs1 - bs0)); + tcg_out_depi(s, ret, -1, bs0, bs1 - bs0); } static void tcg_out_andi(TCGContext *s, int ret, int arg, tcg_target_ulong m) @@ -529,8 +536,7 @@ static void tcg_out_andi(TCGContext *s, int ret, int arg, tcg_target_ulong m) tcg_out_extr(s, ret, arg, 0, ls0, 0); } else { tcg_out_mov(s, TCG_TYPE_I32, ret, arg); - tcg_out32(s, INSN_DEPI | INSN_R2(ret) | INSN_IM5(0) - | INSN_SHDEP_CP(31 - ls0) | INSN_DEP_LEN(ls1 - ls0)); + tcg_out_depi(s, ret, 0, ls0, ls1 - ls0); } } @@ -1459,6 +1465,14 @@ static inline void tcg_out_op(TCGContext *s, TCGOpcode opc, const TCGArg *args, args[4], args[5], const_args[2], const_args[4]); break; + case INDEX_op_deposit_i32: + if (const_args[2]) { + tcg_out_depi(s, args[0], args[2], args[3], args[4]); + } else { + tcg_out_dep(s, args[0], args[2], args[3], args[4]); + } + break; + case INDEX_op_qemu_ld8u: tcg_out_qemu_ld(s, args, 0); break; @@ -1552,6 +1566,8 @@ static const TCGTargetOpDef hppa_op_defs[] = { { INDEX_op_add2_i32, { "r", "r", "rZ", "rZ", "rI", "rZ" } }, { INDEX_op_sub2_i32, { "r", "r", "rI", "rZ", "rK", "rZ" } }, + { INDEX_op_deposit_i32, { "r", "0", "rJ" } }, + #if TARGET_LONG_BITS == 32 { INDEX_op_qemu_ld8u, { "r", "L" } }, { INDEX_op_qemu_ld8s, { "r", "L" } }, diff --git a/tcg/hppa/tcg-target.h b/tcg/hppa/tcg-target.h index a5cc440d49..f7919ceef7 100644 --- a/tcg/hppa/tcg-target.h +++ b/tcg/hppa/tcg-target.h @@ -94,6 +94,7 @@ enum { #define TCG_TARGET_HAS_not_i32 #define TCG_TARGET_HAS_andc_i32 // #define TCG_TARGET_HAS_orc_i32 +#define TCG_TARGET_HAS_deposit_i32 /* optional instructions automatically implemented */ #undef TCG_TARGET_HAS_neg_i32 /* sub rd, 0, rs */ From 8e91ed308062e742610e4cfdfd4a09bc045ead45 Mon Sep 17 00:00:00 2001 From: Artyom Tarasenko Date: Fri, 1 Jul 2011 21:28:42 +0200 Subject: [PATCH 025/209] fix cpu_cc_src and cpu_cc_src2 corruption in udivx and sdivx udivx and sdvix don't modify condition flags, so they shall not overwrite cpu_cc_* Signed-off-by: Artyom Tarasenko Signed-off-by: Blue Swirl --- target-sparc/translate.c | 32 ++++++++++++++++++++++---------- 1 file changed, 22 insertions(+), 10 deletions(-) diff --git a/target-sparc/translate.c b/target-sparc/translate.c index 992cd77e72..f32a674f35 100644 --- a/target-sparc/translate.c +++ b/target-sparc/translate.c @@ -727,19 +727,24 @@ static inline void gen_trap_ifdivzero_tl(TCGv divisor) static inline void gen_op_sdivx(TCGv dst, TCGv src1, TCGv src2) { int l1, l2; + TCGv r_temp1, r_temp2; l1 = gen_new_label(); l2 = gen_new_label(); - tcg_gen_mov_tl(cpu_cc_src, src1); - tcg_gen_mov_tl(cpu_cc_src2, src2); - gen_trap_ifdivzero_tl(cpu_cc_src2); - tcg_gen_brcondi_tl(TCG_COND_NE, cpu_cc_src, INT64_MIN, l1); - tcg_gen_brcondi_tl(TCG_COND_NE, cpu_cc_src2, -1, l1); + r_temp1 = tcg_temp_local_new(); + r_temp2 = tcg_temp_local_new(); + tcg_gen_mov_tl(r_temp1, src1); + tcg_gen_mov_tl(r_temp2, src2); + gen_trap_ifdivzero_tl(r_temp2); + tcg_gen_brcondi_tl(TCG_COND_NE, r_temp1, INT64_MIN, l1); + tcg_gen_brcondi_tl(TCG_COND_NE, r_temp2, -1, l1); tcg_gen_movi_i64(dst, INT64_MIN); tcg_gen_br(l2); gen_set_label(l1); - tcg_gen_div_i64(dst, cpu_cc_src, cpu_cc_src2); + tcg_gen_div_i64(dst, r_temp1, r_temp2); gen_set_label(l2); + tcg_temp_free(r_temp1); + tcg_temp_free(r_temp2); } #endif @@ -3173,10 +3178,17 @@ static void disas_sparc_insn(DisasContext * dc) break; #ifdef TARGET_SPARC64 case 0xd: /* V9 udivx */ - tcg_gen_mov_tl(cpu_cc_src, cpu_src1); - tcg_gen_mov_tl(cpu_cc_src2, cpu_src2); - gen_trap_ifdivzero_tl(cpu_cc_src2); - tcg_gen_divu_i64(cpu_dst, cpu_cc_src, cpu_cc_src2); + { + TCGv r_temp1, r_temp2; + r_temp1 = tcg_temp_local_new(); + r_temp2 = tcg_temp_local_new(); + tcg_gen_mov_tl(r_temp1, cpu_src1); + tcg_gen_mov_tl(r_temp2, cpu_src2); + gen_trap_ifdivzero_tl(r_temp2); + tcg_gen_divu_i64(cpu_dst, r_temp1, r_temp2); + tcg_temp_free(r_temp1); + tcg_temp_free(r_temp2); + } break; #endif case 0xe: /* udiv */ From f74b32dec96b3298d0b33a76190ad8ea2db02869 Mon Sep 17 00:00:00 2001 From: Blue Swirl Date: Sat, 28 May 2011 08:27:20 +0000 Subject: [PATCH 026/209] TCG/PPC: use stack for TCG temps Use stack instead of temp_buf array in CPUState for TCG temps. Signed-off-by: Blue Swirl --- tcg/ppc/tcg-target.c | 7 +++++-- tcg/ppc64/tcg-target.c | 7 +++++-- 2 files changed, 10 insertions(+), 4 deletions(-) diff --git a/tcg/ppc/tcg-target.c b/tcg/ppc/tcg-target.c index 0cd887383d..58c8621709 100644 --- a/tcg/ppc/tcg-target.c +++ b/tcg/ppc/tcg-target.c @@ -907,9 +907,14 @@ static void tcg_target_qemu_prologue (TCGContext *s) + LINKAGE_AREA_SIZE + TCG_STATIC_CALL_ARGS_SIZE + ARRAY_SIZE (tcg_target_callee_save_regs) * 4 + + CPU_TEMP_BUF_NLONGS * sizeof(long) ; frame_size = (frame_size + 15) & ~15; + tcg_set_frame(s, TCG_REG_CALL_STACK, frame_size + - CPU_TEMP_BUF_NLONGS * sizeof(long), + CPU_TEMP_BUF_NLONGS * sizeof(long)); + #ifdef _CALL_AIX { uint32_t addr; @@ -1914,6 +1919,4 @@ static void tcg_target_init(TCGContext *s) #endif tcg_add_target_add_op_defs(ppc_op_defs); - tcg_set_frame(s, TCG_AREG0, offsetof(CPUState, temp_buf), - CPU_TEMP_BUF_NLONGS * sizeof(long)); } diff --git a/tcg/ppc64/tcg-target.c b/tcg/ppc64/tcg-target.c index 065c710f33..02a6cb2411 100644 --- a/tcg/ppc64/tcg-target.c +++ b/tcg/ppc64/tcg-target.c @@ -875,9 +875,14 @@ static void tcg_target_qemu_prologue (TCGContext *s) + 8 /* TOC save area */ + TCG_STATIC_CALL_ARGS_SIZE + ARRAY_SIZE (tcg_target_callee_save_regs) * 8 + + CPU_TEMP_BUF_NLONGS * sizeof(long) ; frame_size = (frame_size + 15) & ~15; + tcg_set_frame(s, TCG_REG_CALL_STACK, frame_size + - CPU_TEMP_BUF_NLONGS * sizeof(long), + CPU_TEMP_BUF_NLONGS * sizeof(long)); + #ifndef __APPLE__ /* First emit adhoc function descriptor */ addr = (uint64_t) s->code_ptr + 24; @@ -1691,6 +1696,4 @@ static void tcg_target_init (TCGContext *s) tcg_regset_set_reg (s->reserved_regs, TCG_REG_R13); tcg_add_target_add_op_defs (ppc_op_defs); - tcg_set_frame(s, TCG_AREG0, offsetof(CPUState, temp_buf), - CPU_TEMP_BUF_NLONGS * sizeof(long)); } From 75ef849696830fc2ddeff8bb90eea5887ff50df6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Herv=C3=A9=20Poussineau?= Date: Sat, 2 Jul 2011 17:23:00 +0200 Subject: [PATCH 027/209] esp: correctly fill bus id with requested lun MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This bug showed up after 1455084ea2c48abf23c4e4e15e378ee43457f381, and may be seen only on operating systems *not* using DMA to give commands to SCSI adapter. Signed-off-by: Hervé Poussineau Signed-off-by: Blue Swirl --- hw/esp.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hw/esp.c b/hw/esp.c index 6d3f5d239b..8e95672f0e 100644 --- a/hw/esp.c +++ b/hw/esp.c @@ -211,7 +211,7 @@ static uint32_t get_cmd(ESPState *s, uint8_t *buf) } else { dmalen = s->ti_size; memcpy(buf, s->ti_buf, dmalen); - buf[0] = 0; + buf[0] = buf[2] >> 5; } DPRINTF("get_cmd: len %d target %d\n", dmalen, target); From 638f4e47798725cef2a5ae5bf83d508fbde36605 Mon Sep 17 00:00:00 2001 From: Gerd Hoffmann Date: Wed, 29 Jun 2011 15:45:16 +0200 Subject: [PATCH 028/209] qxl: device id fixup Move device ID to PCIDeviceInfo. Remove support for the unused unstable device ID. Signed-off-by: Gerd Hoffmann --- hw/qxl.c | 11 +++-------- 1 file changed, 3 insertions(+), 8 deletions(-) diff --git a/hw/qxl.c b/hw/qxl.c index 16316f2bf5..e95d6f71c8 100644 --- a/hw/qxl.c +++ b/hw/qxl.c @@ -1207,7 +1207,6 @@ static DisplayChangeListener display_listener = { static int qxl_init_common(PCIQXLDevice *qxl) { uint8_t* config = qxl->pci.config; - uint32_t pci_device_id; uint32_t pci_device_rev; uint32_t io_size; @@ -1218,20 +1217,14 @@ static int qxl_init_common(PCIQXLDevice *qxl) switch (qxl->revision) { case 1: /* spice 0.4 -- qxl-1 */ - pci_device_id = QXL_DEVICE_ID_STABLE; pci_device_rev = QXL_REVISION_STABLE_V04; break; case 2: /* spice 0.6 -- qxl-2 */ - pci_device_id = QXL_DEVICE_ID_STABLE; + default: pci_device_rev = QXL_REVISION_STABLE_V06; break; - default: /* experimental */ - pci_device_id = QXL_DEVICE_ID_DEVEL; - pci_device_rev = 1; - break; } - pci_config_set_device_id(config, pci_device_id); pci_set_byte(&config[PCI_REVISION_ID], pci_device_rev); pci_set_byte(&config[PCI_INTERRUPT_PIN], 1); @@ -1492,6 +1485,7 @@ static PCIDeviceInfo qxl_info_primary = { .config_write = qxl_write_config, .romfile = "vgabios-qxl.bin", .vendor_id = REDHAT_PCI_VENDOR_ID, + .device_id = QXL_DEVICE_ID_STABLE, .class_id = PCI_CLASS_DISPLAY_VGA, .qdev.props = (Property[]) { DEFINE_PROP_UINT32("ram_size", PCIQXLDevice, vga.vram_size, 64 * 1024 * 1024), @@ -1512,6 +1506,7 @@ static PCIDeviceInfo qxl_info_secondary = { .qdev.vmsd = &qxl_vmstate, .init = qxl_init_secondary, .vendor_id = REDHAT_PCI_VENDOR_ID, + .device_id = QXL_DEVICE_ID_STABLE, .class_id = PCI_CLASS_DISPLAY_OTHER, .qdev.props = (Property[]) { DEFINE_PROP_UINT32("ram_size", PCIQXLDevice, vga.vram_size, 64 * 1024 * 1024), From fba810f1f67b411c209aa0e3d90724127cbd9c0f Mon Sep 17 00:00:00 2001 From: Gerd Hoffmann Date: Wed, 15 Jun 2011 13:11:33 +0200 Subject: [PATCH 029/209] spice: catch spice server initialization failures. When the spice server initialization fails report this and exit instead of ignoring the error. Signed-off-by: Gerd Hoffmann --- ui/spice-core.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/ui/spice-core.c b/ui/spice-core.c index dd9905be36..e142452bb6 100644 --- a/ui/spice-core.c +++ b/ui/spice-core.c @@ -602,7 +602,10 @@ void qemu_spice_init(void) qemu_opt_foreach(opts, add_channel, NULL, 0); - spice_server_init(spice_server, &core_interface); + if (0 != spice_server_init(spice_server, &core_interface)) { + fprintf(stderr, "failed to initialize spice server"); + exit(1); + }; using_spice = 1; migration_state.notify = migration_state_notifier; From 22795174a37e02200944c0d093d518e832650686 Mon Sep 17 00:00:00 2001 From: Alon Levy Date: Wed, 15 Jun 2011 20:44:38 +0200 Subject: [PATCH 030/209] qxl: set mm_time in vga update This fixes a problem where on windows 7 startup phase, before the qxl driver is loaded, the drawables are sufficiently large and video like to trigger a stream, but the lack of a filled mm time field triggers a warning in spice-gtk. Signed-off-by: Gerd Hoffmann --- ui/spice-display.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/ui/spice-display.c b/ui/spice-display.c index 15f0704eaf..feeee73dcc 100644 --- a/ui/spice-display.c +++ b/ui/spice-display.c @@ -70,6 +70,7 @@ static SimpleSpiceUpdate *qemu_spice_create_update(SimpleSpiceDisplay *ssd) QXLCommand *cmd; uint8_t *src, *dst; int by, bw, bh; + struct timespec time_space; if (qemu_spice_rect_is_empty(&ssd->dirty)) { return NULL; @@ -96,6 +97,10 @@ static SimpleSpiceUpdate *qemu_spice_create_update(SimpleSpiceDisplay *ssd) drawable->surfaces_dest[0] = -1; drawable->surfaces_dest[1] = -1; drawable->surfaces_dest[2] = -1; + clock_gettime(CLOCK_MONOTONIC, &time_space); + /* time in milliseconds from epoch. */ + drawable->mm_time = time_space.tv_sec * 1000 + + time_space.tv_nsec / 1000 / 1000; drawable->u.copy.rop_descriptor = SPICE_ROPD_OP_PUT; drawable->u.copy.src_bitmap = (intptr_t)image; From 5b77870ce0edde6cf6dd242fa892e28f5c87b4fd Mon Sep 17 00:00:00 2001 From: Alon Levy Date: Fri, 24 Jun 2011 15:02:47 +0200 Subject: [PATCH 031/209] qxl: interface_get_command: fix reported mode report correct mode when in undefined mode. introduces qxl_mode_to_string(), and uses it in other places too. Signed-off-by: Gerd Hoffmann --- hw/qxl.c | 25 +++++++++++++++++++++---- 1 file changed, 21 insertions(+), 4 deletions(-) diff --git a/hw/qxl.c b/hw/qxl.c index e95d6f71c8..3722f55ce2 100644 --- a/hw/qxl.c +++ b/hw/qxl.c @@ -336,6 +336,21 @@ static void interface_get_init_info(QXLInstance *sin, QXLDevInitInfo *info) info->n_surfaces = NUM_SURFACES; } +static const char *qxl_mode_to_string(int mode) +{ + switch (mode) { + case QXL_MODE_COMPAT: + return "compat"; + case QXL_MODE_NATIVE: + return "native"; + case QXL_MODE_UNDEFINED: + return "undefined"; + case QXL_MODE_VGA: + return "vga"; + } + return "INVALID"; +} + /* called from spice server thread context only */ static int interface_get_command(QXLInstance *sin, struct QXLCommandExt *ext) { @@ -358,18 +373,19 @@ static int interface_get_command(QXLInstance *sin, struct QXLCommandExt *ext) } qemu_mutex_unlock(&qxl->ssd.lock); if (ret) { + dprint(qxl, 2, "%s %s\n", __FUNCTION__, qxl_mode_to_string(qxl->mode)); qxl_log_command(qxl, "vga", ext); } return ret; case QXL_MODE_COMPAT: case QXL_MODE_NATIVE: case QXL_MODE_UNDEFINED: - dprint(qxl, 2, "%s: %s\n", __FUNCTION__, - qxl->cmdflags ? "compat" : "native"); + dprint(qxl, 4, "%s: %s\n", __FUNCTION__, qxl_mode_to_string(qxl->mode)); ring = &qxl->ram->cmd_ring; if (SPICE_RING_IS_EMPTY(ring)) { return false; } + dprint(qxl, 2, "%s: %s\n", __FUNCTION__, qxl_mode_to_string(qxl->mode)); SPICE_RING_CONS_ITEM(ring, cmd); ext->cmd = *cmd; ext->group_id = MEMSLOT_GROUP_GUEST; @@ -993,7 +1009,7 @@ static void ioport_write(void *opaque, uint32_t addr, uint32_t val) break; case QXL_IO_DESTROY_PRIMARY: PANIC_ON(val != 0); - dprint(d, 1, "QXL_IO_DESTROY_PRIMARY\n"); + dprint(d, 1, "QXL_IO_DESTROY_PRIMARY (%s)\n", qxl_mode_to_string(d->mode)); qxl_destroy_primary(d); break; case QXL_IO_DESTROY_SURFACE_WAIT: @@ -1368,7 +1384,8 @@ static int qxl_post_load(void *opaque, int version) d->modes = (QXLModes*)((uint8_t*)d->rom + d->rom->modes_offset); - dprint(d, 1, "%s: restore mode\n", __FUNCTION__); + dprint(d, 1, "%s: restore mode (%s)\n", __FUNCTION__, + qxl_mode_to_string(d->mode)); newmode = d->mode; d->mode = QXL_MODE_UNDEFINED; switch (newmode) { From 1f0ff2fb99eb5043ea38474c961e0fb9f6ff8a63 Mon Sep 17 00:00:00 2001 From: Alon Levy Date: Wed, 29 Jun 2011 13:57:13 +0200 Subject: [PATCH 032/209] qxl-logger: add timestamp to command log Signed-off-by: Gerd Hoffmann --- hw/qxl-logger.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/hw/qxl-logger.c b/hw/qxl-logger.c index 76f43e646c..74cadba302 100644 --- a/hw/qxl-logger.c +++ b/hw/qxl-logger.c @@ -19,6 +19,7 @@ * along with this program; if not, see . */ +#include "qemu-timer.h" #include "qxl.h" static const char *qxl_type[] = { @@ -223,7 +224,8 @@ void qxl_log_command(PCIQXLDevice *qxl, const char *ring, QXLCommandExt *ext) if (!qxl->cmdlog) { return; } - fprintf(stderr, "qxl-%d/%s:", qxl->id, ring); + fprintf(stderr, "%ld qxl-%d/%s:", qemu_get_clock_ns(vm_clock), + qxl->id, ring); fprintf(stderr, " cmd @ 0x%" PRIx64 " %s%s", ext->cmd.data, qxl_name(qxl_type, ext->cmd.type), compat ? "(compat)" : ""); From 6ebebb551ad1a5c4e24d3fccd246c5111450c1b3 Mon Sep 17 00:00:00 2001 From: Alon Levy Date: Wed, 29 Jun 2011 13:57:15 +0200 Subject: [PATCH 033/209] qxl: add dev id to guest prints Signed-off-by: Gerd Hoffmann --- hw/qxl.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/hw/qxl.c b/hw/qxl.c index 3722f55ce2..d55b68d872 100644 --- a/hw/qxl.c +++ b/hw/qxl.c @@ -985,7 +985,8 @@ static void ioport_write(void *opaque, uint32_t addr, uint32_t val) break; case QXL_IO_LOG: if (d->guestdebug) { - fprintf(stderr, "qxl/guest: %s", d->ram->log_buf); + fprintf(stderr, "qxl/guest-%d: %ld: %s", d->id, + qemu_get_clock_ns(vm_clock), d->ram->log_buf); } break; case QXL_IO_RESET: From 868379ce6994a72c617fbc29bcc95e8166833400 Mon Sep 17 00:00:00 2001 From: Yonit Halperin Date: Mon, 4 Jul 2011 15:08:01 +0300 Subject: [PATCH 034/209] qxl: make sure primary surface is saved on migration Signed-off-by: Gerd Hoffmann --- hw/qxl.c | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/hw/qxl.c b/hw/qxl.c index d55b68d872..5e4953676e 100644 --- a/hw/qxl.c +++ b/hw/qxl.c @@ -1184,11 +1184,14 @@ static void qxl_vm_change_state_handler(void *opaque, int running, int reason) qemu_spice_vm_change_state_handler(&qxl->ssd, running, reason); if (!running && qxl->mode == QXL_MODE_NATIVE) { - /* dirty all vram (which holds surfaces) to make sure it is saved */ + /* dirty all vram (which holds surfaces) and devram (primary surface) + * to make sure they are saved */ /* FIXME #1: should go out during "live" stage */ /* FIXME #2: we only need to save the areas which are actually used */ - ram_addr_t addr = qxl->vram_offset; - qxl_set_dirty(addr, addr + qxl->vram_size); + ram_addr_t vram_addr = qxl->vram_offset; + ram_addr_t surface0_addr = qxl->vga.vram_offset + qxl->shadow_rom.draw_area_offset; + qxl_set_dirty(vram_addr, vram_addr + qxl->vram_size); + qxl_set_dirty(surface0_addr, surface0_addr + qxl->shadow_rom.surface0_area_size); } } From 81144d1a3675faf3c314ff2c7a369f809361d3ed Mon Sep 17 00:00:00 2001 From: Gerd Hoffmann Date: Fri, 24 Jun 2011 12:23:44 +0200 Subject: [PATCH 035/209] qxl: put QXL_IO_UPDATE_IRQ into vgamode whitelist Signed-off-by: Gerd Hoffmann --- hw/qxl.c | 1 + 1 file changed, 1 insertion(+) diff --git a/hw/qxl.c b/hw/qxl.c index 5e4953676e..848c90fc97 100644 --- a/hw/qxl.c +++ b/hw/qxl.c @@ -942,6 +942,7 @@ static void ioport_write(void *opaque, uint32_t addr, uint32_t val) case QXL_IO_MEMSLOT_ADD: case QXL_IO_MEMSLOT_DEL: case QXL_IO_CREATE_PRIMARY: + case QXL_IO_UPDATE_IRQ: break; default: if (d->mode == QXL_MODE_NATIVE || d->mode == QXL_MODE_COMPAT) From a3d14054d727efb8ff4c5060a4c3171bae2046ef Mon Sep 17 00:00:00 2001 From: Alon Levy Date: Wed, 29 Jun 2011 13:57:11 +0200 Subject: [PATCH 036/209] qxl: allow QXL_IO_LOG also in vga The driver may change us to vga mode and still issue a QXL_IO_LOG, which we can easily support. Signed-off-by: Gerd Hoffmann --- hw/qxl.c | 1 + 1 file changed, 1 insertion(+) diff --git a/hw/qxl.c b/hw/qxl.c index 848c90fc97..0b9a4c71ec 100644 --- a/hw/qxl.c +++ b/hw/qxl.c @@ -943,6 +943,7 @@ static void ioport_write(void *opaque, uint32_t addr, uint32_t val) case QXL_IO_MEMSLOT_DEL: case QXL_IO_CREATE_PRIMARY: case QXL_IO_UPDATE_IRQ: + case QXL_IO_LOG: break; default: if (d->mode == QXL_MODE_NATIVE || d->mode == QXL_MODE_COMPAT) From 462a8bc6468912b79629f20f18798558342ce315 Mon Sep 17 00:00:00 2001 From: Stefan Weil Date: Thu, 23 Jun 2011 17:53:48 +0200 Subject: [PATCH 037/209] arm: Add const attribute to some arm_boot_info pointers Parameter 'info' is const, so add the missing attribute. v2: Add 'const' to the local variable info in do_cpu_reset() and to the boot_info field in CPUARMState (suggested by Peter Maydell). Cc: Andrzej Zaborowski Cc: Peter Maydell Signed-off-by: Stefan Weil Signed-off-by: Andrzej Zaborowski --- hw/arm-misc.h | 2 +- hw/arm_boot.c | 6 +++--- hw/nseries.c | 4 ++-- target-arm/cpu.h | 2 +- 4 files changed, 7 insertions(+), 7 deletions(-) diff --git a/hw/arm-misc.h b/hw/arm-misc.h index 010acb4cf9..9aeeaea759 100644 --- a/hw/arm-misc.h +++ b/hw/arm-misc.h @@ -31,7 +31,7 @@ struct arm_boot_info { target_phys_addr_t smp_priv_base; int nb_cpus; int board_id; - int (*atag_board)(struct arm_boot_info *info, void *p); + int (*atag_board)(const struct arm_boot_info *info, void *p); /* Used internally by arm_boot.c */ int is_linux; target_phys_addr_t initrd_size; diff --git a/hw/arm_boot.c b/hw/arm_boot.c index bfac982e65..e0215768b1 100644 --- a/hw/arm_boot.c +++ b/hw/arm_boot.c @@ -49,7 +49,7 @@ static uint32_t smpboot[] = { p += 4; \ } while (0) -static void set_kernel_args(struct arm_boot_info *info, +static void set_kernel_args(const struct arm_boot_info *info, int initrd_size, target_phys_addr_t base) { target_phys_addr_t p; @@ -102,7 +102,7 @@ static void set_kernel_args(struct arm_boot_info *info, WRITE_WORD(p, 0); } -static void set_kernel_args_old(struct arm_boot_info *info, +static void set_kernel_args_old(const struct arm_boot_info *info, int initrd_size, target_phys_addr_t base) { target_phys_addr_t p; @@ -178,7 +178,7 @@ static void set_kernel_args_old(struct arm_boot_info *info, static void do_cpu_reset(void *opaque) { CPUState *env = opaque; - struct arm_boot_info *info = env->boot_info; + const struct arm_boot_info *info = env->boot_info; cpu_reset(env); if (info) { diff --git a/hw/nseries.c b/hw/nseries.c index 2f6f473d64..2f84f5305b 100644 --- a/hw/nseries.c +++ b/hw/nseries.c @@ -1254,12 +1254,12 @@ static int n8x0_atag_setup(void *p, int model) return (void *) w - p; } -static int n800_atag_setup(struct arm_boot_info *info, void *p) +static int n800_atag_setup(const struct arm_boot_info *info, void *p) { return n8x0_atag_setup(p, 800); } -static int n810_atag_setup(struct arm_boot_info *info, void *p) +static int n810_atag_setup(const struct arm_boot_info *info, void *p) { return n8x0_atag_setup(p, 810); } diff --git a/target-arm/cpu.h b/target-arm/cpu.h index 116131eef8..1022a03753 100644 --- a/target-arm/cpu.h +++ b/target-arm/cpu.h @@ -221,7 +221,7 @@ typedef struct CPUARMState { void *opaque; } cp[15]; void *nvic; - struct arm_boot_info *boot_info; + const struct arm_boot_info *boot_info; } CPUARMState; CPUARMState *cpu_arm_init(const char *cpu_model); From 9312805d33e8b106bae356d13a8071fb37d75554 Mon Sep 17 00:00:00 2001 From: Vasily Khoruzhick Date: Fri, 17 Jun 2011 13:04:36 +0300 Subject: [PATCH 038/209] pxa2xx_lcd: add proper rotation support Until now, pxa2xx_lcd only supported 90deg rotation, but some machines (for example Zipit Z2) needs 270deg rotation. Signed-off-by: Vasily Khoruzhick Signed-off-by: Andrzej Zaborowski --- hw/framebuffer.c | 3 ++ hw/pxa2xx_lcd.c | 109 +++++++++++++++++++++++++++++++++++++++++------ input.c | 34 +++++++++++---- qemu-options.hx | 9 ++++ vl.c | 11 ++++- 5 files changed, 144 insertions(+), 22 deletions(-) diff --git a/hw/framebuffer.c b/hw/framebuffer.c index 24cdf25d0b..56cf16e27a 100644 --- a/hw/framebuffer.c +++ b/hw/framebuffer.c @@ -78,6 +78,9 @@ void framebuffer_update_display( dest = ds_get_data(ds); if (dest_col_pitch < 0) dest -= dest_col_pitch * (cols - 1); + if (dest_row_pitch < 0) { + dest -= dest_row_pitch * (rows - 1); + } first = -1; addr = pd; diff --git a/hw/pxa2xx_lcd.c b/hw/pxa2xx_lcd.c index e5248023f8..a5f8c51169 100644 --- a/hw/pxa2xx_lcd.c +++ b/hw/pxa2xx_lcd.c @@ -665,7 +665,7 @@ static void pxa2xx_palette_parse(PXA2xxLCDState *s, int ch, int bpp) } } -static void pxa2xx_lcdc_dma0_redraw_horiz(PXA2xxLCDState *s, +static void pxa2xx_lcdc_dma0_redraw_rot0(PXA2xxLCDState *s, target_phys_addr_t addr, int *miny, int *maxy) { int src_width, dest_width; @@ -692,7 +692,7 @@ static void pxa2xx_lcdc_dma0_redraw_horiz(PXA2xxLCDState *s, fn, s->dma_ch[0].palette, miny, maxy); } -static void pxa2xx_lcdc_dma0_redraw_vert(PXA2xxLCDState *s, +static void pxa2xx_lcdc_dma0_redraw_rot90(PXA2xxLCDState *s, target_phys_addr_t addr, int *miny, int *maxy) { int src_width, dest_width; @@ -720,6 +720,67 @@ static void pxa2xx_lcdc_dma0_redraw_vert(PXA2xxLCDState *s, miny, maxy); } +static void pxa2xx_lcdc_dma0_redraw_rot180(PXA2xxLCDState *s, + target_phys_addr_t addr, int *miny, int *maxy) +{ + int src_width, dest_width; + drawfn fn = NULL; + if (s->dest_width) { + fn = s->line_fn[s->transp][s->bpp]; + } + if (!fn) { + return; + } + + src_width = (s->xres + 3) & ~3; /* Pad to a 4 pixels multiple */ + if (s->bpp == pxa_lcdc_19pbpp || s->bpp == pxa_lcdc_18pbpp) { + src_width *= 3; + } else if (s->bpp > pxa_lcdc_16bpp) { + src_width *= 4; + } else if (s->bpp > pxa_lcdc_8bpp) { + src_width *= 2; + } + + dest_width = s->xres * s->dest_width; + *miny = 0; + framebuffer_update_display(s->ds, + addr, s->xres, s->yres, + src_width, -dest_width, -s->dest_width, + s->invalidated, + fn, s->dma_ch[0].palette, miny, maxy); +} + +static void pxa2xx_lcdc_dma0_redraw_rot270(PXA2xxLCDState *s, + target_phys_addr_t addr, int *miny, int *maxy) +{ + int src_width, dest_width; + drawfn fn = NULL; + if (s->dest_width) { + fn = s->line_fn[s->transp][s->bpp]; + } + if (!fn) { + return; + } + + src_width = (s->xres + 3) & ~3; /* Pad to a 4 pixels multiple */ + if (s->bpp == pxa_lcdc_19pbpp || s->bpp == pxa_lcdc_18pbpp) { + src_width *= 3; + } else if (s->bpp > pxa_lcdc_16bpp) { + src_width *= 4; + } else if (s->bpp > pxa_lcdc_8bpp) { + src_width *= 2; + } + + dest_width = s->yres * s->dest_width; + *miny = 0; + framebuffer_update_display(s->ds, + addr, s->xres, s->yres, + src_width, -s->dest_width, dest_width, + s->invalidated, + fn, s->dma_ch[0].palette, + miny, maxy); +} + static void pxa2xx_lcdc_resize(PXA2xxLCDState *s) { int width, height; @@ -730,10 +791,11 @@ static void pxa2xx_lcdc_resize(PXA2xxLCDState *s) height = LCCR2_LPP(s->control[2]) + 1; if (width != s->xres || height != s->yres) { - if (s->orientation) + if (s->orientation == 90 || s->orientation == 270) { qemu_console_resize(s->ds, height, width); - else + } else { qemu_console_resize(s->ds, width, height); + } s->invalidated = 1; s->xres = width; s->yres = height; @@ -797,10 +859,24 @@ static void pxa2xx_update_display(void *opaque) } if (miny >= 0) { - if (s->orientation) - dpy_update(s->ds, miny, 0, maxy - miny, s->xres); - else - dpy_update(s->ds, 0, miny, s->xres, maxy - miny); + switch (s->orientation) { + case 0: + dpy_update(s->ds, 0, miny, s->xres, maxy - miny + 1); + break; + case 90: + dpy_update(s->ds, miny, 0, maxy - miny + 1, s->xres); + break; + case 180: + maxy = s->yres - maxy - 1; + miny = s->yres - miny - 1; + dpy_update(s->ds, 0, maxy, s->xres, miny - maxy + 1); + break; + case 270: + maxy = s->yres - maxy - 1; + miny = s->yres - miny - 1; + dpy_update(s->ds, maxy, 0, miny - maxy + 1, s->xres); + break; + } } pxa2xx_lcdc_int_update(s); @@ -822,10 +898,19 @@ static void pxa2xx_lcdc_orientation(void *opaque, int angle) { PXA2xxLCDState *s = (PXA2xxLCDState *) opaque; - if (angle) { - s->dma_ch[0].redraw = pxa2xx_lcdc_dma0_redraw_vert; - } else { - s->dma_ch[0].redraw = pxa2xx_lcdc_dma0_redraw_horiz; + switch (angle) { + case 0: + s->dma_ch[0].redraw = pxa2xx_lcdc_dma0_redraw_rot0; + break; + case 90: + s->dma_ch[0].redraw = pxa2xx_lcdc_dma0_redraw_rot90; + break; + case 180: + s->dma_ch[0].redraw = pxa2xx_lcdc_dma0_redraw_rot180; + break; + case 270: + s->dma_ch[0].redraw = pxa2xx_lcdc_dma0_redraw_rot270; + break; } s->orientation = angle; diff --git a/input.c b/input.c index 5664d3a1e3..f0a02e783d 100644 --- a/input.c +++ b/input.c @@ -148,7 +148,7 @@ void kbd_mouse_event(int dx, int dy, int dz, int buttons_state) QEMUPutMouseEntry *entry; QEMUPutMouseEvent *mouse_event; void *mouse_event_opaque; - int width; + int width, height; if (QTAILQ_EMPTY(&mouse_handlers)) { return; @@ -160,15 +160,31 @@ void kbd_mouse_event(int dx, int dy, int dz, int buttons_state) mouse_event_opaque = entry->qemu_put_mouse_event_opaque; if (mouse_event) { - if (graphic_rotate) { - if (entry->qemu_put_mouse_event_absolute) { - width = 0x7fff; - } else { - width = graphic_width - 1; - } - mouse_event(mouse_event_opaque, width - dy, dx, dz, buttons_state); + if (entry->qemu_put_mouse_event_absolute) { + width = 0x7fff; + height = 0x7fff; } else { - mouse_event(mouse_event_opaque, dx, dy, dz, buttons_state); + width = graphic_width - 1; + height = graphic_height - 1; + } + + switch (graphic_rotate) { + case 0: + mouse_event(mouse_event_opaque, + dx, dy, dz, buttons_state); + break; + case 90: + mouse_event(mouse_event_opaque, + width - dy, dx, dz, buttons_state); + break; + case 180: + mouse_event(mouse_event_opaque, + width - dx, height - dy, dz, buttons_state); + break; + case 270: + mouse_event(mouse_event_opaque, + dy, height - dx, dz, buttons_state); + break; } } } diff --git a/qemu-options.hx b/qemu-options.hx index 37e54ee27a..e6d7adc3af 100644 --- a/qemu-options.hx +++ b/qemu-options.hx @@ -787,6 +787,15 @@ STEXI Rotate graphical output 90 deg left (only PXA LCD). ETEXI +DEF("rotate", HAS_ARG, QEMU_OPTION_rotate, + "-rotate rotate graphical output some deg left (only PXA LCD)\n", + QEMU_ARCH_ALL) +STEXI +@item -rotate +@findex -rotate +Rotate graphical output some deg left (only PXA LCD). +ETEXI + DEF("vga", HAS_ARG, QEMU_OPTION_vga, "-vga [std|cirrus|vmware|qxl|xenfb|none]\n" " select video card type\n", QEMU_ARCH_ALL) diff --git a/vl.c b/vl.c index 52402a2b73..fcd7395dd8 100644 --- a/vl.c +++ b/vl.c @@ -2300,7 +2300,16 @@ int main(int argc, char **argv, char **envp) #endif break; case QEMU_OPTION_portrait: - graphic_rotate = 1; + graphic_rotate = 90; + break; + case QEMU_OPTION_rotate: + graphic_rotate = strtol(optarg, (char **) &optarg, 10); + if (graphic_rotate != 0 && graphic_rotate != 90 && + graphic_rotate != 180 && graphic_rotate != 270) { + fprintf(stderr, + "qemu: only 90, 180, 270 deg rotation is available\n"); + exit(1); + } break; case QEMU_OPTION_kernel: kernel_filename = optarg; From 02854532c215872b2af5ed48af93b7a309de1b1b Mon Sep 17 00:00:00 2001 From: Kevin Wolf Date: Tue, 28 Jun 2011 13:41:07 +0200 Subject: [PATCH 039/209] Documentation: Remove outdated host_device note People shouldn't explicitly specify host_device any more. raw is doing the Right Thing. Signed-off-by: Kevin Wolf --- qemu-img.texi | 6 ------ 1 file changed, 6 deletions(-) diff --git a/qemu-img.texi b/qemu-img.texi index ced64a40ed..526474c112 100644 --- a/qemu-img.texi +++ b/qemu-img.texi @@ -173,12 +173,6 @@ Linux or NTFS on Windows), then only the written sectors will reserve space. Use @code{qemu-img info} to know the real size used by the image or @code{ls -ls} on Unix/Linux. -@item host_device - -Host device format. This format should be used instead of raw when -converting to block devices or other devices where "holes" are not -supported. - @item qcow2 QEMU image format, the most versatile format. Use it to have smaller images (useful if your filesystem does not supports holes, for example From 661a0f712b7c5b78408d85ba8b7f47ce44f395c0 Mon Sep 17 00:00:00 2001 From: Federico Simoncelli Date: Mon, 20 Jun 2011 12:48:19 -0400 Subject: [PATCH 040/209] qemu-img: Add cache command line option qemu-img currently writes disk images using writeback and filling up the cache buffers which are then flushed by the kernel preventing other processes from accessing the storage. This is particularly bad in cluster environments where time-based algorithms might be in place and accessing the storage within certain timeouts is critical. This patch adds the option to choose a cache method when writing disk images. Signed-off-by: Federico Simoncelli Signed-off-by: Kevin Wolf --- qemu-img-cmds.hx | 6 ++-- qemu-img.c | 80 ++++++++++++++++++++++++++++++++++++++++-------- 2 files changed, 70 insertions(+), 16 deletions(-) diff --git a/qemu-img-cmds.hx b/qemu-img-cmds.hx index 3072d383cf..2b70618c70 100644 --- a/qemu-img-cmds.hx +++ b/qemu-img-cmds.hx @@ -22,13 +22,13 @@ STEXI ETEXI DEF("commit", img_commit, - "commit [-f fmt] filename") + "commit [-f fmt] [-t cache] filename") STEXI @item commit [-f @var{fmt}] @var{filename} ETEXI DEF("convert", img_convert, - "convert [-c] [-p] [-f fmt] [-O output_fmt] [-o options] [-s snapshot_name] filename [filename2 [...]] output_filename") + "convert [-c] [-p] [-f fmt] [-t cache] [-O output_fmt] [-o options] [-s snapshot_name] filename [filename2 [...]] output_filename") STEXI @item convert [-c] [-f @var{fmt}] [-O @var{output_fmt}] [-o @var{options}] [-s @var{snapshot_name}] @var{filename} [@var{filename2} [...]] @var{output_filename} ETEXI @@ -46,7 +46,7 @@ STEXI ETEXI DEF("rebase", img_rebase, - "rebase [-f fmt] [-p] [-u] -b backing_file [-F backing_fmt] filename") + "rebase [-f fmt] [-t cache] [-p] [-u] -b backing_file [-F backing_fmt] filename") STEXI @item rebase [-f @var{fmt}] [-u] -b @var{backing_file} [-F @var{backing_fmt}] @var{filename} ETEXI diff --git a/qemu-img.c b/qemu-img.c index 32628b3110..54137a4e92 100644 --- a/qemu-img.c +++ b/qemu-img.c @@ -40,6 +40,7 @@ typedef struct img_cmd_t { /* Default to cache=writeback as data integrity is not important for qemu-tcg. */ #define BDRV_O_FLAGS BDRV_O_CACHE_WB +#define BDRV_DEFAULT_CACHE "writeback" static void format_print(void *opaque, const char *name) { @@ -64,6 +65,8 @@ static void help(void) "Command parameters:\n" " 'filename' is a disk image filename\n" " 'fmt' is the disk image format. It is guessed automatically in most cases\n" + " 'cache' is the cache mode used to write the output disk image, the valid\n" + " options are: 'none', 'writeback' (default), 'writethrough' and 'unsafe'\n" " 'size' is the disk image size in bytes. Optional suffixes\n" " 'k' or 'K' (kilobyte, 1024), 'M' (megabyte, 1024k), 'G' (gigabyte, 1024M)\n" " and T (terabyte, 1024G) are supported. 'b' is ignored.\n" @@ -180,6 +183,27 @@ static int read_password(char *buf, int buf_size) } #endif +static int set_cache_flag(const char *mode, int *flags) +{ + *flags &= ~BDRV_O_CACHE_MASK; + + if (!strcmp(mode, "none") || !strcmp(mode, "off")) { + *flags |= BDRV_O_CACHE_WB; + *flags |= BDRV_O_NOCACHE; + } else if (!strcmp(mode, "writeback")) { + *flags |= BDRV_O_CACHE_WB; + } else if (!strcmp(mode, "unsafe")) { + *flags |= BDRV_O_CACHE_WB; + *flags |= BDRV_O_NO_FLUSH; + } else if (!strcmp(mode, "writethrough")) { + /* this is the default */ + } else { + return -1; + } + + return 0; +} + static int print_block_option_help(const char *filename, const char *fmt) { BlockDriver *drv, *proto_drv; @@ -441,13 +465,14 @@ static int img_check(int argc, char **argv) static int img_commit(int argc, char **argv) { - int c, ret; - const char *filename, *fmt; + int c, ret, flags; + const char *filename, *fmt, *cache; BlockDriverState *bs; fmt = NULL; + cache = BDRV_DEFAULT_CACHE; for(;;) { - c = getopt(argc, argv, "f:h"); + c = getopt(argc, argv, "f:ht:"); if (c == -1) { break; } @@ -459,6 +484,9 @@ static int img_commit(int argc, char **argv) case 'f': fmt = optarg; break; + case 't': + cache = optarg; + break; } } if (optind >= argc) { @@ -466,7 +494,14 @@ static int img_commit(int argc, char **argv) } filename = argv[optind++]; - bs = bdrv_new_open(filename, fmt, BDRV_O_FLAGS | BDRV_O_RDWR); + flags = BDRV_O_RDWR; + ret = set_cache_flag(cache, &flags); + if (ret < 0) { + error_report("Invalid cache option: %s", cache); + return -1; + } + + bs = bdrv_new_open(filename, fmt, flags); if (!bs) { return 1; } @@ -591,8 +626,8 @@ static int compare_sectors(const uint8_t *buf1, const uint8_t *buf2, int n, static int img_convert(int argc, char **argv) { int c, ret = 0, n, n1, bs_n, bs_i, compress, cluster_size, cluster_sectors; - int progress = 0; - const char *fmt, *out_fmt, *out_baseimg, *out_filename; + int progress = 0, flags; + const char *fmt, *out_fmt, *cache, *out_baseimg, *out_filename; BlockDriver *drv, *proto_drv; BlockDriverState **bs = NULL, *out_bs = NULL; int64_t total_sectors, nb_sectors, sector_num, bs_offset; @@ -608,10 +643,11 @@ static int img_convert(int argc, char **argv) fmt = NULL; out_fmt = "raw"; + cache = "unsafe"; out_baseimg = NULL; compress = 0; for(;;) { - c = getopt(argc, argv, "f:O:B:s:hce6o:p"); + c = getopt(argc, argv, "f:O:B:s:hce6o:pt:"); if (c == -1) { break; } @@ -649,6 +685,9 @@ static int img_convert(int argc, char **argv) case 'p': progress = 1; break; + case 't': + cache = optarg; + break; } } @@ -779,8 +818,14 @@ static int img_convert(int argc, char **argv) goto out; } - out_bs = bdrv_new_open(out_filename, out_fmt, - BDRV_O_FLAGS | BDRV_O_RDWR | BDRV_O_NO_FLUSH); + flags = BDRV_O_RDWR; + ret = set_cache_flag(cache, &flags); + if (ret < 0) { + error_report("Invalid cache option: %s", cache); + return -1; + } + + out_bs = bdrv_new_open(out_filename, out_fmt, flags); if (!out_bs) { ret = -1; goto out; @@ -1225,18 +1270,18 @@ static int img_rebase(int argc, char **argv) BlockDriverState *bs, *bs_old_backing = NULL, *bs_new_backing = NULL; BlockDriver *old_backing_drv, *new_backing_drv; char *filename; - const char *fmt, *out_basefmt, *out_baseimg; + const char *fmt, *cache, *out_basefmt, *out_baseimg; int c, flags, ret; int unsafe = 0; int progress = 0; /* Parse commandline parameters */ fmt = NULL; + cache = BDRV_DEFAULT_CACHE; out_baseimg = NULL; out_basefmt = NULL; - for(;;) { - c = getopt(argc, argv, "uhf:F:b:p"); + c = getopt(argc, argv, "uhf:F:b:pt:"); if (c == -1) { break; } @@ -1260,6 +1305,9 @@ static int img_rebase(int argc, char **argv) case 'p': progress = 1; break; + case 't': + cache = optarg; + break; } } @@ -1271,13 +1319,19 @@ static int img_rebase(int argc, char **argv) qemu_progress_init(progress, 2.0); qemu_progress_print(0, 100); + flags = BDRV_O_RDWR | (unsafe ? BDRV_O_NO_BACKING : 0); + ret = set_cache_flag(cache, &flags); + if (ret < 0) { + error_report("Invalid cache option: %s", cache); + return -1; + } + /* * Open the images. * * Ignore the old backing file for unsafe rebase in case we want to correct * the reference to a renamed or moved backing file. */ - flags = BDRV_O_FLAGS | BDRV_O_RDWR | (unsafe ? BDRV_O_NO_BACKING : 0); bs = bdrv_new_open(filename, fmt, flags); if (!bs) { return 1; From 343f85685ce3f9613c13e4d0f884698194b6a4f6 Mon Sep 17 00:00:00 2001 From: Johannes Stezenbach Date: Wed, 29 Jun 2011 16:25:17 +0200 Subject: [PATCH 041/209] block/raw-posix: Linux compat-ioctl warning workaround On Linux x86_64 host with 32bit userspace, running qemu or even just "qemu-img create -f qcow2 some.img 1G" causes a kernel warning: ioctl32(qemu-img:5296): Unknown cmd fd(3) cmd(00005326){t:'S';sz:0} arg(7fffffff) on some.img ioctl32(qemu-img:5296): Unknown cmd fd(3) cmd(801c0204){t:02;sz:28} arg(fff77350) on some.img ioctl 00005326 is CDROM_DRIVE_STATUS, ioctl 801c0204 is FDGETPRM. The warning appears because the Linux compat-ioctl handler for these ioctls only applies to block devices, while qemu also uses the ioctls on plain files. Work around by calling fstat() the ensure the ioctls are only used on block devices. Signed-off-by: Johannes Stezenbach Signed-off-by: Kevin Wolf --- block/raw-posix.c | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/block/raw-posix.c b/block/raw-posix.c index 4cd7d7afbb..34b64aa205 100644 --- a/block/raw-posix.c +++ b/block/raw-posix.c @@ -46,6 +46,8 @@ #include #endif #ifdef __linux__ +#include +#include #include #include #include @@ -1188,6 +1190,7 @@ static int floppy_probe_device(const char *filename) int fd, ret; int prio = 0; struct floppy_struct fdparam; + struct stat st; if (strstart(filename, "/dev/fd", NULL)) prio = 50; @@ -1196,12 +1199,17 @@ static int floppy_probe_device(const char *filename) if (fd < 0) { goto out; } + ret = fstat(fd, &st); + if (ret == -1 || !S_ISBLK(st.st_mode)) { + goto outc; + } /* Attempt to detect via a floppy specific ioctl */ ret = ioctl(fd, FDGETPRM, &fdparam); if (ret >= 0) prio = 100; +outc: close(fd); out: return prio; @@ -1290,17 +1298,23 @@ static int cdrom_probe_device(const char *filename) { int fd, ret; int prio = 0; + struct stat st; fd = open(filename, O_RDONLY | O_NONBLOCK); if (fd < 0) { goto out; } + ret = fstat(fd, &st); + if (ret == -1 || !S_ISBLK(st.st_mode)) { + goto outc; + } /* Attempt to detect via a CDROM specific ioctl */ ret = ioctl(fd, CDROM_DRIVE_STATUS, CDSL_CURRENT); if (ret >= 0) prio = 100; +outc: close(fd); out: return prio; From a8686a9b2b347d0619141e950221c4d6d0311d0b Mon Sep 17 00:00:00 2001 From: Markus Armbruster Date: Mon, 20 Jun 2011 11:35:18 +0200 Subject: [PATCH 042/209] virtio-blk: Turn drive serial into a qdev property It needs to be a qdev property, because it belongs to the drive's guest part. Precedence: commit a0fef654 and 6ced55a5. Bonus: info qtree now shows the serial number. Signed-off-by: Markus Armbruster Signed-off-by: Kevin Wolf --- hw/s390-virtio-bus.c | 4 +++- hw/s390-virtio-bus.h | 1 + hw/virtio-blk.c | 29 +++++++++++++++++++---------- hw/virtio-blk.h | 2 ++ hw/virtio-pci.c | 4 +++- hw/virtio-pci.h | 1 + hw/virtio.h | 3 ++- 7 files changed, 31 insertions(+), 13 deletions(-) diff --git a/hw/s390-virtio-bus.c b/hw/s390-virtio-bus.c index d4a12f7531..2bf4821a11 100644 --- a/hw/s390-virtio-bus.c +++ b/hw/s390-virtio-bus.c @@ -128,7 +128,8 @@ static int s390_virtio_blk_init(VirtIOS390Device *dev) { VirtIODevice *vdev; - vdev = virtio_blk_init((DeviceState *)dev, &dev->block); + vdev = virtio_blk_init((DeviceState *)dev, &dev->block, + &dev->block_serial); if (!vdev) { return -1; } @@ -355,6 +356,7 @@ static VirtIOS390DeviceInfo s390_virtio_blk = { .qdev.size = sizeof(VirtIOS390Device), .qdev.props = (Property[]) { DEFINE_BLOCK_PROPERTIES(VirtIOS390Device, block), + DEFINE_PROP_STRING("serial", VirtIOS390Device, block_serial), DEFINE_PROP_END_OF_LIST(), }, }; diff --git a/hw/s390-virtio-bus.h b/hw/s390-virtio-bus.h index 0c412d08e1..f1bece738b 100644 --- a/hw/s390-virtio-bus.h +++ b/hw/s390-virtio-bus.h @@ -42,6 +42,7 @@ typedef struct VirtIOS390Device { uint8_t feat_len; VirtIODevice *vdev; BlockConf block; + char *block_serial; NICConf nic; uint32_t host_features; virtio_serial_conf serial; diff --git a/hw/virtio-blk.c b/hw/virtio-blk.c index 91e0394af9..6471ac85ab 100644 --- a/hw/virtio-blk.c +++ b/hw/virtio-blk.c @@ -28,8 +28,8 @@ typedef struct VirtIOBlock void *rq; QEMUBH *bh; BlockConf *conf; + char *serial; unsigned short sector_mask; - char sn[BLOCK_SERIAL_STRLEN]; DeviceState *qdev; } VirtIOBlock; @@ -362,8 +362,13 @@ static void virtio_blk_handle_request(VirtIOBlockReq *req, } else if (type & VIRTIO_BLK_T_GET_ID) { VirtIOBlock *s = req->dev; - memcpy(req->elem.in_sg[0].iov_base, s->sn, - MIN(req->elem.in_sg[0].iov_len, sizeof(s->sn))); + /* + * NB: per existing s/n string convention the string is + * terminated by '\0' only when shorter than buffer. + */ + strncpy(req->elem.in_sg[0].iov_base, + s->serial ? s->serial : "", + MIN(req->elem.in_sg[0].iov_len, VIRTIO_BLK_ID_BYTES)); virtio_blk_req_complete(req, VIRTIO_BLK_S_OK); } else if (type & VIRTIO_BLK_T_OUT) { qemu_iovec_init_external(&req->qiov, &req->elem.out_sg[1], @@ -531,7 +536,8 @@ static void virtio_blk_change_cb(void *opaque, int reason) } } -VirtIODevice *virtio_blk_init(DeviceState *dev, BlockConf *conf) +VirtIODevice *virtio_blk_init(DeviceState *dev, BlockConf *conf, + char **serial) { VirtIOBlock *s; int cylinders, heads, secs; @@ -547,6 +553,14 @@ VirtIODevice *virtio_blk_init(DeviceState *dev, BlockConf *conf) return NULL; } + if (!*serial) { + /* try to fall back to value set with legacy -drive serial=... */ + dinfo = drive_get_by_blockdev(conf->bs); + if (*dinfo->serial) { + *serial = strdup(dinfo->serial); + } + } + s = (VirtIOBlock *)virtio_common_init("virtio-blk", VIRTIO_ID_BLOCK, sizeof(struct virtio_blk_config), sizeof(VirtIOBlock)); @@ -556,16 +570,11 @@ VirtIODevice *virtio_blk_init(DeviceState *dev, BlockConf *conf) s->vdev.reset = virtio_blk_reset; s->bs = conf->bs; s->conf = conf; + s->serial = *serial; s->rq = NULL; s->sector_mask = (s->conf->logical_block_size / BDRV_SECTOR_SIZE) - 1; bdrv_guess_geometry(s->bs, &cylinders, &heads, &secs); - /* NB: per existing s/n string convention the string is terminated - * by '\0' only when less than sizeof (s->sn) - */ - dinfo = drive_get_by_blockdev(s->bs); - strncpy(s->sn, dinfo->serial, sizeof (s->sn)); - s->vq = virtio_add_queue(&s->vdev, 128, virtio_blk_handle_output); qemu_add_vm_change_state_handler(virtio_blk_dma_restart_cb, s); diff --git a/hw/virtio-blk.h b/hw/virtio-blk.h index fff46da7db..5645d2bf3c 100644 --- a/hw/virtio-blk.h +++ b/hw/virtio-blk.h @@ -34,6 +34,8 @@ #define VIRTIO_BLK_F_WCACHE 9 /* write cache enabled */ #define VIRTIO_BLK_F_TOPOLOGY 10 /* Topology information is available */ +#define VIRTIO_BLK_ID_BYTES 20 /* ID string length */ + struct virtio_blk_config { uint64_t capacity; diff --git a/hw/virtio-pci.c b/hw/virtio-pci.c index b3e7ba5d12..d685243728 100644 --- a/hw/virtio-pci.c +++ b/hw/virtio-pci.c @@ -700,7 +700,8 @@ static int virtio_blk_init_pci(PCIDevice *pci_dev) proxy->class_code != PCI_CLASS_STORAGE_OTHER) proxy->class_code = PCI_CLASS_STORAGE_SCSI; - vdev = virtio_blk_init(&pci_dev->qdev, &proxy->block); + vdev = virtio_blk_init(&pci_dev->qdev, &proxy->block, + &proxy->block_serial); if (!vdev) { return -1; } @@ -805,6 +806,7 @@ static PCIDeviceInfo virtio_info[] = { .qdev.props = (Property[]) { DEFINE_PROP_HEX32("class", VirtIOPCIProxy, class_code, 0), DEFINE_BLOCK_PROPERTIES(VirtIOPCIProxy, block), + DEFINE_PROP_STRING("serial", VirtIOPCIProxy, block_serial), DEFINE_PROP_BIT("ioeventfd", VirtIOPCIProxy, flags, VIRTIO_PCI_FLAG_USE_IOEVENTFD_BIT, true), DEFINE_PROP_UINT32("vectors", VirtIOPCIProxy, nvectors, 2), diff --git a/hw/virtio-pci.h b/hw/virtio-pci.h index b5189172de..1f0de5684d 100644 --- a/hw/virtio-pci.h +++ b/hw/virtio-pci.h @@ -26,6 +26,7 @@ typedef struct { uint32_t class_code; uint32_t nvectors; BlockConf block; + char *block_serial; NICConf nic; uint32_t host_features; #ifdef CONFIG_LINUX diff --git a/hw/virtio.h b/hw/virtio.h index 69e6bb1a4e..0fd0bb0ac5 100644 --- a/hw/virtio.h +++ b/hw/virtio.h @@ -197,7 +197,8 @@ void virtio_bind_device(VirtIODevice *vdev, const VirtIOBindings *binding, void *opaque); /* Base devices. */ -VirtIODevice *virtio_blk_init(DeviceState *dev, BlockConf *conf); +VirtIODevice *virtio_blk_init(DeviceState *dev, BlockConf *conf, + char **serial); struct virtio_net_conf; VirtIODevice *virtio_net_init(DeviceState *dev, NICConf *conf, struct virtio_net_conf *net); From 2d3999fe13b5e4663fd8ffc3661b1ffd44130e55 Mon Sep 17 00:00:00 2001 From: Luiz Capitulino Date: Fri, 1 Jul 2011 10:46:12 -0300 Subject: [PATCH 043/209] block: drive_init(): Simplify interface type setting Signed-off-by: Luiz Capitulino Reviewed-by: Markus Armbruster Signed-off-by: Kevin Wolf --- blockdev.c | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) diff --git a/blockdev.c b/blockdev.c index 7d579d6124..470be71cbf 100644 --- a/blockdev.c +++ b/blockdev.c @@ -240,14 +240,6 @@ DriveInfo *drive_init(QemuOpts *opts, int default_to_scsi) int ret; translation = BIOS_ATA_TRANSLATION_AUTO; - - if (default_to_scsi) { - type = IF_SCSI; - pstrcpy(devname, sizeof(devname), "scsi"); - } else { - type = IF_IDE; - pstrcpy(devname, sizeof(devname), "ide"); - } media = MEDIA_DISK; /* extract parameters */ @@ -273,7 +265,11 @@ DriveInfo *drive_init(QemuOpts *opts, int default_to_scsi) error_report("unsupported bus type '%s'", buf); return NULL; } + } else { + type = default_to_scsi ? IF_SCSI : IF_IDE; + pstrcpy(devname, sizeof(devname), if_name[type]); } + max_devs = if_max_devs[type]; if (cyls || heads || secs) { From e7ff8f0e0c03853c5018d683b28b338b9738588a Mon Sep 17 00:00:00 2001 From: Luiz Capitulino Date: Fri, 1 Jul 2011 10:46:13 -0300 Subject: [PATCH 044/209] block: drive_init(): Improve CHS setting error message The current message doesn't clearly communicate the error cause. Signed-off-by: Luiz Capitulino Reviewed-by: Markus Armbruster Signed-off-by: Kevin Wolf --- blockdev.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/blockdev.c b/blockdev.c index 470be71cbf..a97a801e95 100644 --- a/blockdev.c +++ b/blockdev.c @@ -310,7 +310,7 @@ DriveInfo *drive_init(QemuOpts *opts, int default_to_scsi) media = MEDIA_DISK; } else if (!strcmp(buf, "cdrom")) { if (cyls || secs || heads) { - error_report("'%s' invalid physical CHS format", buf); + error_report("CHS can't be set with media=%s", buf); return NULL; } media = MEDIA_CDROM; From 40c4ed3f95f0b2ffa0848df0fc311556bb7472a1 Mon Sep 17 00:00:00 2001 From: Kevin Wolf Date: Mon, 4 Jul 2011 14:07:50 +0200 Subject: [PATCH 045/209] ide: Ignore reads during PIO in and writes during PIO out This fixes https://bugs.launchpad.net/qemu/+bug/786209: When the DRQ_STAT bit is set, the IDE core permits both data reads and data writes, regardless of whether the current transfer was initiated as a read or write. This potentially leaks uninitialized host memory into the guest, if, before doing anything else to an IDE device, the guest begins a write transaction (e.g. WIN_WRITE), but then *reads* from the IO port instead of writing to it. Signed-off-by: Kevin Wolf Reviewed-by: Markus Armbruster --- hw/ide/core.c | 44 ++++++++++++++++++++++++++++++++++++-------- 1 file changed, 36 insertions(+), 8 deletions(-) diff --git a/hw/ide/core.c b/hw/ide/core.c index ca17a436c0..a29ae9fb90 100644 --- a/hw/ide/core.c +++ b/hw/ide/core.c @@ -56,6 +56,7 @@ static const int smart_attributes[][12] = { }; static int ide_handle_rw_error(IDEState *s, int error, int op); +static void ide_dummy_transfer_stop(IDEState *s); static void padstr(char *str, const char *src, int len) { @@ -1532,15 +1533,36 @@ void ide_cmd_write(void *opaque, uint32_t addr, uint32_t val) bus->cmd = val; } +/* + * Returns true if the running PIO transfer is a PIO out (i.e. data is + * transferred from the device to the guest), false if it's a PIO in + */ +static bool ide_is_pio_out(IDEState *s) +{ + if (s->end_transfer_func == ide_sector_write || + s->end_transfer_func == ide_atapi_cmd) { + return false; + } else if (s->end_transfer_func == ide_sector_read || + s->end_transfer_func == ide_transfer_stop || + s->end_transfer_func == ide_atapi_cmd_reply_end || + s->end_transfer_func == ide_dummy_transfer_stop) { + return true; + } + + abort(); +} + void ide_data_writew(void *opaque, uint32_t addr, uint32_t val) { IDEBus *bus = opaque; IDEState *s = idebus_active_if(bus); uint8_t *p; - /* PIO data access allowed only when DRQ bit is set */ - if (!(s->status & DRQ_STAT)) + /* PIO data access allowed only when DRQ bit is set. The result of a write + * during PIO out is indeterminate, just ignore it. */ + if (!(s->status & DRQ_STAT) || ide_is_pio_out(s)) { return; + } p = s->data_ptr; *(uint16_t *)p = le16_to_cpu(val); @@ -1557,9 +1579,11 @@ uint32_t ide_data_readw(void *opaque, uint32_t addr) uint8_t *p; int ret; - /* PIO data access allowed only when DRQ bit is set */ - if (!(s->status & DRQ_STAT)) + /* PIO data access allowed only when DRQ bit is set. The result of a read + * during PIO in is indeterminate, return 0 and don't move forward. */ + if (!(s->status & DRQ_STAT) || !ide_is_pio_out(s)) { return 0; + } p = s->data_ptr; ret = cpu_to_le16(*(uint16_t *)p); @@ -1576,9 +1600,11 @@ void ide_data_writel(void *opaque, uint32_t addr, uint32_t val) IDEState *s = idebus_active_if(bus); uint8_t *p; - /* PIO data access allowed only when DRQ bit is set */ - if (!(s->status & DRQ_STAT)) + /* PIO data access allowed only when DRQ bit is set. The result of a write + * during PIO out is indeterminate, just ignore it. */ + if (!(s->status & DRQ_STAT) || ide_is_pio_out(s)) { return; + } p = s->data_ptr; *(uint32_t *)p = le32_to_cpu(val); @@ -1595,9 +1621,11 @@ uint32_t ide_data_readl(void *opaque, uint32_t addr) uint8_t *p; int ret; - /* PIO data access allowed only when DRQ bit is set */ - if (!(s->status & DRQ_STAT)) + /* PIO data access allowed only when DRQ bit is set. The result of a read + * during PIO in is indeterminate, return 0 and don't move forward. */ + if (!(s->status & DRQ_STAT) || !ide_is_pio_out(s)) { return 0; + } p = s->data_ptr; ret = cpu_to_le32(*(uint32_t *)p); From c925400ba83bd57bf560e071f400012248f1644a Mon Sep 17 00:00:00 2001 From: Kevin Wolf Date: Mon, 4 Jul 2011 14:43:58 +0200 Subject: [PATCH 046/209] ide: Initialise buffers with zeros Just in case there's still a way how a guest can read out buffers when it's not supposed to, let's zero the buffers during initialisation so that we don't leak information to the guest. Signed-off-by: Kevin Wolf Reviewed-by: Markus Armbruster --- hw/ide/core.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/hw/ide/core.c b/hw/ide/core.c index a29ae9fb90..d145b19b0c 100644 --- a/hw/ide/core.c +++ b/hw/ide/core.c @@ -1789,9 +1789,13 @@ static void ide_init1(IDEBus *bus, int unit) s->unit = unit; s->drive_serial = drive_serial++; /* we need at least 2k alignment for accessing CDROMs using O_DIRECT */ - s->io_buffer = qemu_memalign(2048, IDE_DMA_BUF_SECTORS*512 + 4); s->io_buffer_total_len = IDE_DMA_BUF_SECTORS*512 + 4; + s->io_buffer = qemu_memalign(2048, s->io_buffer_total_len); + memset(s->io_buffer, 0, s->io_buffer_total_len); + s->smart_selftest_data = qemu_blockalign(s->bs, 512); + memset(s->smart_selftest_data, 0, 512); + s->sector_write_timer = qemu_new_timer_ns(vm_clock, ide_sector_write_timer_cb, s); } From 000eb4fa525d0b9b2d2b2a2ad4925863af60e5f7 Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Tue, 14 Jun 2011 12:24:04 +0100 Subject: [PATCH 047/209] hw/usb-musb.c: Don't misuse usb_packet_complete() In musb_packet() handle final processing of non-asynchronous USB packets by directly calling musb_schedule_cb() rather than going through usb_packet_complete(). The latter will trigger an assertion because the packet doesn't belong to a device. Signed-off-by: Peter Maydell Signed-off-by: Gerd Hoffmann --- hw/usb-musb.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hw/usb-musb.c b/hw/usb-musb.c index 21f35afa92..d15971fcfc 100644 --- a/hw/usb-musb.c +++ b/hw/usb-musb.c @@ -616,7 +616,7 @@ static void musb_packet(MUSBState *s, MUSBEndPoint *ep, } ep->status[dir] = ret; - usb_packet_complete(s->port.dev, &ep->packey[dir].p); + musb_schedule_cb(s->port.dev, &ep->packey[dir].p); } static void musb_tx_packet_complete(USBPacket *packey, void *opaque) From 090ac6425accf5d87be5ff8087080c876a2904e5 Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Thu, 30 Jun 2011 11:57:57 +0200 Subject: [PATCH 048/209] usb: Add a usb_fill_port helper function Signed-off-by: Hans de Goede Signed-off-by: Gerd Hoffmann --- hw/usb-bus.c | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/hw/usb-bus.c b/hw/usb-bus.c index 2abce12de5..776974e6cf 100644 --- a/hw/usb-bus.c +++ b/hw/usb-bus.c @@ -140,8 +140,8 @@ USBDevice *usb_create_simple(USBBus *bus, const char *name) return dev; } -void usb_register_port(USBBus *bus, USBPort *port, void *opaque, int index, - USBPortOps *ops, int speedmask) +static void usb_fill_port(USBPort *port, void *opaque, int index, + USBPortOps *ops, int speedmask) { port->opaque = opaque; port->index = index; @@ -149,6 +149,12 @@ void usb_register_port(USBBus *bus, USBPort *port, void *opaque, int index, port->index = index; port->ops = ops; port->speedmask = speedmask; +} + +void usb_register_port(USBBus *bus, USBPort *port, void *opaque, int index, + USBPortOps *ops, int speedmask) +{ + usb_fill_port(port, opaque, index, ops, speedmask); QTAILQ_INSERT_TAIL(&bus->free, port, next); bus->nfree++; } From 3631e6c8c263528ca159707b9255117a2c3bb175 Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Thu, 30 Jun 2011 12:05:19 +0200 Subject: [PATCH 049/209] usb: Move (initial) call of usb_port_location to usb_fill_port Cleanup / preparation patch for companion controller support. Note that as a "side-effect" this patch also fixes the milkymist-softusb controller not having a port_location set for its ports. Signed-off-by: Hans de Goede Signed-off-by: Gerd Hoffmann --- hw/usb-bus.c | 1 + hw/usb-ehci.c | 1 - hw/usb-musb.c | 1 - hw/usb-ohci.c | 1 - hw/usb-uhci.c | 1 - 5 files changed, 1 insertion(+), 4 deletions(-) diff --git a/hw/usb-bus.c b/hw/usb-bus.c index 776974e6cf..e37e8a28a6 100644 --- a/hw/usb-bus.c +++ b/hw/usb-bus.c @@ -149,6 +149,7 @@ static void usb_fill_port(USBPort *port, void *opaque, int index, port->index = index; port->ops = ops; port->speedmask = speedmask; + usb_port_location(port, NULL, index + 1); } void usb_register_port(USBBus *bus, USBPort *port, void *opaque, int index, diff --git a/hw/usb-ehci.c b/hw/usb-ehci.c index 91fb7dea93..88cb2c249b 100644 --- a/hw/usb-ehci.c +++ b/hw/usb-ehci.c @@ -2206,7 +2206,6 @@ static int usb_ehci_initfn(PCIDevice *dev) for(i = 0; i < NB_PORTS; i++) { usb_register_port(&s->bus, &s->ports[i], s, i, &ehci_port_ops, USB_SPEED_MASK_HIGH); - usb_port_location(&s->ports[i], NULL, i+1); s->ports[i].dev = 0; } diff --git a/hw/usb-musb.c b/hw/usb-musb.c index d15971fcfc..84e601762c 100644 --- a/hw/usb-musb.c +++ b/hw/usb-musb.c @@ -369,7 +369,6 @@ struct MUSBState *musb_init(qemu_irq *irqs) usb_bus_new(&s->bus, &musb_bus_ops, NULL /* FIXME */); usb_register_port(&s->bus, &s->port, s, 0, &musb_port_ops, USB_SPEED_MASK_LOW | USB_SPEED_MASK_FULL); - usb_port_location(&s->port, NULL, 1); return s; } diff --git a/hw/usb-ohci.c b/hw/usb-ohci.c index 1c29b9fa6c..95e4623d52 100644 --- a/hw/usb-ohci.c +++ b/hw/usb-ohci.c @@ -1742,7 +1742,6 @@ static void usb_ohci_init(OHCIState *ohci, DeviceState *dev, for (i = 0; i < num_ports; i++) { usb_register_port(&ohci->bus, &ohci->rhport[i].port, ohci, i, &ohci_port_ops, USB_SPEED_MASK_LOW | USB_SPEED_MASK_FULL); - usb_port_location(&ohci->rhport[i].port, NULL, i+1); } ohci->async_td = 0; diff --git a/hw/usb-uhci.c b/hw/usb-uhci.c index 405fa7b65e..fd25d2ae0c 100644 --- a/hw/usb-uhci.c +++ b/hw/usb-uhci.c @@ -1129,7 +1129,6 @@ static int usb_uhci_common_initfn(PCIDevice *dev) for(i = 0; i < NB_PORTS; i++) { usb_register_port(&s->bus, &s->ports[i].port, s, i, &uhci_port_ops, USB_SPEED_MASK_LOW | USB_SPEED_MASK_FULL); - usb_port_location(&s->ports[i].port, NULL, i+1); } s->frame_timer = qemu_new_timer_ns(vm_clock, uhci_frame_timer, s); s->num_ports_vmstate = NB_PORTS; From ae60fea97c78e7f855794f2770517244d93def73 Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Fri, 24 Jun 2011 11:29:56 +0200 Subject: [PATCH 050/209] usb: Add a register_companion USB bus op. This is a preparation patch for adding support for USB companion controllers. Signed-off-by: Hans de Goede Signed-off-by: Gerd Hoffmann --- hw/usb-bus.c | 31 +++++++++++++++++++++++++++++++ hw/usb.h | 5 +++++ 2 files changed, 36 insertions(+) diff --git a/hw/usb-bus.c b/hw/usb-bus.c index e37e8a28a6..b511bac411 100644 --- a/hw/usb-bus.c +++ b/hw/usb-bus.c @@ -160,6 +160,37 @@ void usb_register_port(USBBus *bus, USBPort *port, void *opaque, int index, bus->nfree++; } +int usb_register_companion(const char *masterbus, USBPort *ports[], + uint32_t portcount, uint32_t firstport, + void *opaque, USBPortOps *ops, int speedmask) +{ + USBBus *bus; + int i; + + QTAILQ_FOREACH(bus, &busses, next) { + if (strcmp(bus->qbus.name, masterbus) == 0) { + break; + } + } + + if (!bus || !bus->ops->register_companion) { + qerror_report(QERR_INVALID_PARAMETER_VALUE, "masterbus", + "an USB masterbus"); + if (bus) { + error_printf_unless_qmp( + "USB bus '%s' does not allow companion controllers\n", + masterbus); + } + return -1; + } + + for (i = 0; i < portcount; i++) { + usb_fill_port(ports[i], opaque, i, ops, speedmask); + } + + return bus->ops->register_companion(bus, ports, portcount, firstport); +} + void usb_port_location(USBPort *downstream, USBPort *upstream, int portnr) { if (upstream) { diff --git a/hw/usb.h b/hw/usb.h index 076e2ffce4..a5f2efa613 100644 --- a/hw/usb.h +++ b/hw/usb.h @@ -344,6 +344,8 @@ struct USBBus { }; struct USBBusOps { + int (*register_companion)(USBBus *bus, USBPort *ports[], + uint32_t portcount, uint32_t firstport); void (*device_destroy)(USBBus *bus, USBDevice *dev); }; @@ -356,6 +358,9 @@ USBDevice *usb_create_simple(USBBus *bus, const char *name); USBDevice *usbdevice_create(const char *cmdline); void usb_register_port(USBBus *bus, USBPort *port, void *opaque, int index, USBPortOps *ops, int speedmask); +int usb_register_companion(const char *masterbus, USBPort *ports[], + uint32_t portcount, uint32_t firstport, + void *opaque, USBPortOps *ops, int speedmask); void usb_port_location(USBPort *downstream, USBPort *upstream, int portnr); void usb_unregister_port(USBBus *bus, USBPort *port); int usb_device_attach(USBDevice *dev); From d47e59b8b8adc96a2052f7e004cb12b6ff62edd9 Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Tue, 21 Jun 2011 11:52:28 +0200 Subject: [PATCH 051/209] usb: Make port wakeup and complete ops take a USBPort instead of a Device This makes them consistent with the attach and detach ops, and in general it makes sense to make portops take a port as argument. This also makes adding support for a companion controller easier / cleaner. [ kraxel: fix usb-musb.c build ] Signed-off-by: Hans de Goede Signed-off-by: Gerd Hoffmann --- hw/usb-ehci.c | 2 +- hw/usb-hub.c | 10 +++++----- hw/usb-musb.c | 6 +++--- hw/usb-ohci.c | 12 +++++------- hw/usb-uhci.c | 11 +++++------ hw/usb.c | 4 ++-- hw/usb.h | 9 +++++++-- 7 files changed, 28 insertions(+), 26 deletions(-) diff --git a/hw/usb-ehci.c b/hw/usb-ehci.c index 88cb2c249b..428c90bcf3 100644 --- a/hw/usb-ehci.c +++ b/hw/usb-ehci.c @@ -1111,7 +1111,7 @@ static int ehci_buffer_rw(EHCIQueue *q, int bytes, int rw) return 0; } -static void ehci_async_complete_packet(USBDevice *dev, USBPacket *packet) +static void ehci_async_complete_packet(USBPort *port, USBPacket *packet) { EHCIQueue *q = container_of(packet, EHCIQueue, packet); diff --git a/hw/usb-hub.c b/hw/usb-hub.c index 6e2a35839d..d324bbad8e 100644 --- a/hw/usb-hub.c +++ b/hw/usb-hub.c @@ -246,10 +246,10 @@ static void usb_hub_detach(USBPort *port1) } } -static void usb_hub_wakeup(USBDevice *dev) +static void usb_hub_wakeup(USBPort *port1) { - USBHubState *s = dev->port->opaque; - USBHubPort *port = &s->ports[dev->port->index]; + USBHubState *s = port1->opaque; + USBHubPort *port = &s->ports[port1->index]; if (port->wPortStatus & PORT_STAT_SUSPEND) { port->wPortChange |= PORT_STAT_C_SUSPEND; @@ -257,9 +257,9 @@ static void usb_hub_wakeup(USBDevice *dev) } } -static void usb_hub_complete(USBDevice *dev, USBPacket *packet) +static void usb_hub_complete(USBPort *port, USBPacket *packet) { - USBHubState *s = dev->port->opaque; + USBHubState *s = port->opaque; /* * Just pass it along upstream for now. diff --git a/hw/usb-musb.c b/hw/usb-musb.c index 84e601762c..580bdc815a 100644 --- a/hw/usb-musb.c +++ b/hw/usb-musb.c @@ -261,7 +261,7 @@ static void musb_attach(USBPort *port); static void musb_detach(USBPort *port); -static void musb_schedule_cb(USBDevice *dev, USBPacket *p); +static void musb_schedule_cb(USBPort *port, USBPacket *p); static void musb_device_destroy(USBBus *bus, USBDevice *dev); static USBPortOps musb_port_ops = { @@ -517,7 +517,7 @@ static void musb_cb_tick1(void *opaque) #define musb_cb_tick (dir ? musb_cb_tick1 : musb_cb_tick0) -static void musb_schedule_cb(USBDevice *dev, USBPacket *packey) +static void musb_schedule_cb(USBPort *port, USBPacket *packey) { MUSBPacket *p = container_of(packey, MUSBPacket, p); MUSBEndPoint *ep = p->ep; @@ -615,7 +615,7 @@ static void musb_packet(MUSBState *s, MUSBEndPoint *ep, } ep->status[dir] = ret; - musb_schedule_cb(s->port.dev, &ep->packey[dir].p); + musb_schedule_cb(&s->port, &ep->packey[dir].p); } static void musb_tx_packet_complete(USBPacket *packey, void *opaque) diff --git a/hw/usb-ohci.c b/hw/usb-ohci.c index 95e4623d52..bd92c31571 100644 --- a/hw/usb-ohci.c +++ b/hw/usb-ohci.c @@ -367,15 +367,13 @@ static void ohci_detach(USBPort *port1) ohci_set_interrupt(s, OHCI_INTR_RHSC); } -static void ohci_wakeup(USBDevice *dev) +static void ohci_wakeup(USBPort *port1) { - USBBus *bus = usb_bus_from_device(dev); - OHCIState *s = container_of(bus, OHCIState, bus); - int portnum = dev->port->index; - OHCIPort *port = &s->rhport[portnum]; + OHCIState *s = port1->opaque; + OHCIPort *port = &s->rhport[port1->index]; uint32_t intr = 0; if (port->ctrl & OHCI_PORT_PSS) { - DPRINTF("usb-ohci: port %d: wakeup\n", portnum); + DPRINTF("usb-ohci: port %d: wakeup\n", port1->index); port->ctrl |= OHCI_PORT_PSSC; port->ctrl &= ~OHCI_PORT_PSS; intr = OHCI_INTR_RHSC; @@ -602,7 +600,7 @@ static void ohci_copy_iso_td(OHCIState *ohci, static void ohci_process_lists(OHCIState *ohci, int completion); -static void ohci_async_complete_packet(USBDevice *dev, USBPacket *packet) +static void ohci_async_complete_packet(USBPort *port, USBPacket *packet) { OHCIState *ohci = container_of(packet, OHCIState, usb_packet); #ifdef DEBUG_PACKET diff --git a/hw/usb-uhci.c b/hw/usb-uhci.c index fd25d2ae0c..ab635f6450 100644 --- a/hw/usb-uhci.c +++ b/hw/usb-uhci.c @@ -620,11 +620,10 @@ static void uhci_detach(USBPort *port1) uhci_resume(s); } -static void uhci_wakeup(USBDevice *dev) +static void uhci_wakeup(USBPort *port1) { - USBBus *bus = usb_bus_from_device(dev); - UHCIState *s = container_of(bus, UHCIState, bus); - UHCIPort *port = s->ports + dev->port->index; + UHCIState *s = port1->opaque; + UHCIPort *port = &s->ports[port1->index]; if (port->ctrl & UHCI_PORT_SUSPEND && !(port->ctrl & UHCI_PORT_RD)) { port->ctrl |= UHCI_PORT_RD; @@ -657,7 +656,7 @@ static int uhci_broadcast_packet(UHCIState *s, USBPacket *p) return ret; } -static void uhci_async_complete(USBDevice *dev, USBPacket *packet); +static void uhci_async_complete(USBPort *port, USBPacket *packet); static void uhci_process_frame(UHCIState *s); /* return -1 if fatal error (frame must be stopped) @@ -849,7 +848,7 @@ done: return len; } -static void uhci_async_complete(USBDevice *dev, USBPacket *packet) +static void uhci_async_complete(USBPort *port, USBPacket *packet) { UHCIAsync *async = container_of(packet, UHCIAsync, packet); UHCIState *s = async->uhci; diff --git a/hw/usb.c b/hw/usb.c index 4a39cbcc7d..735ffd1966 100644 --- a/hw/usb.c +++ b/hw/usb.c @@ -52,7 +52,7 @@ void usb_attach(USBPort *port, USBDevice *dev) void usb_wakeup(USBDevice *dev) { if (dev->remote_wakeup && dev->port && dev->port->ops->wakeup) { - dev->port->ops->wakeup(dev); + dev->port->ops->wakeup(dev->port); } } @@ -335,7 +335,7 @@ void usb_packet_complete(USBDevice *dev, USBPacket *p) { /* Note: p->owner != dev is possible in case dev is a hub */ assert(p->owner != NULL); - dev->port->ops->complete(dev, p); + dev->port->ops->complete(dev->port, p); p->owner = NULL; } diff --git a/hw/usb.h b/hw/usb.h index a5f2efa613..65f45a0ce4 100644 --- a/hw/usb.h +++ b/hw/usb.h @@ -252,8 +252,13 @@ struct USBDeviceInfo { typedef struct USBPortOps { void (*attach)(USBPort *port); void (*detach)(USBPort *port); - void (*wakeup)(USBDevice *dev); - void (*complete)(USBDevice *dev, USBPacket *p); + void (*wakeup)(USBPort *port); + /* + * Note that port->dev will be different then the device from which + * the packet originated when a hub is involved, if you want the orginating + * device use p->owner + */ + void (*complete)(USBPort *port, USBPacket *p); } USBPortOps; /* USB port on which a device can be connected */ From 4706ab6cc0af86d3f38806664420cc3eb8999bd9 Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Fri, 24 Jun 2011 12:31:11 +0200 Subject: [PATCH 052/209] usb: Replace device_destroy bus op with a child_detach port op Note this fixes 2 things in one go, first of all the device_destroy bus op should be a device_detach bus op, as pending async packets from the device should be cancelled on detach not on destroy. Secondly having this as a bus op won't work with companion controllers, since then there will be 1 bus driven by the ehci controller and thus 1 set of bus ops, but the device being detached may be downstream of a handed over port. Making the detach of a downstream device a port op allows the ehci controller to forward this to the companion controller port for handed over ports. Signed-off-by: Hans de Goede Signed-off-by: Gerd Hoffmann --- hw/milkymist-softusb.c | 9 +++++++-- hw/usb-bus.c | 2 -- hw/usb-ehci.c | 18 ++++++++++-------- hw/usb-hub.c | 12 ++++++++++++ hw/usb-musb.c | 17 +++++++++++++---- hw/usb-ohci.c | 16 ++++++++++++---- hw/usb-uhci.c | 18 ++++++++++-------- hw/usb.h | 6 +++++- 8 files changed, 69 insertions(+), 29 deletions(-) diff --git a/hw/milkymist-softusb.c b/hw/milkymist-softusb.c index 5ab35c3827..ce2bfc60f2 100644 --- a/hw/milkymist-softusb.c +++ b/hw/milkymist-softusb.c @@ -247,16 +247,21 @@ static void softusb_attach(USBPort *port) { } -static void softusb_device_destroy(USBBus *bus, USBDevice *dev) +static void softusb_detach(USBPort *port) +{ +} + +static void softusb_child_detach(USBPort *port, USBDevice *child) { } static USBPortOps softusb_ops = { .attach = softusb_attach, + .detach = softusb_detach, + .child_detach = softusb_child_detach, }; static USBBusOps softusb_bus_ops = { - .device_destroy = softusb_device_destroy, }; static void milkymist_softusb_reset(DeviceState *d) diff --git a/hw/usb-bus.c b/hw/usb-bus.c index b511bac411..c8347e9e3b 100644 --- a/hw/usb-bus.c +++ b/hw/usb-bus.c @@ -82,12 +82,10 @@ static int usb_qdev_init(DeviceState *qdev, DeviceInfo *base) static int usb_qdev_exit(DeviceState *qdev) { USBDevice *dev = DO_UPCAST(USBDevice, qdev, qdev); - USBBus *bus = usb_bus_from_device(dev); if (dev->attached) { usb_device_detach(dev); } - bus->ops->device_destroy(bus, dev); if (dev->info->handle_destroy) { dev->info->handle_destroy(dev); } diff --git a/hw/usb-ehci.c b/hw/usb-ehci.c index 428c90bcf3..96451f334d 100644 --- a/hw/usb-ehci.c +++ b/hw/usb-ehci.c @@ -751,6 +751,8 @@ static void ehci_detach(USBPort *port) trace_usb_ehci_port_detach(port->index); + ehci_queues_rip_device(s, port->dev); + *portsc &= ~PORTSC_CONNECT; *portsc |= PORTSC_CSC; @@ -764,6 +766,13 @@ static void ehci_detach(USBPort *port) } } +static void ehci_child_detach(USBPort *port, USBDevice *child) +{ + EHCIState *s = port->opaque; + + ehci_queues_rip_device(s, child); +} + /* 4.1 host controller initialization */ static void ehci_reset(void *opaque) { @@ -2117,23 +2126,16 @@ static void ehci_map(PCIDevice *pci_dev, int region_num, cpu_register_physical_memory(addr, size, s->mem); } -static void ehci_device_destroy(USBBus *bus, USBDevice *dev) -{ - EHCIState *s = container_of(bus, EHCIState, bus); - - ehci_queues_rip_device(s, dev); -} - static int usb_ehci_initfn(PCIDevice *dev); static USBPortOps ehci_port_ops = { .attach = ehci_attach, .detach = ehci_detach, + .child_detach = ehci_child_detach, .complete = ehci_async_complete_packet, }; static USBBusOps ehci_bus_ops = { - .device_destroy = ehci_device_destroy, }; static PCIDeviceInfo ehci_info = { diff --git a/hw/usb-hub.c b/hw/usb-hub.c index d324bbad8e..b7557cea56 100644 --- a/hw/usb-hub.c +++ b/hw/usb-hub.c @@ -238,6 +238,9 @@ static void usb_hub_detach(USBPort *port1) USBHubState *s = port1->opaque; USBHubPort *port = &s->ports[port1->index]; + /* Let upstream know the device on this port is gone */ + s->dev.port->ops->child_detach(s->dev.port, port1->dev); + port->wPortStatus &= ~PORT_STAT_CONNECTION; port->wPortChange |= PORT_STAT_C_CONNECTION; if (port->wPortStatus & PORT_STAT_ENABLE) { @@ -246,6 +249,14 @@ static void usb_hub_detach(USBPort *port1) } } +static void usb_hub_child_detach(USBPort *port1, USBDevice *child) +{ + USBHubState *s = port1->opaque; + + /* Pass along upstream */ + s->dev.port->ops->child_detach(s->dev.port, child); +} + static void usb_hub_wakeup(USBPort *port1) { USBHubState *s = port1->opaque; @@ -537,6 +548,7 @@ static void usb_hub_handle_destroy(USBDevice *dev) static USBPortOps usb_hub_port_ops = { .attach = usb_hub_attach, .detach = usb_hub_detach, + .child_detach = usb_hub_child_detach, .wakeup = usb_hub_wakeup, .complete = usb_hub_complete, }; diff --git a/hw/usb-musb.c b/hw/usb-musb.c index 580bdc815a..035dda8372 100644 --- a/hw/usb-musb.c +++ b/hw/usb-musb.c @@ -261,17 +261,18 @@ static void musb_attach(USBPort *port); static void musb_detach(USBPort *port); +static void musb_child_detach(USBPort *port, USBDevice *child); static void musb_schedule_cb(USBPort *port, USBPacket *p); -static void musb_device_destroy(USBBus *bus, USBDevice *dev); +static void musb_async_cancel_device(MUSBState *s, USBDevice *dev); static USBPortOps musb_port_ops = { .attach = musb_attach, .detach = musb_detach, + .child_detach = musb_child_detach, .complete = musb_schedule_cb, }; static USBBusOps musb_bus_ops = { - .device_destroy = musb_device_destroy, }; typedef struct MUSBPacket MUSBPacket; @@ -497,10 +498,19 @@ static void musb_detach(USBPort *port) { MUSBState *s = (MUSBState *) port->opaque; + musb_async_cancel_device(s, port->dev); + musb_intr_set(s, musb_irq_disconnect, 1); musb_session_update(s, 1, s->session); } +static void musb_child_detach(USBPort *port, USBDevice *child) +{ + MUSBState *s = (MUSBState *) port->opaque; + + musb_async_cancel_device(s, child); +} + static void musb_cb_tick0(void *opaque) { MUSBEndPoint *ep = (MUSBEndPoint *) opaque; @@ -782,9 +792,8 @@ static void musb_rx_packet_complete(USBPacket *packey, void *opaque) musb_rx_intr_set(s, epnum, 1); } -static void musb_device_destroy(USBBus *bus, USBDevice *dev) +static void musb_async_cancel_device(MUSBState *s, USBDevice *dev) { - MUSBState *s = container_of(bus, MUSBState, bus); int ep, dir; for (ep = 0; ep < 16; ep++) { diff --git a/hw/usb-ohci.c b/hw/usb-ohci.c index bd92c31571..46f0bcbd53 100644 --- a/hw/usb-ohci.c +++ b/hw/usb-ohci.c @@ -124,6 +124,7 @@ struct ohci_hcca { }; static void ohci_bus_stop(OHCIState *ohci); +static void ohci_async_cancel_device(OHCIState *ohci, USBDevice *dev); /* Bitfields for the first word of an Endpoint Desciptor. */ #define OHCI_ED_FA_SHIFT 0 @@ -351,6 +352,8 @@ static void ohci_detach(USBPort *port1) OHCIPort *port = &s->rhport[port1->index]; uint32_t old_state = port->ctrl; + ohci_async_cancel_device(s, port1->dev); + /* set connect status */ if (port->ctrl & OHCI_PORT_CCS) { port->ctrl &= ~OHCI_PORT_CCS; @@ -392,6 +395,13 @@ static void ohci_wakeup(USBPort *port1) ohci_set_interrupt(s, intr); } +static void ohci_child_detach(USBPort *port1, USBDevice *child) +{ + OHCIState *s = port1->opaque; + + ohci_async_cancel_device(s, child); +} + /* Reset the controller */ static void ohci_reset(void *opaque) { @@ -1673,10 +1683,8 @@ static void ohci_mem_write(void *ptr, target_phys_addr_t addr, uint32_t val) } } -static void ohci_device_destroy(USBBus *bus, USBDevice *dev) +static void ohci_async_cancel_device(OHCIState *ohci, USBDevice *dev) { - OHCIState *ohci = container_of(bus, OHCIState, bus); - if (ohci->async_td && ohci->usb_packet.owner == dev) { usb_cancel_packet(&ohci->usb_packet); ohci->async_td = 0; @@ -1700,12 +1708,12 @@ static CPUWriteMemoryFunc * const ohci_writefn[3]={ static USBPortOps ohci_port_ops = { .attach = ohci_attach, .detach = ohci_detach, + .child_detach = ohci_child_detach, .wakeup = ohci_wakeup, .complete = ohci_async_complete_packet, }; static USBBusOps ohci_bus_ops = { - .device_destroy = ohci_device_destroy, }; static void usb_ohci_init(OHCIState *ohci, DeviceState *dev, diff --git a/hw/usb-uhci.c b/hw/usb-uhci.c index ab635f6450..a46d61a2e3 100644 --- a/hw/usb-uhci.c +++ b/hw/usb-uhci.c @@ -606,6 +606,8 @@ static void uhci_detach(USBPort *port1) UHCIState *s = port1->opaque; UHCIPort *port = &s->ports[port1->index]; + uhci_async_cancel_device(s, port1->dev); + /* set connect status */ if (port->ctrl & UHCI_PORT_CCS) { port->ctrl &= ~UHCI_PORT_CCS; @@ -620,6 +622,13 @@ static void uhci_detach(USBPort *port1) uhci_resume(s); } +static void uhci_child_detach(USBPort *port1, USBDevice *child) +{ + UHCIState *s = port1->opaque; + + uhci_async_cancel_device(s, child); +} + static void uhci_wakeup(USBPort *port1) { UHCIState *s = port1->opaque; @@ -1095,22 +1104,15 @@ static void uhci_map(PCIDevice *pci_dev, int region_num, register_ioport_read(addr, 32, 1, uhci_ioport_readb, s); } -static void uhci_device_destroy(USBBus *bus, USBDevice *dev) -{ - UHCIState *s = container_of(bus, UHCIState, bus); - - uhci_async_cancel_device(s, dev); -} - static USBPortOps uhci_port_ops = { .attach = uhci_attach, .detach = uhci_detach, + .child_detach = uhci_child_detach, .wakeup = uhci_wakeup, .complete = uhci_async_complete, }; static USBBusOps uhci_bus_ops = { - .device_destroy = uhci_device_destroy, }; static int usb_uhci_common_initfn(PCIDevice *dev) diff --git a/hw/usb.h b/hw/usb.h index 65f45a0ce4..ded2de29b9 100644 --- a/hw/usb.h +++ b/hw/usb.h @@ -252,6 +252,11 @@ struct USBDeviceInfo { typedef struct USBPortOps { void (*attach)(USBPort *port); void (*detach)(USBPort *port); + /* + * This gets called when a device downstream from the device attached to + * the port (iow attached through a hub) gets detached. + */ + void (*child_detach)(USBPort *port, USBDevice *child); void (*wakeup)(USBPort *port); /* * Note that port->dev will be different then the device from which @@ -351,7 +356,6 @@ struct USBBus { struct USBBusOps { int (*register_companion)(USBBus *bus, USBPort *ports[], uint32_t portcount, uint32_t firstport); - void (*device_destroy)(USBBus *bus, USBDevice *dev); }; void usb_bus_new(USBBus *bus, USBBusOps *ops, DeviceState *host); From 053f9fcfb1d84ab2f1d8fbd608d5e31aae58cbb7 Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Fri, 17 Jun 2011 15:26:29 +0200 Subject: [PATCH 053/209] usb-ehci: drop unused num-ports state member Signed-off-by: Hans de Goede Signed-off-by: Gerd Hoffmann --- hw/usb-ehci.c | 1 - 1 file changed, 1 deletion(-) diff --git a/hw/usb-ehci.c b/hw/usb-ehci.c index 96451f334d..87e1de30fc 100644 --- a/hw/usb-ehci.c +++ b/hw/usb-ehci.c @@ -373,7 +373,6 @@ struct EHCIState { qemu_irq irq; target_phys_addr_t mem_base; int mem; - int num_ports; /* properties */ uint32_t freq; From c44fd61c0f38aa7927fd41bd7e59cb3e60fd4d75 Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Tue, 21 Jun 2011 12:12:35 +0200 Subject: [PATCH 054/209] usb-ehci: Connect Status bit is read only, don't allow changing it by the guest Signed-off-by: Hans de Goede Signed-off-by: Gerd Hoffmann --- hw/usb-ehci.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/hw/usb-ehci.c b/hw/usb-ehci.c index 87e1de30fc..ce1a432dd4 100644 --- a/hw/usb-ehci.c +++ b/hw/usb-ehci.c @@ -103,10 +103,10 @@ #define PORTSC_BEGIN PORTSC #define PORTSC_END (PORTSC + 4 * NB_PORTS) /* - * Bits that are reserverd or are read-only are masked out of values + * Bits that are reserved or are read-only are masked out of values * written to us by software */ -#define PORTSC_RO_MASK 0x007021c5 +#define PORTSC_RO_MASK 0x007021c4 #define PORTSC_RWC_MASK 0x0000002a #define PORTSC_WKOC_E (1 << 22) // Wake on Over Current Enable #define PORTSC_WKDS_E (1 << 21) // Wake on Disconnect Enable From fbf9db645765a22b796e128967bebb64c073938a Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Tue, 21 Jun 2011 12:23:40 +0200 Subject: [PATCH 055/209] usb-ehci: cleanup port reset handling Doing a usb_attach when dev is NULL will just result in the port detach op getting called even though nothing was connected in the first place. Signed-off-by: Hans de Goede Signed-off-by: Gerd Hoffmann --- hw/usb-ehci.c | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/hw/usb-ehci.c b/hw/usb-ehci.c index ce1a432dd4..d85e0a9851 100644 --- a/hw/usb-ehci.c +++ b/hw/usb-ehci.c @@ -863,14 +863,9 @@ static void handle_port_status_write(EHCIState *s, int port, uint32_t val) if (!(val & PORTSC_PRESET) &&(*portsc & PORTSC_PRESET)) { trace_usb_ehci_port_reset(port, 0); - usb_attach(&s->ports[port], dev); - - // TODO how to handle reset of ports with no device if (dev) { + usb_attach(&s->ports[port], dev); usb_send_msg(dev, USB_MSG_RESET); - } - - if (s->ports[port].dev) { *portsc &= ~PORTSC_CSC; } From 45b9fd348061ab793cf4521bb3621f07a5bd7bf6 Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Fri, 24 Jun 2011 14:26:18 +0200 Subject: [PATCH 056/209] usb: assert on calling usb_attach(port, NULL) on a port without a dev with the "usb-ehci: cleanup port reset handling" patch in place no callers are calling usb_attach(port, NULL) for a port where port->dev is NULL. Doing that makes no sense as that causes the port detach op to get called for a port with nothing attached. Add an assert that port->dev != NULL when dev == NULL, and remove the check for not having a port->dev in the dev == NULL case. Signed-off-by: Hans de Goede Signed-off-by: Gerd Hoffmann --- hw/usb.c | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/hw/usb.c b/hw/usb.c index 735ffd1966..27a983ca5c 100644 --- a/hw/usb.c +++ b/hw/usb.c @@ -40,12 +40,11 @@ void usb_attach(USBPort *port, USBDevice *dev) } else { /* detach */ dev = port->dev; + assert(dev); port->ops->detach(port); - if (dev) { - usb_send_msg(dev, USB_MSG_DETACH); - dev->port = NULL; - port->dev = NULL; - } + usb_send_msg(dev, USB_MSG_DETACH); + dev->port = NULL; + port->dev = NULL; } } From fbd97532d22b6989fc71d64471c905d3c8174f5d Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Fri, 24 Jun 2011 14:36:13 +0200 Subject: [PATCH 057/209] usb-ehci: Fix handling of PED and PEDC port status bits The PED bit should only be set for highspeed devices and the PEDC bit should not be set on "normal" PED bit changes, only on io errors. Signed-off-by: Hans de Goede Signed-off-by: Gerd Hoffmann --- hw/usb-ehci.c | 24 +++++++++++------------- 1 file changed, 11 insertions(+), 13 deletions(-) diff --git a/hw/usb-ehci.c b/hw/usb-ehci.c index d85e0a9851..973c34292e 100644 --- a/hw/usb-ehci.c +++ b/hw/usb-ehci.c @@ -106,7 +106,7 @@ * Bits that are reserved or are read-only are masked out of values * written to us by software */ -#define PORTSC_RO_MASK 0x007021c4 +#define PORTSC_RO_MASK 0x007021c0 #define PORTSC_RWC_MASK 0x0000002a #define PORTSC_WKOC_E (1 << 22) // Wake on Over Current Enable #define PORTSC_WKDS_E (1 << 21) // Wake on Disconnect Enable @@ -752,7 +752,7 @@ static void ehci_detach(USBPort *port) ehci_queues_rip_device(s, port->dev); - *portsc &= ~PORTSC_CONNECT; + *portsc &= ~(PORTSC_CONNECT|PORTSC_PED); *portsc |= PORTSC_CSC; /* @@ -847,16 +847,14 @@ static void ehci_mem_writew(void *ptr, target_phys_addr_t addr, uint32_t val) static void handle_port_status_write(EHCIState *s, int port, uint32_t val) { uint32_t *portsc = &s->portsc[port]; - int rwc; USBDevice *dev = s->ports[port].dev; - rwc = val & PORTSC_RWC_MASK; + /* Clear rwc bits */ + *portsc &= ~(val & PORTSC_RWC_MASK); + /* The guest may clear, but not set the PED bit */ + *portsc &= val | ~PORTSC_PED; val &= PORTSC_RO_MASK; - // handle_read_write_clear(&val, portsc, PORTSC_PEDC | PORTSC_CSC); - - *portsc &= ~rwc; - if ((val & PORTSC_PRESET) && !(*portsc & PORTSC_PRESET)) { trace_usb_ehci_port_reset(port, 1); } @@ -869,13 +867,13 @@ static void handle_port_status_write(EHCIState *s, int port, uint32_t val) *portsc &= ~PORTSC_CSC; } - /* Table 2.16 Set the enable bit(and enable bit change) to indicate + /* + * Table 2.16 Set the enable bit(and enable bit change) to indicate * to SW that this port has a high speed device attached - * - * TODO - when to disable? */ - val |= PORTSC_PED; - val |= PORTSC_PEDC; + if (dev && (dev->speedmask & USB_SPEED_MASK_HIGH)) { + val |= PORTSC_PED; + } } *portsc &= ~PORTSC_RO_MASK; From a0a3167a910d4ff9b43c9e69000f43753719909a Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Fri, 24 Jun 2011 16:18:13 +0200 Subject: [PATCH 058/209] usb-ehci: Add support for registering companion controllers Signed-off-by: Hans de Goede Signed-off-by: Gerd Hoffmann --- hw/usb-ehci.c | 174 +++++++++++++++++++++++++++++++++++++++++--------- 1 file changed, 144 insertions(+), 30 deletions(-) diff --git a/hw/usb-ehci.c b/hw/usb-ehci.c index 973c34292e..ec68c299fe 100644 --- a/hw/usb-ehci.c +++ b/hw/usb-ehci.c @@ -20,9 +20,6 @@ * * You should have received a copy of the GNU General Public License * along with this program; if not, see . - * - * TODO: - * o Downstream port handoff */ #include "hw.h" @@ -106,7 +103,7 @@ * Bits that are reserved or are read-only are masked out of values * written to us by software */ -#define PORTSC_RO_MASK 0x007021c0 +#define PORTSC_RO_MASK 0x007001c0 #define PORTSC_RWC_MASK 0x0000002a #define PORTSC_WKOC_E (1 << 22) // Wake on Over Current Enable #define PORTSC_WKDS_E (1 << 21) // Wake on Disconnect Enable @@ -373,6 +370,7 @@ struct EHCIState { qemu_irq irq; target_phys_addr_t mem_base; int mem; + int companion_count; /* properties */ uint32_t freq; @@ -408,6 +406,7 @@ struct EHCIState { int astate; // Current state in asynchronous schedule int pstate; // Current state in periodic schedule USBPort ports[NB_PORTS]; + USBPort *companion_ports[NB_PORTS]; uint32_t usbsts_pending; QTAILQ_HEAD(, EHCIQueue) queues; @@ -730,17 +729,17 @@ static void ehci_attach(USBPort *port) trace_usb_ehci_port_attach(port->index, port->dev->product_desc); + if (*portsc & PORTSC_POWNER) { + USBPort *companion = s->companion_ports[port->index]; + companion->dev = port->dev; + companion->ops->attach(companion); + return; + } + *portsc |= PORTSC_CONNECT; *portsc |= PORTSC_CSC; - /* - * If a high speed device is attached then we own this port(indicated - * by zero in the PORTSC_POWNER bit field) so set the status bit - * and set an interrupt if enabled. - */ - if ( !(*portsc & PORTSC_POWNER)) { - ehci_set_interrupt(s, USBSTS_PCD); - } + ehci_set_interrupt(s, USBSTS_PCD); } static void ehci_detach(USBPort *port) @@ -750,36 +749,110 @@ static void ehci_detach(USBPort *port) trace_usb_ehci_port_detach(port->index); + if (*portsc & PORTSC_POWNER) { + USBPort *companion = s->companion_ports[port->index]; + companion->ops->detach(companion); + companion->dev = NULL; + return; + } + ehci_queues_rip_device(s, port->dev); *portsc &= ~(PORTSC_CONNECT|PORTSC_PED); *portsc |= PORTSC_CSC; - /* - * If a high speed device is attached then we own this port(indicated - * by zero in the PORTSC_POWNER bit field) so set the status bit - * and set an interrupt if enabled. - */ - if ( !(*portsc & PORTSC_POWNER)) { - ehci_set_interrupt(s, USBSTS_PCD); - } + ehci_set_interrupt(s, USBSTS_PCD); } static void ehci_child_detach(USBPort *port, USBDevice *child) { EHCIState *s = port->opaque; + uint32_t portsc = s->portsc[port->index]; + + if (portsc & PORTSC_POWNER) { + USBPort *companion = s->companion_ports[port->index]; + companion->ops->child_detach(companion, child); + companion->dev = NULL; + return; + } ehci_queues_rip_device(s, child); } +static void ehci_wakeup(USBPort *port) +{ + EHCIState *s = port->opaque; + uint32_t portsc = s->portsc[port->index]; + + if (portsc & PORTSC_POWNER) { + USBPort *companion = s->companion_ports[port->index]; + if (companion->ops->wakeup) { + companion->ops->wakeup(companion); + } + } +} + +static int ehci_register_companion(USBBus *bus, USBPort *ports[], + uint32_t portcount, uint32_t firstport) +{ + EHCIState *s = container_of(bus, EHCIState, bus); + uint32_t i; + + if (firstport + portcount > NB_PORTS) { + qerror_report(QERR_INVALID_PARAMETER_VALUE, "firstport", + "firstport on masterbus"); + error_printf_unless_qmp( + "firstport value of %u makes companion take ports %u - %u, which " + "is outside of the valid range of 0 - %u\n", firstport, firstport, + firstport + portcount - 1, NB_PORTS - 1); + return -1; + } + + for (i = 0; i < portcount; i++) { + if (s->companion_ports[firstport + i]) { + qerror_report(QERR_INVALID_PARAMETER_VALUE, "masterbus", + "an USB masterbus"); + error_printf_unless_qmp( + "port %u on masterbus %s already has a companion assigned\n", + firstport + i, bus->qbus.name); + return -1; + } + } + + for (i = 0; i < portcount; i++) { + s->companion_ports[firstport + i] = ports[i]; + s->ports[firstport + i].speedmask |= + USB_SPEED_MASK_LOW | USB_SPEED_MASK_FULL; + /* Ensure devs attached before the initial reset go to the companion */ + s->portsc[firstport + i] = PORTSC_POWNER; + } + + s->companion_count++; + s->mmio[0x05] = (s->companion_count << 4) | portcount; + + return 0; +} + /* 4.1 host controller initialization */ static void ehci_reset(void *opaque) { EHCIState *s = opaque; int i; + USBDevice *devs[NB_PORTS]; trace_usb_ehci_reset(); + /* + * Do the detach before touching portsc, so that it correctly gets send to + * us or to our companion based on PORTSC_POWNER before the reset. + */ + for(i = 0; i < NB_PORTS; i++) { + devs[i] = s->ports[i].dev; + if (devs[i]) { + usb_attach(&s->ports[i], NULL); + } + } + memset(&s->mmio[OPREGBASE], 0x00, MMIO_SIZE - OPREGBASE); s->usbcmd = NB_MAXINTRATE << USBCMD_ITC_SH; @@ -791,10 +864,13 @@ static void ehci_reset(void *opaque) s->attach_poll_counter = 0; for(i = 0; i < NB_PORTS; i++) { - s->portsc[i] = PORTSC_POWNER | PORTSC_PPOWER; - - if (s->ports[i].dev) { - usb_attach(&s->ports[i], s->ports[i].dev); + if (s->companion_ports[i]) { + s->portsc[i] = PORTSC_POWNER | PORTSC_PPOWER; + } else { + s->portsc[i] = PORTSC_PPOWER; + } + if (devs[i]) { + usb_attach(&s->ports[i], devs[i]); } } ehci_queues_rip_all(s); @@ -844,6 +920,34 @@ static void ehci_mem_writew(void *ptr, target_phys_addr_t addr, uint32_t val) exit(1); } +static void handle_port_owner_write(EHCIState *s, int port, uint32_t owner) +{ + USBDevice *dev = s->ports[port].dev; + uint32_t *portsc = &s->portsc[port]; + uint32_t orig; + + if (s->companion_ports[port] == NULL) + return; + + owner = owner & PORTSC_POWNER; + orig = *portsc & PORTSC_POWNER; + + if (!(owner ^ orig)) { + return; + } + + if (dev) { + usb_attach(&s->ports[port], NULL); + } + + *portsc &= ~PORTSC_POWNER; + *portsc |= owner; + + if (dev) { + usb_attach(&s->ports[port], dev); + } +} + static void handle_port_status_write(EHCIState *s, int port, uint32_t val) { uint32_t *portsc = &s->portsc[port]; @@ -853,6 +957,9 @@ static void handle_port_status_write(EHCIState *s, int port, uint32_t val) *portsc &= ~(val & PORTSC_RWC_MASK); /* The guest may clear, but not set the PED bit */ *portsc &= val | ~PORTSC_PED; + /* POWNER is masked out by RO_MASK as it is RO when we've no companion */ + handle_port_owner_write(s, port, val); + /* And finally apply RO_MASK */ val &= PORTSC_RO_MASK; if ((val & PORTSC_PRESET) && !(*portsc & PORTSC_PRESET)) { @@ -956,7 +1063,7 @@ static void ehci_mem_writel(void *ptr, target_phys_addr_t addr, uint32_t val) val &= 0x1; if (val) { for(i = 0; i < NB_PORTS; i++) - s->portsc[i] &= ~PORTSC_POWNER; + handle_port_owner_write(s, i, 0); } break; @@ -1114,8 +1221,17 @@ static int ehci_buffer_rw(EHCIQueue *q, int bytes, int rw) static void ehci_async_complete_packet(USBPort *port, USBPacket *packet) { - EHCIQueue *q = container_of(packet, EHCIQueue, packet); + EHCIQueue *q; + EHCIState *s = port->opaque; + uint32_t portsc = s->portsc[port->index]; + if (portsc & PORTSC_POWNER) { + USBPort *companion = s->companion_ports[port->index]; + companion->ops->complete(companion, packet); + return; + } + + q = container_of(packet, EHCIQueue, packet); trace_usb_ehci_queue_action(q, "wakeup"); assert(q->async == EHCI_ASYNC_INFLIGHT); q->async = EHCI_ASYNC_FINISHED; @@ -1245,8 +1361,6 @@ static int ehci_execute(EHCIQueue *q) port = &q->ehci->ports[i]; dev = port->dev; - // TODO sometime we will also need to check if we are the port owner - if (!(q->ehci->portsc[i] &(PORTSC_CONNECT))) { DPRINTF("Port %d, no exec, not connected(%08X)\n", i, q->ehci->portsc[i]); @@ -1339,8 +1453,6 @@ static int ehci_process_itd(EHCIState *ehci, port = &ehci->ports[j]; dev = port->dev; - // TODO sometime we will also need to check if we are the port owner - if (!(ehci->portsc[j] &(PORTSC_CONNECT))) { continue; } @@ -2124,10 +2236,12 @@ static USBPortOps ehci_port_ops = { .attach = ehci_attach, .detach = ehci_detach, .child_detach = ehci_child_detach, + .wakeup = ehci_wakeup, .complete = ehci_async_complete_packet, }; static USBBusOps ehci_bus_ops = { + .register_companion = ehci_register_companion, }; static PCIDeviceInfo ehci_info = { From 35e4977f575a377fc1fa05d819a533b2c5c8646f Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Fri, 24 Jun 2011 17:44:53 +0200 Subject: [PATCH 059/209] usb-uhci: Add support for being a companion controller To use as a companion controller set the masterbus property. Signed-off-by: Hans de Goede Signed-off-by: Gerd Hoffmann --- hw/usb-uhci.c | 41 ++++++++++++++++++++++++++++++++++++----- 1 file changed, 36 insertions(+), 5 deletions(-) diff --git a/hw/usb-uhci.c b/hw/usb-uhci.c index a46d61a2e3..925c03bb0e 100644 --- a/hw/usb-uhci.c +++ b/hw/usb-uhci.c @@ -132,7 +132,7 @@ typedef struct UHCIPort { struct UHCIState { PCIDevice dev; - USBBus bus; + USBBus bus; /* Note unused when we're a companion controller */ uint16_t cmd; /* cmd register */ uint16_t status; uint16_t intr; /* interrupt enable register */ @@ -150,6 +150,10 @@ struct UHCIState { /* Active packets */ QTAILQ_HEAD(,UHCIAsync) async_pending; uint8_t num_ports_vmstate; + + /* Properties */ + char *masterbus; + uint32_t firstport; }; typedef struct UHCI_TD { @@ -1126,10 +1130,22 @@ static int usb_uhci_common_initfn(PCIDevice *dev) pci_conf[PCI_INTERRUPT_PIN] = 4; // interrupt pin 3 pci_conf[USB_SBRN] = USB_RELEASE_1; // release number - usb_bus_new(&s->bus, &uhci_bus_ops, &s->dev.qdev); - for(i = 0; i < NB_PORTS; i++) { - usb_register_port(&s->bus, &s->ports[i].port, s, i, &uhci_port_ops, - USB_SPEED_MASK_LOW | USB_SPEED_MASK_FULL); + if (s->masterbus) { + USBPort *ports[NB_PORTS]; + for(i = 0; i < NB_PORTS; i++) { + ports[i] = &s->ports[i].port; + } + if (usb_register_companion(s->masterbus, ports, NB_PORTS, + s->firstport, s, &uhci_port_ops, + USB_SPEED_MASK_LOW | USB_SPEED_MASK_FULL) != 0) { + return -1; + } + } else { + usb_bus_new(&s->bus, &uhci_bus_ops, &s->dev.qdev); + for (i = 0; i < NB_PORTS; i++) { + usb_register_port(&s->bus, &s->ports[i].port, s, i, &uhci_port_ops, + USB_SPEED_MASK_LOW | USB_SPEED_MASK_FULL); + } } s->frame_timer = qemu_new_timer_ns(vm_clock, uhci_frame_timer, s); s->num_ports_vmstate = NB_PORTS; @@ -1170,6 +1186,11 @@ static PCIDeviceInfo uhci_info[] = { .device_id = PCI_DEVICE_ID_INTEL_82371SB_2, .revision = 0x01, .class_id = PCI_CLASS_SERIAL_USB, + .qdev.props = (Property[]) { + DEFINE_PROP_STRING("masterbus", UHCIState, masterbus), + DEFINE_PROP_UINT32("firstport", UHCIState, firstport, 0), + DEFINE_PROP_END_OF_LIST(), + }, },{ .qdev.name = "piix4-usb-uhci", .qdev.size = sizeof(UHCIState), @@ -1179,6 +1200,11 @@ static PCIDeviceInfo uhci_info[] = { .device_id = PCI_DEVICE_ID_INTEL_82371AB_2, .revision = 0x01, .class_id = PCI_CLASS_SERIAL_USB, + .qdev.props = (Property[]) { + DEFINE_PROP_STRING("masterbus", UHCIState, masterbus), + DEFINE_PROP_UINT32("firstport", UHCIState, firstport, 0), + DEFINE_PROP_END_OF_LIST(), + }, },{ .qdev.name = "vt82c686b-usb-uhci", .qdev.size = sizeof(UHCIState), @@ -1188,6 +1214,11 @@ static PCIDeviceInfo uhci_info[] = { .device_id = PCI_DEVICE_ID_VIA_UHCI, .revision = 0x01, .class_id = PCI_CLASS_SERIAL_USB, + .qdev.props = (Property[]) { + DEFINE_PROP_STRING("masterbus", UHCIState, masterbus), + DEFINE_PROP_UINT32("firstport", UHCIState, firstport, 0), + DEFINE_PROP_END_OF_LIST(), + }, },{ /* end of list */ } From 9c9fc3346bd6ff87babd0e5f7ea0f500d3af5765 Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Fri, 24 Jun 2011 20:29:05 +0200 Subject: [PATCH 060/209] usb-ohci: Add support for being a companion controller To use as a companion controller, use pci-ohci as device and set the masterbus and num-ports properties, ie: -device usb-ehci,addr=0b.1,multifunction=on,id=ehci0 -device pci-ohci,addr=0b.0,multifunction=on,masterbus=ehci0.0,num-ports=4 Signed-off-by: Hans de Goede Signed-off-by: Gerd Hoffmann --- hw/usb-ohci.c | 52 +++++++++++++++++++++++++++++++++++++++------------ 1 file changed, 40 insertions(+), 12 deletions(-) diff --git a/hw/usb-ohci.c b/hw/usb-ohci.c index 46f0bcbd53..c77a20e4f5 100644 --- a/hw/usb-ohci.c +++ b/hw/usb-ohci.c @@ -1716,8 +1716,9 @@ static USBPortOps ohci_port_ops = { static USBBusOps ohci_bus_ops = { }; -static void usb_ohci_init(OHCIState *ohci, DeviceState *dev, - int num_ports, uint32_t localmem_base) +static int usb_ohci_init(OHCIState *ohci, DeviceState *dev, + int num_ports, uint32_t localmem_base, + char *masterbus, uint32_t firstport) { int i; @@ -1737,38 +1738,58 @@ static void usb_ohci_init(OHCIState *ohci, DeviceState *dev, usb_frame_time, usb_bit_time); } + ohci->num_ports = num_ports; + if (masterbus) { + USBPort *ports[OHCI_MAX_PORTS]; + for(i = 0; i < num_ports; i++) { + ports[i] = &ohci->rhport[i].port; + } + if (usb_register_companion(masterbus, ports, num_ports, + firstport, ohci, &ohci_port_ops, + USB_SPEED_MASK_LOW | USB_SPEED_MASK_FULL) != 0) { + return -1; + } + } else { + usb_bus_new(&ohci->bus, &ohci_bus_ops, dev); + for (i = 0; i < num_ports; i++) { + usb_register_port(&ohci->bus, &ohci->rhport[i].port, + ohci, i, &ohci_port_ops, + USB_SPEED_MASK_LOW | USB_SPEED_MASK_FULL); + } + } + ohci->mem = cpu_register_io_memory(ohci_readfn, ohci_writefn, ohci, DEVICE_LITTLE_ENDIAN); ohci->localmem_base = localmem_base; ohci->name = dev->info->name; - usb_bus_new(&ohci->bus, &ohci_bus_ops, dev); - ohci->num_ports = num_ports; - for (i = 0; i < num_ports; i++) { - usb_register_port(&ohci->bus, &ohci->rhport[i].port, ohci, i, &ohci_port_ops, - USB_SPEED_MASK_LOW | USB_SPEED_MASK_FULL); - } - ohci->async_td = 0; qemu_register_reset(ohci_reset, ohci); + + return 0; } typedef struct { PCIDevice pci_dev; OHCIState state; + char *masterbus; + uint32_t num_ports; + uint32_t firstport; } OHCIPCIState; static int usb_ohci_initfn_pci(struct PCIDevice *dev) { OHCIPCIState *ohci = DO_UPCAST(OHCIPCIState, pci_dev, dev); - int num_ports = 3; ohci->pci_dev.config[PCI_CLASS_PROG] = 0x10; /* OHCI */ /* TODO: RST# value should be 0. */ ohci->pci_dev.config[PCI_INTERRUPT_PIN] = 0x01; /* interrupt pin 1 */ - usb_ohci_init(&ohci->state, &dev->qdev, num_ports, 0); + if (usb_ohci_init(&ohci->state, &dev->qdev, ohci->num_ports, 0, + ohci->masterbus, ohci->firstport) != 0) { + return -1; + } ohci->state.irq = ohci->pci_dev.irq[0]; /* TODO: avoid cast below by using dev */ @@ -1792,7 +1813,8 @@ static int ohci_init_pxa(SysBusDevice *dev) { OHCISysBusState *s = FROM_SYSBUS(OHCISysBusState, dev); - usb_ohci_init(&s->ohci, &dev->qdev, s->num_ports, s->dma_offset); + /* Cannot fail as we pass NULL for masterbus */ + usb_ohci_init(&s->ohci, &dev->qdev, s->num_ports, s->dma_offset, NULL, 0); sysbus_init_irq(dev, &s->ohci.irq); sysbus_init_mmio(dev, 0x1000, s->ohci.mem); @@ -1807,6 +1829,12 @@ static PCIDeviceInfo ohci_pci_info = { .vendor_id = PCI_VENDOR_ID_APPLE, .device_id = PCI_DEVICE_ID_APPLE_IPID_USB, .class_id = PCI_CLASS_SERIAL_USB, + .qdev.props = (Property[]) { + DEFINE_PROP_STRING("masterbus", OHCIPCIState, masterbus), + DEFINE_PROP_UINT32("num-ports", OHCIPCIState, num_ports, 3), + DEFINE_PROP_UINT32("firstport", OHCIPCIState, firstport, 0), + DEFINE_PROP_END_OF_LIST(), + }, }; static SysBusDeviceInfo ohci_sysbus_info = { From f9ebf5e564efe10115b48b743d3e175917917248 Mon Sep 17 00:00:00 2001 From: Gerd Hoffmann Date: Fri, 1 Jul 2011 11:45:02 +0200 Subject: [PATCH 061/209] pci: add ich9 usb controller ids Signed-off-by: Gerd Hoffmann --- hw/pci_ids.h | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/hw/pci_ids.h b/hw/pci_ids.h index d94578c87d..927f2b0627 100644 --- a/hw/pci_ids.h +++ b/hw/pci_ids.h @@ -109,5 +109,13 @@ #define PCI_DEVICE_ID_INTEL_82371AB 0x7111 #define PCI_DEVICE_ID_INTEL_82371AB_2 0x7112 #define PCI_DEVICE_ID_INTEL_82371AB_3 0x7113 +#define PCI_DEVICE_ID_INTEL_82801I_UHCI1 0x2934 +#define PCI_DEVICE_ID_INTEL_82801I_UHCI2 0x2935 +#define PCI_DEVICE_ID_INTEL_82801I_UHCI3 0x2936 +#define PCI_DEVICE_ID_INTEL_82801I_UHCI4 0x2937 +#define PCI_DEVICE_ID_INTEL_82801I_UHCI5 0x2938 +#define PCI_DEVICE_ID_INTEL_82801I_UHCI6 0x2939 +#define PCI_DEVICE_ID_INTEL_82801I_EHCI1 0x293a +#define PCI_DEVICE_ID_INTEL_82801I_EHCI2 0x293c #define PCI_VENDOR_ID_XENSOURCE 0x5853 From 1b5a757067a5ecd3504a067937b6494811a62f46 Mon Sep 17 00:00:00 2001 From: Gerd Hoffmann Date: Fri, 1 Jul 2011 09:48:49 +0200 Subject: [PATCH 062/209] uhci: add ich9 controllers Add ich9 controllers, Factor out properties to a separate struct and reference it to reduce duplication. Signed-off-by: Gerd Hoffmann --- hw/usb-uhci.c | 54 +++++++++++++++++++++++++++++++++++++-------------- 1 file changed, 39 insertions(+), 15 deletions(-) diff --git a/hw/usb-uhci.c b/hw/usb-uhci.c index 925c03bb0e..2ef4c5b747 100644 --- a/hw/usb-uhci.c +++ b/hw/usb-uhci.c @@ -1176,6 +1176,12 @@ static int usb_uhci_vt82c686b_initfn(PCIDevice *dev) return usb_uhci_common_initfn(dev); } +static Property uhci_properties[] = { + DEFINE_PROP_STRING("masterbus", UHCIState, masterbus), + DEFINE_PROP_UINT32("firstport", UHCIState, firstport, 0), + DEFINE_PROP_END_OF_LIST(), +}; + static PCIDeviceInfo uhci_info[] = { { .qdev.name = "piix3-usb-uhci", @@ -1186,11 +1192,7 @@ static PCIDeviceInfo uhci_info[] = { .device_id = PCI_DEVICE_ID_INTEL_82371SB_2, .revision = 0x01, .class_id = PCI_CLASS_SERIAL_USB, - .qdev.props = (Property[]) { - DEFINE_PROP_STRING("masterbus", UHCIState, masterbus), - DEFINE_PROP_UINT32("firstport", UHCIState, firstport, 0), - DEFINE_PROP_END_OF_LIST(), - }, + .qdev.props = uhci_properties, },{ .qdev.name = "piix4-usb-uhci", .qdev.size = sizeof(UHCIState), @@ -1200,11 +1202,7 @@ static PCIDeviceInfo uhci_info[] = { .device_id = PCI_DEVICE_ID_INTEL_82371AB_2, .revision = 0x01, .class_id = PCI_CLASS_SERIAL_USB, - .qdev.props = (Property[]) { - DEFINE_PROP_STRING("masterbus", UHCIState, masterbus), - DEFINE_PROP_UINT32("firstport", UHCIState, firstport, 0), - DEFINE_PROP_END_OF_LIST(), - }, + .qdev.props = uhci_properties, },{ .qdev.name = "vt82c686b-usb-uhci", .qdev.size = sizeof(UHCIState), @@ -1214,11 +1212,37 @@ static PCIDeviceInfo uhci_info[] = { .device_id = PCI_DEVICE_ID_VIA_UHCI, .revision = 0x01, .class_id = PCI_CLASS_SERIAL_USB, - .qdev.props = (Property[]) { - DEFINE_PROP_STRING("masterbus", UHCIState, masterbus), - DEFINE_PROP_UINT32("firstport", UHCIState, firstport, 0), - DEFINE_PROP_END_OF_LIST(), - }, + .qdev.props = uhci_properties, + },{ + .qdev.name = "ich9-usb-uhci1", + .qdev.size = sizeof(UHCIState), + .qdev.vmsd = &vmstate_uhci, + .init = usb_uhci_common_initfn, + .vendor_id = PCI_VENDOR_ID_INTEL, + .device_id = PCI_DEVICE_ID_INTEL_82801I_UHCI1, + .revision = 0x03, + .class_id = PCI_CLASS_SERIAL_USB, + .qdev.props = uhci_properties, + },{ + .qdev.name = "ich9-usb-uhci2", + .qdev.size = sizeof(UHCIState), + .qdev.vmsd = &vmstate_uhci, + .init = usb_uhci_common_initfn, + .vendor_id = PCI_VENDOR_ID_INTEL, + .device_id = PCI_DEVICE_ID_INTEL_82801I_UHCI2, + .revision = 0x03, + .class_id = PCI_CLASS_SERIAL_USB, + .qdev.props = uhci_properties, + },{ + .qdev.name = "ich9-usb-uhci3", + .qdev.size = sizeof(UHCIState), + .qdev.vmsd = &vmstate_uhci, + .init = usb_uhci_common_initfn, + .vendor_id = PCI_VENDOR_ID_INTEL, + .device_id = PCI_DEVICE_ID_INTEL_82801I_UHCI3, + .revision = 0x03, + .class_id = PCI_CLASS_SERIAL_USB, + .qdev.props = uhci_properties, },{ /* end of list */ } From 5cc194caeb019cf1dae7f74ccbdf0401a56c2ac6 Mon Sep 17 00:00:00 2001 From: Gerd Hoffmann Date: Fri, 1 Jul 2011 09:56:43 +0200 Subject: [PATCH 063/209] ehci: fix port count. The ICH4 EHCI controller which we emulate has six ports not four. Signed-off-by: Gerd Hoffmann --- hw/usb-ehci.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hw/usb-ehci.c b/hw/usb-ehci.c index ec68c299fe..0b959caff3 100644 --- a/hw/usb-ehci.c +++ b/hw/usb-ehci.c @@ -130,7 +130,7 @@ #define FRAME_TIMER_NS (1000000000 / FRAME_TIMER_FREQ) #define NB_MAXINTRATE 8 // Max rate at which controller issues ints -#define NB_PORTS 4 // Number of downstream ports +#define NB_PORTS 6 // Number of downstream ports #define BUFF_SIZE 5*4096 // Max bytes to transfer per transaction #define MAX_ITERATIONS 20 // Max number of QH before we break the loop #define MAX_QH 100 // Max allowable queue heads in a chain From 3028376ea0239e3820842bb596b21822e2373e9d Mon Sep 17 00:00:00 2001 From: Gerd Hoffmann Date: Fri, 1 Jul 2011 11:51:02 +0200 Subject: [PATCH 064/209] ehci: add ich9 controller. Signed-off-by: Gerd Hoffmann --- hw/usb-ehci.c | 43 +++++++++++++++++++++++++++++-------------- 1 file changed, 29 insertions(+), 14 deletions(-) diff --git a/hw/usb-ehci.c b/hw/usb-ehci.c index 0b959caff3..a4758f976e 100644 --- a/hw/usb-ehci.c +++ b/hw/usb-ehci.c @@ -2244,19 +2244,34 @@ static USBBusOps ehci_bus_ops = { .register_companion = ehci_register_companion, }; -static PCIDeviceInfo ehci_info = { - .qdev.name = "usb-ehci", - .qdev.size = sizeof(EHCIState), - .init = usb_ehci_initfn, - .vendor_id = PCI_VENDOR_ID_INTEL, - .device_id = PCI_DEVICE_ID_INTEL_82801D, - .revision = 0x10, - .class_id = PCI_CLASS_SERIAL_USB, - .qdev.props = (Property[]) { - DEFINE_PROP_UINT32("freq", EHCIState, freq, FRAME_TIMER_FREQ), - DEFINE_PROP_UINT32("maxframes", EHCIState, maxframes, 128), - DEFINE_PROP_END_OF_LIST(), - }, +static Property ehci_properties[] = { + DEFINE_PROP_UINT32("freq", EHCIState, freq, FRAME_TIMER_FREQ), + DEFINE_PROP_UINT32("maxframes", EHCIState, maxframes, 128), + DEFINE_PROP_END_OF_LIST(), +}; + +static PCIDeviceInfo ehci_info[] = { + { + .qdev.name = "usb-ehci", + .qdev.size = sizeof(EHCIState), + .init = usb_ehci_initfn, + .vendor_id = PCI_VENDOR_ID_INTEL, + .device_id = PCI_DEVICE_ID_INTEL_82801D, /* ich4 */ + .revision = 0x10, + .class_id = PCI_CLASS_SERIAL_USB, + .qdev.props = ehci_properties, + },{ + .qdev.name = "ich9-usb-ehci1", + .qdev.size = sizeof(EHCIState), + .init = usb_ehci_initfn, + .vendor_id = PCI_VENDOR_ID_INTEL, + .device_id = PCI_DEVICE_ID_INTEL_82801I_EHCI1, + .revision = 0x03, + .class_id = PCI_CLASS_SERIAL_USB, + .qdev.props = ehci_properties, + },{ + /* end of list */ + } }; static int usb_ehci_initfn(PCIDevice *dev) @@ -2335,7 +2350,7 @@ static int usb_ehci_initfn(PCIDevice *dev) static void ehci_register(void) { - pci_qdev_register(&ehci_info); + pci_qdev_register_many(ehci_info); } device_init(ehci_register); From 76f30473da752ee8448e80dfd1e050cedd482b6c Mon Sep 17 00:00:00 2001 From: Gerd Hoffmann Date: Tue, 5 Jul 2011 16:58:41 +0200 Subject: [PATCH 065/209] usb: update documentation Add a paragraph on companion controller mode and a configuration file which sets it all up for you. Signed-off-by: Gerd Hoffmann --- docs/ich9-ehci-uhci.cfg | 37 +++++++++++++++++++++++++++++++++++++ docs/usb2.txt | 33 ++++++++++++++++++++++++++++----- 2 files changed, 65 insertions(+), 5 deletions(-) create mode 100644 docs/ich9-ehci-uhci.cfg diff --git a/docs/ich9-ehci-uhci.cfg b/docs/ich9-ehci-uhci.cfg new file mode 100644 index 0000000000..a0e9b96f4d --- /dev/null +++ b/docs/ich9-ehci-uhci.cfg @@ -0,0 +1,37 @@ +########################################################################### +# +# You can pass this file directly to qemu using the -readconfig +# command line switch. +# +# This config file creates a EHCI adapter with companion UHCI +# controllers as multifunction device in PCI slot "1d". +# +# Specify "bus=ehci.0" when creating usb devices to hook them up +# there. +# + +[device "ehci"] + driver = "ich9-usb-ehci1" + addr = "1d.7" + multifunction = "on" + +[device "uhci-1"] + driver = "ich9-usb-uhci1" + addr = "1d.0" + multifunction = "on" + masterbus = "ehci.0" + firstport = "0" + +[device "uhci-2"] + driver = "ich9-usb-uhci2" + addr = "1d.1" + multifunction = "on" + masterbus = "ehci.0" + firstport = "2" + +[device "uhci-3"] + driver = "ich9-usb-uhci3" + addr = "1d.2" + multifunction = "on" + masterbus = "ehci.0" + firstport = "4" diff --git a/docs/usb2.txt b/docs/usb2.txt index 5950c713e9..228aa33ceb 100644 --- a/docs/usb2.txt +++ b/docs/usb2.txt @@ -2,11 +2,13 @@ USB 2.0 Quick Start =================== -The QEMU EHCI Adapter does *not* support companion controllers. That -implies there are two completely separate USB busses: One USB 1.1 bus -driven by the UHCI controller and one USB 2.0 bus driven by the EHCI -controller. Devices must be attached to the correct controller -manually. +The QEMU EHCI Adapter can be used with and without companion +controllers. See below for the companion controller mode. + +When not running in companion controller mode there are two completely +separate USB busses: One USB 1.1 bus driven by the UHCI controller and +one USB 2.0 bus driven by the EHCI controller. Devices must be +attached to the correct controller manually. The '-usb' switch will make qemu create the UHCI controller as part of the PIIX3 chipset. The USB 1.1 bus will carry the name "usb.0". @@ -32,6 +34,27 @@ This attaches a usb tablet to the UHCI adapter and a usb mass storage device to the EHCI adapter. +Companion controller support +---------------------------- + +Companion controller support has been added recently. The operational +model described above with two completely separate busses still works +fine. Additionally the UHCI and OHCI controllers got the ability to +attach to a usb bus created by EHCI as companion controllers. This is +done by specifying the masterbus and firstport properties. masterbus +specifies the bus name the controller should attach to. firstport +specifies the first port the controller should attach to, which is +needed as usually one ehci controller with six ports has three uhci +companion controllers with two ports each. + +There is a config file in docs which will do all this for you, just +try ... + + qemu -readconfig docs/ich9-ehci-uhci.cfg + +... then use "bus=ehci.0" to assign your usb devices to that bus. + + More USB tips & tricks ====================== From eb3b58f96f6740ab1cc64ba514367ce1814779e4 Mon Sep 17 00:00:00 2001 From: Jes Sorensen Date: Mon, 4 Jul 2011 17:33:05 +0200 Subject: [PATCH 066/209] usb_register_port(): do not set port->opaque and port->index twice Signed-off-by: Jes Sorensen Signed-off-by: Gerd Hoffmann --- hw/usb-bus.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/hw/usb-bus.c b/hw/usb-bus.c index c8347e9e3b..f1dd55eccd 100644 --- a/hw/usb-bus.c +++ b/hw/usb-bus.c @@ -141,8 +141,6 @@ USBDevice *usb_create_simple(USBBus *bus, const char *name) static void usb_fill_port(USBPort *port, void *opaque, int index, USBPortOps *ops, int speedmask) { - port->opaque = opaque; - port->index = index; port->opaque = opaque; port->index = index; port->ops = ops; From dd850cf2030b99d34019dd50e0df43907a3e1b41 Mon Sep 17 00:00:00 2001 From: Gerd Hoffmann Date: Wed, 6 Jul 2011 12:40:28 +0200 Subject: [PATCH 067/209] usb: fixup bluetooth descriptors Commit 4696425cd05c7baa0a4b469d43ba4b8488bcfc0f changes some endpoints from isocrounous to interrupt by mistake. Fix it. Signed-off-by: Gerd Hoffmann --- hw/usb-bt.c | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/hw/usb-bt.c b/hw/usb-bt.c index baae4876ea..e364513a01 100644 --- a/hw/usb-bt.c +++ b/hw/usb-bt.c @@ -99,13 +99,13 @@ static const USBDescIface desc_iface_bluetooth[] = { .eps = (USBDescEndpoint[]) { { .bEndpointAddress = USB_DIR_OUT | USB_SCO_EP, - .bmAttributes = USB_ENDPOINT_XFER_INT, + .bmAttributes = USB_ENDPOINT_XFER_ISOC, .wMaxPacketSize = 0, .bInterval = 0x01, }, { .bEndpointAddress = USB_DIR_IN | USB_SCO_EP, - .bmAttributes = USB_ENDPOINT_XFER_INT, + .bmAttributes = USB_ENDPOINT_XFER_ISOC, .wMaxPacketSize = 0, .bInterval = 0x01, }, @@ -120,13 +120,13 @@ static const USBDescIface desc_iface_bluetooth[] = { .eps = (USBDescEndpoint[]) { { .bEndpointAddress = USB_DIR_OUT | USB_SCO_EP, - .bmAttributes = USB_ENDPOINT_XFER_INT, + .bmAttributes = USB_ENDPOINT_XFER_ISOC, .wMaxPacketSize = 0x09, .bInterval = 0x01, }, { .bEndpointAddress = USB_DIR_IN | USB_SCO_EP, - .bmAttributes = USB_ENDPOINT_XFER_INT, + .bmAttributes = USB_ENDPOINT_XFER_ISOC, .wMaxPacketSize = 0x09, .bInterval = 0x01, }, @@ -141,13 +141,13 @@ static const USBDescIface desc_iface_bluetooth[] = { .eps = (USBDescEndpoint[]) { { .bEndpointAddress = USB_DIR_OUT | USB_SCO_EP, - .bmAttributes = USB_ENDPOINT_XFER_INT, + .bmAttributes = USB_ENDPOINT_XFER_ISOC, .wMaxPacketSize = 0x11, .bInterval = 0x01, }, { .bEndpointAddress = USB_DIR_IN | USB_SCO_EP, - .bmAttributes = USB_ENDPOINT_XFER_INT, + .bmAttributes = USB_ENDPOINT_XFER_ISOC, .wMaxPacketSize = 0x11, .bInterval = 0x01, }, @@ -162,13 +162,13 @@ static const USBDescIface desc_iface_bluetooth[] = { .eps = (USBDescEndpoint[]) { { .bEndpointAddress = USB_DIR_OUT | USB_SCO_EP, - .bmAttributes = USB_ENDPOINT_XFER_INT, + .bmAttributes = USB_ENDPOINT_XFER_ISOC, .wMaxPacketSize = 0x19, .bInterval = 0x01, }, { .bEndpointAddress = USB_DIR_IN | USB_SCO_EP, - .bmAttributes = USB_ENDPOINT_XFER_INT, + .bmAttributes = USB_ENDPOINT_XFER_ISOC, .wMaxPacketSize = 0x19, .bInterval = 0x01, }, @@ -183,13 +183,13 @@ static const USBDescIface desc_iface_bluetooth[] = { .eps = (USBDescEndpoint[]) { { .bEndpointAddress = USB_DIR_OUT | USB_SCO_EP, - .bmAttributes = USB_ENDPOINT_XFER_INT, + .bmAttributes = USB_ENDPOINT_XFER_ISOC, .wMaxPacketSize = 0x21, .bInterval = 0x01, }, { .bEndpointAddress = USB_DIR_IN | USB_SCO_EP, - .bmAttributes = USB_ENDPOINT_XFER_INT, + .bmAttributes = USB_ENDPOINT_XFER_ISOC, .wMaxPacketSize = 0x21, .bInterval = 0x01, }, @@ -204,13 +204,13 @@ static const USBDescIface desc_iface_bluetooth[] = { .eps = (USBDescEndpoint[]) { { .bEndpointAddress = USB_DIR_OUT | USB_SCO_EP, - .bmAttributes = USB_ENDPOINT_XFER_INT, + .bmAttributes = USB_ENDPOINT_XFER_ISOC, .wMaxPacketSize = 0x31, .bInterval = 0x01, }, { .bEndpointAddress = USB_DIR_IN | USB_SCO_EP, - .bmAttributes = USB_ENDPOINT_XFER_INT, + .bmAttributes = USB_ENDPOINT_XFER_ISOC, .wMaxPacketSize = 0x31, .bInterval = 0x01, }, From 6c2385270b1c495515cd685b2f77c76a1b3fc864 Mon Sep 17 00:00:00 2001 From: Gerd Hoffmann Date: Thu, 7 Jul 2011 15:02:58 +0200 Subject: [PATCH 068/209] usb-hub: remove unused descriptor arrays Somehow they where left over when converting the hub to the new usb descriptor infrastructure ... Signed-off-by: Gerd Hoffmann --- hw/usb-hub.c | 68 ---------------------------------------------------- 1 file changed, 68 deletions(-) diff --git a/hw/usb-hub.c b/hw/usb-hub.c index b7557cea56..b49a2fe882 100644 --- a/hw/usb-hub.c +++ b/hw/usb-hub.c @@ -138,74 +138,6 @@ static const USBDesc desc_hub = { .str = desc_strings, }; -static const uint8_t qemu_hub_dev_descriptor[] = { - 0x12, /* u8 bLength; */ - 0x01, /* u8 bDescriptorType; Device */ - 0x10, 0x01, /* u16 bcdUSB; v1.1 */ - - 0x09, /* u8 bDeviceClass; HUB_CLASSCODE */ - 0x00, /* u8 bDeviceSubClass; */ - 0x00, /* u8 bDeviceProtocol; [ low/full speeds only ] */ - 0x08, /* u8 bMaxPacketSize0; 8 Bytes */ - - 0x00, 0x00, /* u16 idVendor; */ - 0x00, 0x00, /* u16 idProduct; */ - 0x01, 0x01, /* u16 bcdDevice */ - - 0x03, /* u8 iManufacturer; */ - 0x02, /* u8 iProduct; */ - 0x01, /* u8 iSerialNumber; */ - 0x01 /* u8 bNumConfigurations; */ -}; - -/* XXX: patch interrupt size */ -static const uint8_t qemu_hub_config_descriptor[] = { - - /* one configuration */ - 0x09, /* u8 bLength; */ - 0x02, /* u8 bDescriptorType; Configuration */ - 0x19, 0x00, /* u16 wTotalLength; */ - 0x01, /* u8 bNumInterfaces; (1) */ - 0x01, /* u8 bConfigurationValue; */ - 0x00, /* u8 iConfiguration; */ - 0xe0, /* u8 bmAttributes; - Bit 7: must be set, - 6: Self-powered, - 5: Remote wakeup, - 4..0: resvd */ - 0x00, /* u8 MaxPower; */ - - /* USB 1.1: - * USB 2.0, single TT organization (mandatory): - * one interface, protocol 0 - * - * USB 2.0, multiple TT organization (optional): - * two interfaces, protocols 1 (like single TT) - * and 2 (multiple TT mode) ... config is - * sometimes settable - * NOT IMPLEMENTED - */ - - /* one interface */ - 0x09, /* u8 if_bLength; */ - 0x04, /* u8 if_bDescriptorType; Interface */ - 0x00, /* u8 if_bInterfaceNumber; */ - 0x00, /* u8 if_bAlternateSetting; */ - 0x01, /* u8 if_bNumEndpoints; */ - 0x09, /* u8 if_bInterfaceClass; HUB_CLASSCODE */ - 0x00, /* u8 if_bInterfaceSubClass; */ - 0x00, /* u8 if_bInterfaceProtocol; [usb1.1 or single tt] */ - 0x00, /* u8 if_iInterface; */ - - /* one endpoint (status change endpoint) */ - 0x07, /* u8 ep_bLength; */ - 0x05, /* u8 ep_bDescriptorType; Endpoint */ - 0x81, /* u8 ep_bEndpointAddress; IN Endpoint 1 */ - 0x03, /* u8 ep_bmAttributes; Interrupt */ - 0x02, 0x00, /* u16 ep_wMaxPacketSize; 1 + (MAX_ROOT_PORTS / 8) */ - 0xff /* u8 ep_bInterval; (255ms -- usb 2.0 spec) */ -}; - static const uint8_t qemu_hub_hub_descriptor[] = { 0x00, /* u8 bLength; patched in later */ From 3dc345d5874475a05794e7e1688f133b35384a9a Mon Sep 17 00:00:00 2001 From: Gerd Hoffmann Date: Thu, 7 Jul 2011 15:18:50 +0200 Subject: [PATCH 069/209] usb-ohci: raise interrupt on attach Got lost in commit 618c169b577db64ac6589ad48825d2e11760d1a6, add it back in. Also fix codestyle while we are at it. Signed-off-by: Gerd Hoffmann --- hw/usb-ohci.c | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/hw/usb-ohci.c b/hw/usb-ohci.c index c77a20e4f5..8491d59928 100644 --- a/hw/usb-ohci.c +++ b/hw/usb-ohci.c @@ -327,6 +327,7 @@ static void ohci_attach(USBPort *port1) { OHCIState *s = port1->opaque; OHCIPort *port = &s->rhport[port1->index]; + uint32_t old_state = port->ctrl; /* set connect status */ port->ctrl |= OHCI_PORT_CCS | OHCI_PORT_CSC; @@ -344,6 +345,10 @@ static void ohci_attach(USBPort *port1) } DPRINTF("usb-ohci: Attached port %d\n", port1->index); + + if (old_state != port->ctrl) { + ohci_set_interrupt(s, OHCI_INTR_RHSC); + } } static void ohci_detach(USBPort *port1) @@ -366,8 +371,9 @@ static void ohci_detach(USBPort *port1) } DPRINTF("usb-ohci: Detached port %d\n", port1->index); - if (old_state != port->ctrl) + if (old_state != port->ctrl) { ohci_set_interrupt(s, OHCI_INTR_RHSC); + } } static void ohci_wakeup(USBPort *port1) From 1c1b40c162a6964e1898e84304230a308f4d16c3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A9dric=20VINCENT?= Date: Wed, 29 Jun 2011 12:49:41 +0200 Subject: [PATCH 070/209] arm-semi: Provide access to CLI arguments passed through the "-append" option MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This patch basically adapts the new semi-hosting command-line support -- introduced by Wolfgang Schildbach in the commit 2e8785ac -- for use in system-mode. Note that the "arm_cmdline_len" and "host_cmdline_len" variables were renamed respectively "input_size" and "output_size" because: * in C, the term "length" is generally used to count the number of character in a string, not to count the number of bytes in a buffer (as it is the case here). * in QEMU, the term "host" is used to name variables that are in the host address space, not to name variables in the target address space (as it is the case here). * in the case of this system-call, the terms "input" and "output" fit the semantic of the official ARM semi-hosting specification quite well. I know renaming can be considered harmful but I do think in this case the semantic really matters to keep this code more understandable. Signed-off-by: Cédric VINCENT Reviewed-by: Christophe Lyon Cc: Peter Maydell Cc: Paul Brook Cc: Wolfgang Schildbach Cc: Riku Voipio Signed-off-by: Riku Voipio --- arm-semi.c | 129 +++++++++++++++++++++++++++++++---------------------- 1 file changed, 75 insertions(+), 54 deletions(-) diff --git a/arm-semi.c b/arm-semi.c index 5a62d03b36..873518a20e 100644 --- a/arm-semi.c +++ b/arm-semi.c @@ -34,6 +34,7 @@ #else #include "qemu-common.h" #include "gdbstub.h" +#include "hw/arm-misc.h" #endif #define SYS_OPEN 0x01 @@ -369,68 +370,88 @@ uint32_t do_arm_semihosting(CPUState *env) return syscall_err; #endif case SYS_GET_CMDLINE: -#ifdef CONFIG_USER_ONLY - /* Build a commandline from the original argv. */ { - char *arm_cmdline_buffer; - const char *host_cmdline_buffer; + /* Build a command-line from the original argv. + * + * The inputs are: + * * ARG(0), pointer to a buffer of at least the size + * specified in ARG(1). + * * ARG(1), size of the buffer pointed to by ARG(0) in + * bytes. + * + * The outputs are: + * * ARG(0), pointer to null-terminated string of the + * command line. + * * ARG(1), length of the string pointed to by ARG(0). + */ + char *output_buffer; + size_t input_size = ARG(1); + size_t output_size; + int status = 0; + + /* Compute the size of the output string. */ +#if !defined(CONFIG_USER_ONLY) + output_size = strlen(ts->boot_info->kernel_filename) + + 1 /* Separating space. */ + + strlen(ts->boot_info->kernel_cmdline) + + 1; /* Terminating null byte. */ +#else unsigned int i; - unsigned int arm_cmdline_len = ARG(1); - unsigned int host_cmdline_len = - ts->info->arg_end-ts->info->arg_start; - if (!arm_cmdline_len || host_cmdline_len > arm_cmdline_len) { - return -1; /* not enough space to store command line */ - } - - if (!host_cmdline_len) { + output_size = ts->info->arg_end - ts->info->arg_start; + if (!output_size) { /* We special-case the "empty command line" case (argc==0). Just provide the terminating 0. */ - arm_cmdline_buffer = lock_user(VERIFY_WRITE, ARG(0), 1, 0); - arm_cmdline_buffer[0] = 0; - unlock_user(arm_cmdline_buffer, ARG(0), 1); - - /* Adjust the commandline length argument. */ - SET_ARG(1, 0); - return 0; + output_size = 1; } - - /* lock the buffers on the ARM side */ - arm_cmdline_buffer = - lock_user(VERIFY_WRITE, ARG(0), host_cmdline_len, 0); - host_cmdline_buffer = - lock_user(VERIFY_READ, ts->info->arg_start, - host_cmdline_len, 1); - - if (arm_cmdline_buffer && host_cmdline_buffer) - { - /* the last argument is zero-terminated; - no need for additional termination */ - memcpy(arm_cmdline_buffer, host_cmdline_buffer, - host_cmdline_len); - - /* separate arguments by white spaces */ - for (i = 0; i < host_cmdline_len-1; i++) { - if (arm_cmdline_buffer[i] == 0) { - arm_cmdline_buffer[i] = ' '; - } - } - - /* Adjust the commandline length argument. */ - SET_ARG(1, host_cmdline_len-1); - } - - /* Unlock the buffers on the ARM side. */ - unlock_user(arm_cmdline_buffer, ARG(0), host_cmdline_len); - unlock_user((void*)host_cmdline_buffer, ts->info->arg_start, 0); - - /* Return success if we could return a commandline. */ - return (arm_cmdline_buffer && host_cmdline_buffer) ? 0 : -1; - } -#else - return -1; #endif + + if (output_size > input_size) { + /* Not enough space to store command-line arguments. */ + return -1; + } + + /* Adjust the command-line length. */ + SET_ARG(1, output_size - 1); + + /* Lock the buffer on the ARM side. */ + output_buffer = lock_user(VERIFY_WRITE, ARG(0), output_size, 0); + if (!output_buffer) { + return -1; + } + + /* Copy the command-line arguments. */ +#if !defined(CONFIG_USER_ONLY) + pstrcpy(output_buffer, output_size, ts->boot_info->kernel_filename); + pstrcat(output_buffer, output_size, " "); + pstrcat(output_buffer, output_size, ts->boot_info->kernel_cmdline); +#else + if (output_size == 1) { + /* Empty command-line. */ + output_buffer[0] = '\0'; + goto out; + } + + if (copy_from_user(output_buffer, ts->info->arg_start, + output_size)) { + status = -1; + goto out; + } + + /* Separate arguments by white spaces. */ + for (i = 0; i < output_size - 1; i++) { + if (output_buffer[i] == 0) { + output_buffer[i] = ' '; + } + } + out: +#endif + /* Unlock the buffer on the ARM side. */ + unlock_user(output_buffer, ARG(0), output_size); + + return status; + } case SYS_HEAPINFO: { uint32_t *ptr; From e6fe18fb318cb399623246e2561581442ffc0372 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A9dric=20VINCENT?= Date: Wed, 29 Jun 2011 15:09:09 +0200 Subject: [PATCH 071/209] linux-user: Add support for KD...LED ioctls MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit DirectFB-1.0 uses at least one of the four added ioctls, and the three others were added for completeness. This patch was validated with the program "setleds" and the following Makefile: SETLEDS_INIT = setleds -v -num -caps -scroll SETLEDS_TESTS = sh -c ' \ setleds -v +num +caps +scroll; \ setleds -v -num -caps -scroll; \ setleds -v +num -caps -scroll; \ setleds -v +num +caps -scroll; \ setleds -v +num +caps +scroll; \ setleds -v -num +caps +scroll; \ setleds -v -num -caps +scroll; \ setleds -v -num -caps -scroll' SETLEDS_HOST = setleds SETLEDS_QEMU = "SETLEDS_QEMU not set" .PHONY: setleds_tests setleds_tests: rm -f setleds.host setleds.target $(SETLEDS_INIT:setleds=$(SETLEDS_HOST)) $(SETLEDS_TESTS:setleds=$(SETLEDS_HOST)) >> setleds.host $(SETLEDS_INIT:setleds=$(SETLEDS_QEMU)) $(SETLEDS_TESTS:setleds=$(SETLEDS_QEMU)) >> setleds.target cmp setleds.host setleds.target Signed-off-by: Cédric VINCENT Cc: Riku Voipio Signed-off-by: Riku Voipio --- linux-user/ioctls.h | 4 ++++ linux-user/syscall_defs.h | 4 ++++ 2 files changed, 8 insertions(+) diff --git a/linux-user/ioctls.h b/linux-user/ioctls.h index 42b3ae3725..68418e425c 100644 --- a/linux-user/ioctls.h +++ b/linux-user/ioctls.h @@ -59,6 +59,10 @@ IOCTL(KDSKBMODE, 0, TYPE_INT) IOCTL(KDGKBENT, IOC_RW, MK_PTR(MK_STRUCT(STRUCT_kbentry))) IOCTL(KDGKBSENT, IOC_RW, MK_PTR(MK_STRUCT(STRUCT_kbsentry))) + IOCTL(KDGKBLED, 0, TYPE_INT) + IOCTL(KDSKBLED, 0, TYPE_INT) + IOCTL(KDGETLED, 0, TYPE_INT) + IOCTL(KDSETLED, 0, TYPE_INT) IOCTL(BLKROSET, IOC_W, MK_PTR(TYPE_INT)) IOCTL(BLKROGET, IOC_R, MK_PTR(TYPE_INT)) diff --git a/linux-user/syscall_defs.h b/linux-user/syscall_defs.h index 04c268de7c..2b74547885 100644 --- a/linux-user/syscall_defs.h +++ b/linux-user/syscall_defs.h @@ -708,6 +708,10 @@ struct target_pollfd { #define TARGET_KDSKBMODE 0x4b45 #define TARGET_KDGKBENT 0x4B46 /* gets one entry in translation table */ #define TARGET_KDGKBSENT 0x4B48 /* gets one function key string entry */ +#define TARGET_KDGKBLED 0x4B64 /* get led flags (not lights) */ +#define TARGET_KDSKBLED 0x4B65 /* set led flags (not lights) */ +#define TARGET_KDGETLED 0x4B31 /* return current led state */ +#define TARGET_KDSETLED 0x4B32 /* set led state [lights, not flags] */ #define TARGET_SIOCATMARK 0x8905 From 774750c088192112df1623610dc35d9e03983d49 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A9dric=20VINCENT?= Date: Wed, 29 Jun 2011 15:09:10 +0200 Subject: [PATCH 072/209] linux-user: Add support for more VT ioctls MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit DirectFB-1.0 uses at least two of the four added ioctls, and the two others were added for completeness. This patch was validated with the program "vlock -all/-new". Signed-off-by: Cédric VINCENT Cc: Riku Voipio Signed-off-by: Riku Voipio --- linux-user/ioctls.h | 4 ++++ linux-user/syscall_defs.h | 4 ++++ linux-user/syscall_types.h | 7 +++++++ 3 files changed, 15 insertions(+) diff --git a/linux-user/ioctls.h b/linux-user/ioctls.h index 68418e425c..7bc1c48207 100644 --- a/linux-user/ioctls.h +++ b/linux-user/ioctls.h @@ -336,3 +336,7 @@ IOCTL(VT_WAITACTIVE, 0, TYPE_INT) IOCTL(VT_LOCKSWITCH, 0, TYPE_INT) IOCTL(VT_UNLOCKSWITCH, 0, TYPE_INT) + IOCTL(VT_GETMODE, IOC_RW, MK_PTR(MK_STRUCT(STRUCT_vt_mode))) + IOCTL(VT_SETMODE, IOC_RW, MK_PTR(MK_STRUCT(STRUCT_vt_mode))) + IOCTL(VT_RELDISP, 0, TYPE_INT) + IOCTL(VT_DISALLOCATE, 0, TYPE_INT) diff --git a/linux-user/syscall_defs.h b/linux-user/syscall_defs.h index 2b74547885..4a59b36d68 100644 --- a/linux-user/syscall_defs.h +++ b/linux-user/syscall_defs.h @@ -940,6 +940,10 @@ struct target_pollfd { #define TARGET_VT_WAITACTIVE 0x5607 #define TARGET_VT_LOCKSWITCH 0x560b #define TARGET_VT_UNLOCKSWITCH 0x560c +#define TARGET_VT_GETMODE 0x5601 +#define TARGET_VT_SETMODE 0x5602 +#define TARGET_VT_RELDISP 0x5605 +#define TARGET_VT_DISALLOCATE 0x5608 /* from asm/termbits.h */ diff --git a/linux-user/syscall_types.h b/linux-user/syscall_types.h index 0e67cd8f30..94b0ce0f56 100644 --- a/linux-user/syscall_types.h +++ b/linux-user/syscall_types.h @@ -166,6 +166,13 @@ STRUCT(vt_stat, TYPE_SHORT, /* v_signal */ TYPE_SHORT) /* v_state */ +STRUCT(vt_mode, + TYPE_CHAR, /* mode */ + TYPE_CHAR, /* waitv */ + TYPE_SHORT, /* relsig */ + TYPE_SHORT, /* acqsig */ + TYPE_SHORT) /* frsig */ + STRUCT(fiemap_extent, TYPE_ULONGLONG, /* fe_logical */ TYPE_ULONGLONG, /* fe_physical */ From 12b81b7145ec28a3c608d1eee5abcd5cdd6a677b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A9dric=20VINCENT?= Date: Wed, 29 Jun 2011 15:09:11 +0200 Subject: [PATCH 073/209] linux-user: Add support for even more FB ioctls MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This patch was validated with programs from DirectFB-1.0 and WebKit/DirectFB. Signed-off-by: Cédric VINCENT Cc: Riku Voipio Signed-off-by: Riku Voipio --- linux-user/ioctls.h | 5 +++++ linux-user/syscall_defs.h | 5 +++++ linux-user/syscall_types.h | 13 +++++++++++++ 3 files changed, 23 insertions(+) diff --git a/linux-user/ioctls.h b/linux-user/ioctls.h index 7bc1c48207..6514502dc4 100644 --- a/linux-user/ioctls.h +++ b/linux-user/ioctls.h @@ -329,6 +329,11 @@ IOCTL(FBIOGET_FSCREENINFO, IOC_R, MK_PTR(MK_STRUCT(STRUCT_fb_fix_screeninfo))) IOCTL(FBIOGET_VSCREENINFO, IOC_R, MK_PTR(MK_STRUCT(STRUCT_fb_var_screeninfo))) IOCTL(FBIOPUT_VSCREENINFO, IOC_W, MK_PTR(MK_STRUCT(STRUCT_fb_var_screeninfo))) + IOCTL(FBIOGETCMAP, IOC_RW, MK_PTR(MK_STRUCT(STRUCT_fb_cmap))) + IOCTL(FBIOPUTCMAP, IOC_RW, MK_PTR(MK_STRUCT(STRUCT_fb_cmap))) + IOCTL(FBIOPAN_DISPLAY, IOC_RW, MK_PTR(MK_STRUCT(STRUCT_fb_var_screeninfo))) + IOCTL(FBIOGET_CON2FBMAP, IOC_RW, MK_PTR(MK_STRUCT(STRUCT_fb_con2fbmap))) + IOCTL(FBIOPUT_CON2FBMAP, IOC_RW, MK_PTR(MK_STRUCT(STRUCT_fb_con2fbmap))) IOCTL(VT_OPENQRY, IOC_R, MK_PTR(TYPE_INT)) IOCTL(VT_GETSTATE, IOC_R, MK_PTR(MK_STRUCT(STRUCT_vt_stat))) diff --git a/linux-user/syscall_defs.h b/linux-user/syscall_defs.h index 4a59b36d68..1b734515bf 100644 --- a/linux-user/syscall_defs.h +++ b/linux-user/syscall_defs.h @@ -932,6 +932,11 @@ struct target_pollfd { #define TARGET_FBIOGET_VSCREENINFO 0x4600 #define TARGET_FBIOPUT_VSCREENINFO 0x4601 #define TARGET_FBIOGET_FSCREENINFO 0x4602 +#define TARGET_FBIOGETCMAP 0x4604 +#define TARGET_FBIOPUTCMAP 0x4605 +#define TARGET_FBIOPAN_DISPLAY 0x4606 +#define TARGET_FBIOGET_CON2FBMAP 0x460F +#define TARGET_FBIOPUT_CON2FBMAP 0x4610 /* vt ioctls */ #define TARGET_VT_OPENQRY 0x5600 diff --git a/linux-user/syscall_types.h b/linux-user/syscall_types.h index 94b0ce0f56..c370125170 100644 --- a/linux-user/syscall_types.h +++ b/linux-user/syscall_types.h @@ -161,6 +161,19 @@ STRUCT(fb_var_screeninfo, TYPE_INT, /* rotate */ MK_ARRAY(TYPE_INT, 5)) /* reserved */ +STRUCT(fb_cmap, + TYPE_INT, /* start */ + TYPE_INT, /* len */ + TYPE_PTRVOID, /* red */ + TYPE_PTRVOID, /* green */ + TYPE_PTRVOID, /* blue */ + TYPE_PTRVOID) /* transp */ + +STRUCT(fb_con2fbmap, + TYPE_INT, /* console */ + TYPE_INT) /* framebuffer */ + + STRUCT(vt_stat, TYPE_SHORT, /* v_active */ TYPE_SHORT, /* v_signal */ From d979e8eb544da31df78bc76358a73f0d1c823c17 Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Mon, 27 Jun 2011 17:44:51 +0100 Subject: [PATCH 074/209] linux-user: Add syscall numbers from kernel 2.6.39.2 Add syscall numbers for new syscall numbers; this brings us into line with Linux 2.6.39.2. Signed-off-by: Peter Maydell Signed-off-by: Riku Voipio --- linux-user/alpha/syscall_nr.h | 23 ++++++++++++++++++++- linux-user/arm/syscall_nr.h | 13 ++++++++++++ linux-user/cris/syscall_nr.h | 2 ++ linux-user/i386/syscall_nr.h | 12 +++++++++++ linux-user/m68k/syscall_nr.h | 16 +++++++++++++++ linux-user/main.c | 27 +++++++++++++++++++++++++ linux-user/microblaze/syscall_nr.h | 14 +++++++++++-- linux-user/mips/syscall_nr.h | 13 ++++++++++++ linux-user/mips64/syscall_nr.h | 13 ++++++++++++ linux-user/mipsn32/syscall_nr.h | 14 +++++++++++++ linux-user/ppc/syscall_nr.h | 30 ++++++++++++++++++++++++++++ linux-user/s390x/syscall_nr.h | 13 ++++++++++-- linux-user/sh4/syscall_nr.h | 32 ++++++++++++++++++++++++++++++ linux-user/sparc/syscall_nr.h | 12 +++++++++++ linux-user/sparc64/syscall_nr.h | 12 +++++++++++ linux-user/x86_64/syscall_nr.h | 12 +++++++++++ 16 files changed, 253 insertions(+), 5 deletions(-) diff --git a/linux-user/alpha/syscall_nr.h b/linux-user/alpha/syscall_nr.h index e3127df4ac..f6284db22f 100644 --- a/linux-user/alpha/syscall_nr.h +++ b/linux-user/alpha/syscall_nr.h @@ -411,4 +411,25 @@ #define TARGET_NR_signalfd 476 #define TARGET_NR_timerfd 477 #define TARGET_NR_eventfd 478 - +#define TARGET_NR_recvmmsg 479 +#define TARGET_NR_fallocate 480 +#define TARGET_NR_timerfd_create 481 +#define TARGET_NR_timerfd_settime 482 +#define TARGET_NR_timerfd_gettime 483 +#define TARGET_NR_signalfd4 484 +#define TARGET_NR_eventfd2 485 +#define TARGET_NR_epoll_create1 486 +#define TARGET_NR_dup3 487 +#define TARGET_NR_pipe2 488 +#define TARGET_NR_inotify_init1 489 +#define TARGET_NR_preadv 490 +#define TARGET_NR_pwritev 491 +#define TARGET_NR_rt_tgsigqueueinfo 492 +#define TARGET_NR_perf_event_open 493 +#define TARGET_NR_fanotify_init 494 +#define TARGET_NR_fanotify_mark 495 +#define TARGET_NR_prlimit64 496 +#define TARGET_NR_name_to_handle_at 497 +#define TARGET_NR_open_by_handle_at 498 +#define TARGET_NR_clock_adjtime 499 +#define TARGET_NR_syncfs 500 diff --git a/linux-user/arm/syscall_nr.h b/linux-user/arm/syscall_nr.h index 79a216a137..7f05879ea3 100644 --- a/linux-user/arm/syscall_nr.h +++ b/linux-user/arm/syscall_nr.h @@ -365,3 +365,16 @@ #define TARGET_NR_dup3 (358) #define TARGET_NR_pipe2 (359) #define TARGET_NR_inotify_init1 (360) +#define TARGET_NR_preadv (361) +#define TARGET_NR_pwritev (362) +#define TARGET_NR_rt_tgsigqueueinfo (363) +#define TARGET_NR_perf_event_open (364) +#define TARGET_NR_recvmmsg (365) +#define TARGET_NR_accept4 (366) +#define TARGET_NR_fanotify_init (367) +#define TARGET_NR_fanotify_mark (368) +#define TARGET_NR_prlimit64 (369) +#define TARGET_NR_name_to_handle_at (370) +#define TARGET_NR_open_by_handle_at (371) +#define TARGET_NR_clock_adjtime (372) +#define TARGET_NR_syncfs (373) diff --git a/linux-user/cris/syscall_nr.h b/linux-user/cris/syscall_nr.h index 6132817105..98f1a0b415 100644 --- a/linux-user/cris/syscall_nr.h +++ b/linux-user/cris/syscall_nr.h @@ -333,3 +333,5 @@ #define TARGET_NR_dup3 330 #define TARGET_NR_pipe2 331 #define TARGET_NR_inotify_init1 332 +#define TARGET_NR_preadv 333 +#define TARGET_NR_pwritev 334 diff --git a/linux-user/i386/syscall_nr.h b/linux-user/i386/syscall_nr.h index 3ef71ce004..74abfcacb4 100644 --- a/linux-user/i386/syscall_nr.h +++ b/linux-user/i386/syscall_nr.h @@ -335,3 +335,15 @@ #define TARGET_NR_dup3 330 #define TARGET_NR_pipe2 331 #define TARGET_NR_inotify_init1 332 +#define TARGET_NR_preadv 333 +#define TARGET_NR_pwritev 334 +#define TARGET_NR_rt_tgsigqueueinfo 335 +#define TARGET_NR_perf_event_open 336 +#define TARGET_NR_recvmmsg 337 +#define TARGET_NR_fanotify_init 338 +#define TARGET_NR_fanotify_mark 339 +#define TARGET_NR_prlimit64 340 +#define TARGET_NR_name_to_handle_at 341 +#define TARGET_NR_open_by_handle_at 342 +#define TARGET_NR_clock_adjtime 343 +#define TARGET_NR_syncfs 344 diff --git a/linux-user/m68k/syscall_nr.h b/linux-user/m68k/syscall_nr.h index 1c0ba07bfb..4d0937e505 100644 --- a/linux-user/m68k/syscall_nr.h +++ b/linux-user/m68k/syscall_nr.h @@ -328,3 +328,19 @@ #define TARGET_NR_dup3 326 #define TARGET_NR_pipe2 327 #define TARGET_NR_inotify_init1 328 +#define TARGET_NR_inotify_init1 328 +#define TARGET_NR_preadv 329 +#define TARGET_NR_pwritev 330 +#define TARGET_NR_rt_tgsigqueueinfo 331 +#define TARGET_NR_perf_event_open 332 +#define TARGET_NR_get_thread_area 333 +#define TARGET_NR_set_thread_area 334 +#define TARGET_NR_atomic_cmpxchg_32 335 +#define TARGET_NR_atomic_barrier 336 +#define TARGET_NR_fanotify_init 337 +#define TARGET_NR_fanotify_mark 338 +#define TARGET_NR_prlimit64 339 +#define TARGET_NR_name_to_handle_at 340 +#define TARGET_NR_open_by_handle_at 341 +#define TARGET_NR_clock_adjtime 342 +#define TARGET_NR_syncfs 343 diff --git a/linux-user/main.c b/linux-user/main.c index 289054b0b7..48f0443191 100644 --- a/linux-user/main.c +++ b/linux-user/main.c @@ -1985,6 +1985,33 @@ static const uint8_t mips_syscall_args[] = { MIPS_SYS(sys_epoll_pwait, 6) MIPS_SYS(sys_ioprio_set, 3) MIPS_SYS(sys_ioprio_get, 2) + MIPS_SYS(sys_utimensat, 4) + MIPS_SYS(sys_signalfd, 3) + MIPS_SYS(sys_ni_syscall, 0) /* was timerfd */ + MIPS_SYS(sys_eventfd, 1) + MIPS_SYS(sys_fallocate, 6) /* 4320 */ + MIPS_SYS(sys_timerfd_create, 2) + MIPS_SYS(sys_timerfd_gettime, 2) + MIPS_SYS(sys_timerfd_settime, 4) + MIPS_SYS(sys_signalfd4, 4) + MIPS_SYS(sys_eventfd2, 2) /* 4325 */ + MIPS_SYS(sys_epoll_create1, 1) + MIPS_SYS(sys_dup3, 3) + MIPS_SYS(sys_pipe2, 2) + MIPS_SYS(sys_inotify_init1, 1) + MIPS_SYS(sys_preadv, 6) /* 4330 */ + MIPS_SYS(sys_pwritev, 6) + MIPS_SYS(sys_rt_tgsigqueueinfo, 4) + MIPS_SYS(sys_perf_event_open, 5) + MIPS_SYS(sys_accept4, 4) + MIPS_SYS(sys_recvmmsg, 5) /* 4335 */ + MIPS_SYS(sys_fanotify_init, 2) + MIPS_SYS(sys_fanotify_mark, 6) + MIPS_SYS(sys_prlimit64, 4) + MIPS_SYS(sys_name_to_handle_at, 5) + MIPS_SYS(sys_open_by_handle_at, 3) /* 4340 */ + MIPS_SYS(sys_clock_adjtime, 2) + MIPS_SYS(sys_syncfs, 1) }; #undef MIPS_SYS diff --git a/linux-user/microblaze/syscall_nr.h b/linux-user/microblaze/syscall_nr.h index 3e641cdb4d..f1fe0e7d8f 100644 --- a/linux-user/microblaze/syscall_nr.h +++ b/linux-user/microblaze/syscall_nr.h @@ -364,6 +364,16 @@ #define TARGET_NR_sendmsg 360 /* new */ #define TARGET_NR_recvmsg 361 /* new */ #define TARGET_NR_accept04 362 /* new */ - -#define TARGET_NR_syscalls 363 +#define TARGET_NR_preadv 363 /* new */ +#define TARGET_NR_pwritev 364 /* new */ +#define TARGET_NR_rt_tgsigqueueinfo 365 /* new */ +#define TARGET_NR_perf_event_open 366 /* new */ +#define TARGET_NR_recvmmsg 367 /* new */ +#define TARGET_NR_fanotify_init 368 +#define TARGET_NR_fanotify_mark 369 +#define TARGET_NR_prlimit64 370 +#define TARGET_NR_name_to_handle_at 371 +#define TARGET_NR_open_by_handle_at 372 +#define TARGET_NR_clock_adjtime 373 +#define TARGET_NR_syncfs 374 diff --git a/linux-user/mips/syscall_nr.h b/linux-user/mips/syscall_nr.h index 059530801b..fbdc348ffc 100644 --- a/linux-user/mips/syscall_nr.h +++ b/linux-user/mips/syscall_nr.h @@ -332,3 +332,16 @@ #define TARGET_NR_dup3 (TARGET_NR_Linux + 327) #define TARGET_NR_pipe2 (TARGET_NR_Linux + 328) #define TARGET_NR_inotify_init1 (TARGET_NR_Linux + 329) +#define TARGET_NR_preadv (TARGET_NR_Linux + 330) +#define TARGET_NR_pwritev (TARGET_NR_Linux + 331) +#define TARGET_NR_rt_tgsigqueueinfo (TARGET_NR_Linux + 332) +#define TARGET_NR_perf_event_open (TARGET_NR_Linux + 333) +#define TARGET_NR_accept4 (TARGET_NR_Linux + 334) +#define TARGET_NR_recvmmsg (TARGET_NR_Linux + 335) +#define TARGET_NR_fanotify_init (TARGET_NR_Linux + 336) +#define TARGET_NR_fanotify_mark (TARGET_NR_Linux + 337) +#define TARGET_NR_prlimit64 (TARGET_NR_Linux + 338) +#define TARGET_NR_name_to_handle_at (TARGET_NR_Linux + 339) +#define TARGET_NR_open_by_handle_at (TARGET_NR_Linux + 340) +#define TARGET_NR_clock_adjtime (TARGET_NR_Linux + 341) +#define TARGET_NR_syncfs (TARGET_NR_Linux + 342) diff --git a/linux-user/mips64/syscall_nr.h b/linux-user/mips64/syscall_nr.h index ee1d134146..36d27b5159 100644 --- a/linux-user/mips64/syscall_nr.h +++ b/linux-user/mips64/syscall_nr.h @@ -291,3 +291,16 @@ #define TARGET_NR_dup3 (TARGET_NR_Linux + 286) #define TARGET_NR_pipe2 (TARGET_NR_Linux + 287) #define TARGET_NR_inotify_init1 (TARGET_NR_Linux + 288) +#define TARGET_NR_preadv (TARGET_NR_Linux + 289) +#define TARGET_NR_pwritev (TARGET_NR_Linux + 290) +#define TARGET_NR_rt_tgsigqueueinfo (TARGET_NR_Linux + 291) +#define TARGET_NR_perf_event_open (TARGET_NR_Linux + 292) +#define TARGET_NR_accept4 (TARGET_NR_Linux + 293) +#define TARGET_NR_recvmmsg (TARGET_NR_Linux + 294) +#define TARGET_NR_fanotify_init (TARGET_NR_Linux + 295) +#define TARGET_NR_fanotify_mark (TARGET_NR_Linux + 296) +#define TARGET_NR_prlimit64 (TARGET_NR_Linux + 297) +#define TARGET_NR_name_to_handle_at (TARGET_NR_Linux + 298) +#define TARGET_NR_open_by_handle_at (TARGET_NR_Linux + 299) +#define TARGET_NR_clock_adjtime (TARGET_NR_Linux + 300) +#define TARGET_NR_syncfs (TARGET_NR_Linux + 301) diff --git a/linux-user/mipsn32/syscall_nr.h b/linux-user/mipsn32/syscall_nr.h index 60a99ddf6e..4e1aca3a9b 100644 --- a/linux-user/mipsn32/syscall_nr.h +++ b/linux-user/mipsn32/syscall_nr.h @@ -295,3 +295,17 @@ #define TARGET_NR_dup3 (TARGET_NR_Linux + 290) #define TARGET_NR_pipe2 (TARGET_NR_Linux + 291) #define TARGET_NR_inotify_init1 (TARGET_NR_Linux + 292) +#define TARGET_NR_preadv (TARGET_NR_Linux + 293) +#define TARGET_NR_pwritev (TARGET_NR_Linux + 294) +#define TARGET_NR_rt_tgsigqueueinfo (TARGET_NR_Linux + 295) +#define TARGET_NR_perf_event_open (TARGET_NR_Linux + 296) +#define TARGET_NR_accept4 (TARGET_NR_Linux + 297) +#define TARGET_NR_recvmmsg (TARGET_NR_Linux + 298) +#define TARGET_NR_getdents64 (TARGET_NR_Linux + 299) +#define TARGET_NR_fanotify_init (TARGET_NR_Linux + 300) +#define TARGET_NR_fanotify_mark (TARGET_NR_Linux + 301) +#define TARGET_NR_prlimit64 (TARGET_NR_Linux + 302) +#define TARGET_NR_name_to_handle_at (TARGET_NR_Linux + 303) +#define TARGET_NR_open_by_handle_at (TARGET_NR_Linux + 304) +#define TARGET_NR_clock_adjtime (TARGET_NR_Linux + 305) +#define TARGET_NR_syncfs (TARGET_NR_Linux + 306) diff --git a/linux-user/ppc/syscall_nr.h b/linux-user/ppc/syscall_nr.h index cc84a4c04d..0673b7d169 100644 --- a/linux-user/ppc/syscall_nr.h +++ b/linux-user/ppc/syscall_nr.h @@ -332,3 +332,33 @@ #define TARGET_NR_dup3 316 #define TARGET_NR_pipe2 317 #define TARGET_NR_inotify_init1 318 +#define TARGET_NR_perf_event_open 319 +#define TARGET_NR_preadv 320 +#define TARGET_NR_pwritev 321 +#define TARGET_NR_rt_tgsigqueueinfo 322 +#define TARGET_NR_fanotify_init 323 +#define TARGET_NR_fanotify_mark 324 +#define TARGET_NR_prlimit64 325 +#define TARGET_NR_socket 326 +#define TARGET_NR_bind 327 +#define TARGET_NR_connect 328 +#define TARGET_NR_listen 329 +#define TARGET_NR_accept 330 +#define TARGET_NR_getsockname 331 +#define TARGET_NR_getpeername 332 +#define TARGET_NR_socketpair 333 +#define TARGET_NR_send 334 +#define TARGET_NR_sendto 335 +#define TARGET_NR_recv 336 +#define TARGET_NR_recvfrom 337 +#define TARGET_NR_shutdown 338 +#define TARGET_NR_setsockopt 339 +#define TARGET_NR_getsockopt 340 +#define TARGET_NR_sendmsg 341 +#define TARGET_NR_recvmsg 342 +#define TARGET_NR_recvmmsg 343 +#define TARGET_NR_accept4 344 +#define TARGET_NR_name_to_handle_at 345 +#define TARGET_NR_open_by_handle_at 346 +#define TARGET_NR_clock_adjtime 347 +#define TARGET_NR_syncfs 348 diff --git a/linux-user/s390x/syscall_nr.h b/linux-user/s390x/syscall_nr.h index 7cc6db2e1b..d4529ac03c 100644 --- a/linux-user/s390x/syscall_nr.h +++ b/linux-user/s390x/syscall_nr.h @@ -254,8 +254,17 @@ #define TARGET_NR_pipe2 325 #define TARGET_NR_dup3 326 #define TARGET_NR_epoll_create1 327 -#undef NR_syscalls -#define NR_syscalls 328 +#define TARGET_NR_preadv 328 +#define TARGET_NR_pwritev 329 +#define TARGET_NR_rt_tgsigqueueinfo 330 +#define TARGET_NR_perf_event_open 331 +#define TARGET_NR_fanotify_init 332 +#define TARGET_NR_fanotify_mark 333 +#define TARGET_NR_prlimit64 334 +#define TARGET_NR_name_to_handle_at 335 +#define TARGET_NR_open_by_handle_at 336 +#define TARGET_NR_clock_adjtime 337 +#define TARGET_NR_syncfs 338 /* * There are some system calls that are not present on 64 bit, some diff --git a/linux-user/sh4/syscall_nr.h b/linux-user/sh4/syscall_nr.h index 262b236333..6173a7c059 100644 --- a/linux-user/sh4/syscall_nr.h +++ b/linux-user/sh4/syscall_nr.h @@ -334,3 +334,35 @@ #define TARGET_NR_dup3 330 #define TARGET_NR_pipe2 331 #define TARGET_NR_inotify_init1 332 +#define TARGET_NR_preadv 333 +#define TARGET_NR_pwritev 334 +#define TARGET_NR_rt_tgsigqueueinfo 335 +#define TARGET_NR_perf_event_open 336 +#define TARGET_NR_fanotify_init 337 +#define TARGET_NR_fanotify_mark 338 +#define TARGET_NR_prlimit64 339 + +/* Non-multiplexed socket family */ +#define TARGET_NR_socket 340 +#define TARGET_NR_bind 341 +#define TARGET_NR_connect 342 +#define TARGET_NR_listen 343 +#define TARGET_NR_accept 344 +#define TARGET_NR_getsockname 345 +#define TARGET_NR_getpeername 346 +#define TARGET_NR_socketpair 347 +#define TARGET_NR_send 348 +#define TARGET_NR_sendto 349 +#define TARGET_NR_recv 350 +#define TARGET_NR_recvfrom 351 +#define TARGET_NR_shutdown 352 +#define TARGET_NR_setsockopt 353 +#define TARGET_NR_getsockopt 354 +#define TARGET_NR_sendmsg 355 +#define TARGET_NR_recvmsg 356 +#define TARGET_NR_recvmmsg 357 +#define TARGET_NR_accept4 358 +#define TARGET_NR_name_to_handle_at 359 +#define TARGET_NR_open_by_handle_at 360 +#define TARGET_NR_clock_adjtime 361 +#define TARGET_NR_syncfs 362 diff --git a/linux-user/sparc/syscall_nr.h b/linux-user/sparc/syscall_nr.h index 5d1ac21ac9..be503f23dd 100644 --- a/linux-user/sparc/syscall_nr.h +++ b/linux-user/sparc/syscall_nr.h @@ -285,3 +285,15 @@ #define TARGET_NR_pipe2 321 #define TARGET_NR_inotify_init1 322 #define TARGET_NR_accept4 323 +#define TARGET_NR_preadv 324 +#define TARGET_NR_pwritev 325 +#define TARGET_NR_rt_tgsigqueueinfo 326 +#define TARGET_NR_perf_event_open 327 +#define TARGET_NR_recvmmsg 328 +#define TARGET_NR_fanotify_init 329 +#define TARGET_NR_fanotify_mark 330 +#define TARGET_NR_prlimit64 331 +#define TARGET_NR_name_to_handle_at 332 +#define TARGET_NR_open_by_handle_at 333 +#define TARGET_NR_clock_adjtime 334 +#define TARGET_NR_syncfs 335 diff --git a/linux-user/sparc64/syscall_nr.h b/linux-user/sparc64/syscall_nr.h index bdca2a7331..70988b2ec9 100644 --- a/linux-user/sparc64/syscall_nr.h +++ b/linux-user/sparc64/syscall_nr.h @@ -322,3 +322,15 @@ #define TARGET_NR_pipe2 321 #define TARGET_NR_inotify_init1 322 #define TARGET_NR_accept4 323 +#define TARGET_NR_preadv 324 +#define TARGET_NR_pwritev 325 +#define TARGET_NR_rt_tgsigqueueinfo 326 +#define TARGET_NR_perf_event_open 327 +#define TARGET_NR_recvmmsg 328 +#define TARGET_NR_fanotify_init 329 +#define TARGET_NR_fanotify_mark 330 +#define TARGET_NR_prlimit64 331 +#define TARGET_NR_name_to_handle_at 332 +#define TARGET_NR_open_by_handle_at 333 +#define TARGET_NR_clock_adjtime 334 +#define TARGET_NR_syncfs 335 diff --git a/linux-user/x86_64/syscall_nr.h b/linux-user/x86_64/syscall_nr.h index 568a901d71..947e961ce4 100644 --- a/linux-user/x86_64/syscall_nr.h +++ b/linux-user/x86_64/syscall_nr.h @@ -293,3 +293,15 @@ #define TARGET_NR_dup3 292 #define TARGET_NR_pipe2 293 #define TARGET_NR_inotify_init1 294 +#define TARGET_NR_preadv 295 +#define TARGET_NR_pwritev 296 +#define TARGET_NR_rt_tgsigqueueinfo 297 +#define TARGET_NR_perf_event_open 298 +#define TARGET_NR_recvmmsg 299 +#define TARGET_NR_fanotify_init 300 +#define TARGET_NR_fanotify_mark 301 +#define TARGET_NR_prlimit64 302 +#define TARGET_NR_name_to_handle_at 303 +#define TARGET_NR_open_by_handle_at 304 +#define TARGET_NR_clock_adjtime 305 +#define TARGET_NR_syncfs 306 From 163a05a8398bc4b56c7498fa9901422a159168bf Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Mon, 27 Jun 2011 17:44:52 +0100 Subject: [PATCH 075/209] linux-user: Implement prlimit64 syscall Implement the prlimit64 syscall. Slightly modified to apply upstream -Riku Signed-off-by: Peter Maydell Signed-off-by: Riku Voipio --- linux-user/syscall.c | 43 +++++++++++++++++++++++++++++++++++++++ linux-user/syscall_defs.h | 4 ++++ 2 files changed, 47 insertions(+) diff --git a/linux-user/syscall.c b/linux-user/syscall.c index fed7a8fe0f..e2f356bb96 100644 --- a/linux-user/syscall.c +++ b/linux-user/syscall.c @@ -559,6 +559,21 @@ _syscall6(int, sys_pselect6, int, nfds, fd_set *, readfds, fd_set *, writefds, fd_set *, exceptfds, struct timespec *, timeout, void *, sig); #endif +#if defined(TARGET_NR_prlimit64) +#ifndef __NR_prlimit64 +# define __NR_prlimit64 -1 +#endif +#define __NR_sys_prlimit64 __NR_prlimit64 +/* The glibc rlimit structure may not be that used by the underlying syscall */ +struct host_rlimit64 { + uint64_t rlim_cur; + uint64_t rlim_max; +}; +_syscall4(int, sys_prlimit64, pid_t, pid, int, resource, + const struct host_rlimit64 *, new_limit, + struct host_rlimit64 *, old_limit) +#endif + extern int personality(int); extern int flock(int, int); extern int setfsuid(int); @@ -7989,6 +8004,34 @@ abi_long do_syscall(void *cpu_env, int num, abi_long arg1, break; } #endif +#endif +#ifdef TARGET_NR_prlimit64 + case TARGET_NR_prlimit64: + { + /* args: pid, resource number, ptr to new rlimit, ptr to old rlimit */ + struct target_rlimit64 *target_rnew, *target_rold; + struct host_rlimit64 rnew, rold, *rnewp = 0; + if (arg3) { + if (!lock_user_struct(VERIFY_READ, target_rnew, arg3, 1)) { + goto efault; + } + rnew.rlim_cur = tswap64(target_rnew->rlim_cur); + rnew.rlim_max = tswap64(target_rnew->rlim_max); + unlock_user_struct(target_rnew, arg3, 0); + rnewp = &rnew; + } + + ret = get_errno(sys_prlimit64(arg1, arg2, rnewp, arg4 ? &rold : 0)); + if (!is_error(ret) && arg4) { + if (!lock_user_struct(VERIFY_WRITE, target_rold, arg4, 1)) { + goto efault; + } + target_rold->rlim_cur = tswap64(rold.rlim_cur); + target_rold->rlim_max = tswap64(rold.rlim_max); + unlock_user_struct(target_rold, arg4, 1); + } + break; + } #endif default: unimplemented: diff --git a/linux-user/syscall_defs.h b/linux-user/syscall_defs.h index 1b734515bf..1fdc84d3e7 100644 --- a/linux-user/syscall_defs.h +++ b/linux-user/syscall_defs.h @@ -2293,3 +2293,7 @@ struct target_epoll_event { target_epoll_data_t data; }; #endif +struct target_rlimit64 { + uint64_t rlim_cur; + uint64_t rlim_max; +}; From 8f04eeb3c094bf370f131e50b7e3da85d08d518f Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Tue, 28 Jun 2011 12:21:57 +0100 Subject: [PATCH 076/209] linux-user/syscall.c: Enforce pselect6 sigset size restrictions Enforce the same restriction on the size of the sigset passed to pselect6 as the Linux kernel does. This is both correct and silences a gcc 4.6 warning about a write-only variable. Signed-off-by: Peter Maydell Signed-off-by: Riku Voipio --- linux-user/syscall.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/linux-user/syscall.c b/linux-user/syscall.c index e2f356bb96..90f6789e5f 100644 --- a/linux-user/syscall.c +++ b/linux-user/syscall.c @@ -5699,6 +5699,11 @@ abi_long do_syscall(void *cpu_env, int num, abi_long arg1, if (arg_sigset) { sig.set = &set; + if (arg_sigsize != sizeof(*target_sigset)) { + /* Like the kernel, we enforce correct size sigsets */ + ret = -TARGET_EINVAL; + goto fail; + } target_sigset = lock_user(VERIFY_READ, arg_sigset, sizeof(*target_sigset), 1); if (!target_sigset) { From 053ebb2726c17af6133cfcc8de2193121a107eb5 Mon Sep 17 00:00:00 2001 From: "Wesley W. Terpstra" Date: Tue, 12 Jul 2011 14:32:31 +0300 Subject: [PATCH 077/209] mips: sigaltstack args The syscall sigaltstack takes two parameters, not zero. This patch should have no impact as only values above 4 influence the runtime behaviour. Nevertheless, it is wrong. Signed-off-by: Wesley W. Terpstra Signed-off-by: Riku Voipio --- linux-user/main.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/linux-user/main.c b/linux-user/main.c index 48f0443191..d695610349 100644 --- a/linux-user/main.c +++ b/linux-user/main.c @@ -1875,7 +1875,7 @@ static const uint8_t mips_syscall_args[] = { MIPS_SYS(sys_getcwd , 2) MIPS_SYS(sys_capget , 2) MIPS_SYS(sys_capset , 2) /* 4205 */ - MIPS_SYS(sys_sigaltstack , 0) + MIPS_SYS(sys_sigaltstack , 2) MIPS_SYS(sys_sendfile , 4) MIPS_SYS(sys_ni_syscall , 0) MIPS_SYS(sys_ni_syscall , 0) From 7c2f6157d833cb95b355c7321597bc5a5ac976e4 Mon Sep 17 00:00:00 2001 From: "Wesley W. Terpstra" Date: Tue, 12 Jul 2011 14:33:23 +0300 Subject: [PATCH 078/209] mips: missing syscall returns wrong errno Return -TARGET_ENOSYS instead of -ENOSYS from linux-user/main.c * Caused strange 'Level 2 synchronization messages' instead of correctly reporting the syscall was missing. * Made glibc simply fail instead of using older syscalls Signed-off-by: Riku Voipio Signed-off-by: Wesley W. Terpstra --- linux-user/main.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/linux-user/main.c b/linux-user/main.c index d695610349..e32f987786 100644 --- a/linux-user/main.c +++ b/linux-user/main.c @@ -2080,7 +2080,7 @@ void cpu_loop(CPUMIPSState *env) syscall_num = env->active_tc.gpr[2] - 4000; env->active_tc.PC += 4; if (syscall_num >= sizeof(mips_syscall_args)) { - ret = -ENOSYS; + ret = -TARGET_ENOSYS; } else { int nb_args; abi_ulong sp_reg; From e6e5bd2dd1868b5a244bc572422f585cef579ffb Mon Sep 17 00:00:00 2001 From: "Wesley W. Terpstra" Date: Tue, 12 Jul 2011 14:34:23 +0300 Subject: [PATCH 079/209] mips: null pointer deref should segfault Dereferencing a null pointer causes an exception 0xC (EXCP_AdEL) instead of EXCP_TLBL. This should also trigger a segfault. Signed-off-by: Wesley W. Terpstra Signed-off-by: Riku Voipio --- linux-user/main.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/linux-user/main.c b/linux-user/main.c index e32f987786..2135b9c714 100644 --- a/linux-user/main.c +++ b/linux-user/main.c @@ -2120,6 +2120,8 @@ void cpu_loop(CPUMIPSState *env) break; case EXCP_TLBL: case EXCP_TLBS: + case EXCP_AdEL: + case EXCP_AdES: info.si_signo = TARGET_SIGSEGV; info.si_errno = 0; /* XXX: check env->error_code */ From 95b33b2f4f0293068d1a42b3ab5badcc6333c6ba Mon Sep 17 00:00:00 2001 From: "Wesley W. Terpstra" Date: Tue, 12 Jul 2011 14:38:22 +0300 Subject: [PATCH 080/209] mips: rlimit incorrectly converts values Byte swap was applied in the wrong order with testing for RLIM_INFINITY. On mips bigendian from an amd64 system this results in infinity being misinterpretted as 2^31-1. This is a serious bug because it causes setrlimit stack size to kill all child processes. This means (for example) that 'make' can run no children. The mechanism of failure: 1. parent sets stack size rlimit to 'infinity' 2. qemu screws this value up 3. child process fetches stack size as a large (but non-infinite) value 4. qemu tries to allocate stack before execution 5. stack allocation fails (too big) and child process dies Signed-off-by: Wesley W. Terpstra Signed-off-by: Riku Voipio --- linux-user/syscall.c | 22 +++++++++++++++++----- 1 file changed, 17 insertions(+), 5 deletions(-) diff --git a/linux-user/syscall.c b/linux-user/syscall.c index 90f6789e5f..4b9e3b8e8e 100644 --- a/linux-user/syscall.c +++ b/linux-user/syscall.c @@ -934,18 +934,30 @@ static inline abi_long host_to_target_rusage(abi_ulong target_addr, static inline rlim_t target_to_host_rlim(target_ulong target_rlim) { - if (target_rlim == TARGET_RLIM_INFINITY) - return RLIM_INFINITY; + target_ulong target_rlim_swap; + rlim_t result; + + target_rlim_swap = tswapl(target_rlim); + if (target_rlim_swap == TARGET_RLIM_INFINITY || target_rlim_swap != (rlim_t)target_rlim_swap) + result = RLIM_INFINITY; else - return tswapl(target_rlim); + result = target_rlim_swap; + + return result; } static inline target_ulong host_to_target_rlim(rlim_t rlim) { + target_ulong target_rlim_swap; + target_ulong result; + if (rlim == RLIM_INFINITY || rlim != (target_long)rlim) - return TARGET_RLIM_INFINITY; + target_rlim_swap = TARGET_RLIM_INFINITY; else - return tswapl(rlim); + target_rlim_swap = rlim; + result = tswapl(target_rlim_swap); + + return result; } static inline abi_long copy_from_user_timeval(struct timeval *tv, From e22b7015353be824620b1f0f5e32a8575b898a8c Mon Sep 17 00:00:00 2001 From: "Wesley W. Terpstra" Date: Tue, 12 Jul 2011 14:42:00 +0300 Subject: [PATCH 081/209] mips: rlimit codes are not the same The codes for get/setrlimit differ between linux target platforms. This patch adds conversion. This is important else programs (rsyslog, python, ...) can go into a near infinite loop trying to close all the file descriptors from 0 to -1. Signed-off-by: Wesley W. Terpstra Signed-off-by: Riku Voipio --- linux-user/syscall.c | 45 ++++++++++++++++++++++++++++++++++++--- linux-user/syscall_defs.h | 34 +++++++++++++++++++++++++++++ 2 files changed, 76 insertions(+), 3 deletions(-) diff --git a/linux-user/syscall.c b/linux-user/syscall.c index 4b9e3b8e8e..9eb41a01ac 100644 --- a/linux-user/syscall.c +++ b/linux-user/syscall.c @@ -960,6 +960,44 @@ static inline target_ulong host_to_target_rlim(rlim_t rlim) return result; } +static inline int target_to_host_resource(int code) +{ + switch (code) { + case TARGET_RLIMIT_AS: + return RLIMIT_AS; + case TARGET_RLIMIT_CORE: + return RLIMIT_CORE; + case TARGET_RLIMIT_CPU: + return RLIMIT_CPU; + case TARGET_RLIMIT_DATA: + return RLIMIT_DATA; + case TARGET_RLIMIT_FSIZE: + return RLIMIT_FSIZE; + case TARGET_RLIMIT_LOCKS: + return RLIMIT_LOCKS; + case TARGET_RLIMIT_MEMLOCK: + return RLIMIT_MEMLOCK; + case TARGET_RLIMIT_MSGQUEUE: + return RLIMIT_MSGQUEUE; + case TARGET_RLIMIT_NICE: + return RLIMIT_NICE; + case TARGET_RLIMIT_NOFILE: + return RLIMIT_NOFILE; + case TARGET_RLIMIT_NPROC: + return RLIMIT_NPROC; + case TARGET_RLIMIT_RSS: + return RLIMIT_RSS; + case TARGET_RLIMIT_RTPRIO: + return RLIMIT_RTPRIO; + case TARGET_RLIMIT_SIGPENDING: + return RLIMIT_SIGPENDING; + case TARGET_RLIMIT_STACK: + return RLIMIT_STACK; + default: + return code; + } +} + static inline abi_long copy_from_user_timeval(struct timeval *tv, abi_ulong target_tv_addr) { @@ -5570,7 +5608,7 @@ abi_long do_syscall(void *cpu_env, int num, abi_long arg1, break; case TARGET_NR_setrlimit: { - int resource = arg1; + int resource = target_to_host_resource(arg1); struct target_rlimit *target_rlim; struct rlimit rlim; if (!lock_user_struct(VERIFY_READ, target_rlim, arg2, 1)) @@ -5583,7 +5621,7 @@ abi_long do_syscall(void *cpu_env, int num, abi_long arg1, break; case TARGET_NR_getrlimit: { - int resource = arg1; + int resource = target_to_host_resource(arg1); struct target_rlimit *target_rlim; struct rlimit rlim; @@ -6892,7 +6930,8 @@ abi_long do_syscall(void *cpu_env, int num, abi_long arg1, case TARGET_NR_ugetrlimit: { struct rlimit rlim; - ret = get_errno(getrlimit(arg1, &rlim)); + int resource = target_to_host_resource(arg1); + ret = get_errno(getrlimit(resource, &rlim)); if (!is_error(ret)) { struct target_rlimit *target_rlim; if (!lock_user_struct(VERIFY_WRITE, target_rlim, arg2, 0)) diff --git a/linux-user/syscall_defs.h b/linux-user/syscall_defs.h index 1fdc84d3e7..a117407d84 100644 --- a/linux-user/syscall_defs.h +++ b/linux-user/syscall_defs.h @@ -693,6 +693,40 @@ struct target_rlimit { #define TARGET_RLIM_INFINITY ((target_ulong)~0UL) #endif +#if defined(TARGET_MIPS) +#define TARGET_RLIMIT_CPU 0 +#define TARGET_RLIMIT_FSIZE 1 +#define TARGET_RLIMIT_DATA 2 +#define TARGET_RLIMIT_STACK 3 +#define TARGET_RLIMIT_CORE 4 +#define TARGET_RLIMIT_RSS 7 +#define TARGET_RLIMIT_NPROC 8 +#define TARGET_RLIMIT_NOFILE 5 +#define TARGET_RLIMIT_MEMLOCK 9 +#define TARGET_RLIMIT_AS 6 +#define TARGET_RLIMIT_LOCKS 10 +#define TARGET_RLIMIT_SIGPENDING 11 +#define TARGET_RLIMIT_MSGQUEUE 12 +#define TARGET_RLIMIT_NICE 13 +#define TARGET_RLIMIT_RTPRIO 14 +#else +#define TARGET_RLIMIT_CPU 0 +#define TARGET_RLIMIT_FSIZE 1 +#define TARGET_RLIMIT_DATA 2 +#define TARGET_RLIMIT_STACK 3 +#define TARGET_RLIMIT_CORE 4 +#define TARGET_RLIMIT_RSS 5 +#define TARGET_RLIMIT_NPROC 6 +#define TARGET_RLIMIT_NOFILE 7 +#define TARGET_RLIMIT_MEMLOCK 8 +#define TARGET_RLIMIT_AS 9 +#define TARGET_RLIMIT_LOCKS 10 +#define TARGET_RLIMIT_SIGPENDING 11 +#define TARGET_RLIMIT_MSGQUEUE 12 +#define TARGET_RLIMIT_NICE 13 +#define TARGET_RLIMIT_RTPRIO 14 +#endif + struct target_pollfd { int fd; /* file descriptor */ short events; /* requested events */ From 1e78bcc19c60c60c11ece020ab35952b5b2895ec Mon Sep 17 00:00:00 2001 From: Alexander Graf Date: Wed, 6 Jul 2011 09:09:23 +0200 Subject: [PATCH 082/209] exec: add endian specific phys ld/st functions Device code some times needs to access physical memory and does that through the ld./st._phys functions. However, these are the exact same functions that the CPU uses to access memory, which means they will be endianness swapped depending on the target CPU. However, devices don't know about the CPU's endianness, but instead access memory directly using their own interface to the memory bus, so they need some way to read data with their native endianness. This patch adds _le and _be functions to ld./st._phys. Signed-off-by: Alexander Graf Signed-off-by: Blue Swirl --- cpu-common.h | 12 +++ exec.c | 201 ++++++++++++++++++++++++++++++++++++++++++++++++--- 2 files changed, 203 insertions(+), 10 deletions(-) diff --git a/cpu-common.h b/cpu-common.h index b027e43088..c6a2b5fbb9 100644 --- a/cpu-common.h +++ b/cpu-common.h @@ -135,14 +135,26 @@ void qemu_flush_coalesced_mmio_buffer(void); uint32_t ldub_phys(target_phys_addr_t addr); uint32_t lduw_phys(target_phys_addr_t addr); +uint32_t lduw_le_phys(target_phys_addr_t addr); +uint32_t lduw_be_phys(target_phys_addr_t addr); uint32_t ldl_phys(target_phys_addr_t addr); +uint32_t ldl_le_phys(target_phys_addr_t addr); +uint32_t ldl_be_phys(target_phys_addr_t addr); uint64_t ldq_phys(target_phys_addr_t addr); +uint64_t ldq_le_phys(target_phys_addr_t addr); +uint64_t ldq_be_phys(target_phys_addr_t addr); void stl_phys_notdirty(target_phys_addr_t addr, uint32_t val); void stq_phys_notdirty(target_phys_addr_t addr, uint64_t val); void stb_phys(target_phys_addr_t addr, uint32_t val); void stw_phys(target_phys_addr_t addr, uint32_t val); +void stw_le_phys(target_phys_addr_t addr, uint32_t val); +void stw_be_phys(target_phys_addr_t addr, uint32_t val); void stl_phys(target_phys_addr_t addr, uint32_t val); +void stl_le_phys(target_phys_addr_t addr, uint32_t val); +void stl_be_phys(target_phys_addr_t addr, uint32_t val); void stq_phys(target_phys_addr_t addr, uint64_t val); +void stq_le_phys(target_phys_addr_t addr, uint64_t val); +void stq_be_phys(target_phys_addr_t addr, uint64_t val); void cpu_physical_memory_write_rom(target_phys_addr_t addr, const uint8_t *buf, int len); diff --git a/exec.c b/exec.c index 4c45299906..9e6913e864 100644 --- a/exec.c +++ b/exec.c @@ -4127,7 +4127,8 @@ void cpu_physical_memory_unmap(void *buffer, target_phys_addr_t len, } /* warning: addr must be aligned */ -uint32_t ldl_phys(target_phys_addr_t addr) +static inline uint32_t ldl_phys_internal(target_phys_addr_t addr, + enum device_endian endian) { int io_index; uint8_t *ptr; @@ -4149,17 +4150,52 @@ uint32_t ldl_phys(target_phys_addr_t addr) if (p) addr = (addr & ~TARGET_PAGE_MASK) + p->region_offset; val = io_mem_read[io_index][2](io_mem_opaque[io_index], addr); +#if defined(TARGET_WORDS_BIGENDIAN) + if (endian == DEVICE_LITTLE_ENDIAN) { + val = bswap32(val); + } +#else + if (endian == DEVICE_BIG_ENDIAN) { + val = bswap32(val); + } +#endif } else { /* RAM case */ ptr = qemu_get_ram_ptr(pd & TARGET_PAGE_MASK) + (addr & ~TARGET_PAGE_MASK); - val = ldl_p(ptr); + switch (endian) { + case DEVICE_LITTLE_ENDIAN: + val = ldl_le_p(ptr); + break; + case DEVICE_BIG_ENDIAN: + val = ldl_be_p(ptr); + break; + default: + val = ldl_p(ptr); + break; + } } return val; } +uint32_t ldl_phys(target_phys_addr_t addr) +{ + return ldl_phys_internal(addr, DEVICE_NATIVE_ENDIAN); +} + +uint32_t ldl_le_phys(target_phys_addr_t addr) +{ + return ldl_phys_internal(addr, DEVICE_LITTLE_ENDIAN); +} + +uint32_t ldl_be_phys(target_phys_addr_t addr) +{ + return ldl_phys_internal(addr, DEVICE_BIG_ENDIAN); +} + /* warning: addr must be aligned */ -uint64_t ldq_phys(target_phys_addr_t addr) +static inline uint64_t ldq_phys_internal(target_phys_addr_t addr, + enum device_endian endian) { int io_index; uint8_t *ptr; @@ -4180,6 +4216,9 @@ uint64_t ldq_phys(target_phys_addr_t addr) io_index = (pd >> IO_MEM_SHIFT) & (IO_MEM_NB_ENTRIES - 1); if (p) addr = (addr & ~TARGET_PAGE_MASK) + p->region_offset; + + /* XXX This is broken when device endian != cpu endian. + Fix and add "endian" variable check */ #ifdef TARGET_WORDS_BIGENDIAN val = (uint64_t)io_mem_read[io_index][2](io_mem_opaque[io_index], addr) << 32; val |= io_mem_read[io_index][2](io_mem_opaque[io_index], addr + 4); @@ -4191,11 +4230,36 @@ uint64_t ldq_phys(target_phys_addr_t addr) /* RAM case */ ptr = qemu_get_ram_ptr(pd & TARGET_PAGE_MASK) + (addr & ~TARGET_PAGE_MASK); - val = ldq_p(ptr); + switch (endian) { + case DEVICE_LITTLE_ENDIAN: + val = ldq_le_p(ptr); + break; + case DEVICE_BIG_ENDIAN: + val = ldq_be_p(ptr); + break; + default: + val = ldq_p(ptr); + break; + } } return val; } +uint64_t ldq_phys(target_phys_addr_t addr) +{ + return ldq_phys_internal(addr, DEVICE_NATIVE_ENDIAN); +} + +uint64_t ldq_le_phys(target_phys_addr_t addr) +{ + return ldq_phys_internal(addr, DEVICE_LITTLE_ENDIAN); +} + +uint64_t ldq_be_phys(target_phys_addr_t addr) +{ + return ldq_phys_internal(addr, DEVICE_BIG_ENDIAN); +} + /* XXX: optimize */ uint32_t ldub_phys(target_phys_addr_t addr) { @@ -4205,7 +4269,8 @@ uint32_t ldub_phys(target_phys_addr_t addr) } /* warning: addr must be aligned */ -uint32_t lduw_phys(target_phys_addr_t addr) +static inline uint32_t lduw_phys_internal(target_phys_addr_t addr, + enum device_endian endian) { int io_index; uint8_t *ptr; @@ -4227,15 +4292,49 @@ uint32_t lduw_phys(target_phys_addr_t addr) if (p) addr = (addr & ~TARGET_PAGE_MASK) + p->region_offset; val = io_mem_read[io_index][1](io_mem_opaque[io_index], addr); +#if defined(TARGET_WORDS_BIGENDIAN) + if (endian == DEVICE_LITTLE_ENDIAN) { + val = bswap16(val); + } +#else + if (endian == DEVICE_BIG_ENDIAN) { + val = bswap16(val); + } +#endif } else { /* RAM case */ ptr = qemu_get_ram_ptr(pd & TARGET_PAGE_MASK) + (addr & ~TARGET_PAGE_MASK); - val = lduw_p(ptr); + switch (endian) { + case DEVICE_LITTLE_ENDIAN: + val = lduw_le_p(ptr); + break; + case DEVICE_BIG_ENDIAN: + val = lduw_be_p(ptr); + break; + default: + val = lduw_p(ptr); + break; + } } return val; } +uint32_t lduw_phys(target_phys_addr_t addr) +{ + return lduw_phys_internal(addr, DEVICE_NATIVE_ENDIAN); +} + +uint32_t lduw_le_phys(target_phys_addr_t addr) +{ + return lduw_phys_internal(addr, DEVICE_LITTLE_ENDIAN); +} + +uint32_t lduw_be_phys(target_phys_addr_t addr) +{ + return lduw_phys_internal(addr, DEVICE_BIG_ENDIAN); +} + /* warning: addr must be aligned. The ram page is not masked as dirty and the code inside is not invalidated. It is useful if the dirty bits are used to track modified PTEs */ @@ -4308,7 +4407,8 @@ void stq_phys_notdirty(target_phys_addr_t addr, uint64_t val) } /* warning: addr must be aligned */ -void stl_phys(target_phys_addr_t addr, uint32_t val) +static inline void stl_phys_internal(target_phys_addr_t addr, uint32_t val, + enum device_endian endian) { int io_index; uint8_t *ptr; @@ -4326,13 +4426,32 @@ void stl_phys(target_phys_addr_t addr, uint32_t val) io_index = (pd >> IO_MEM_SHIFT) & (IO_MEM_NB_ENTRIES - 1); if (p) addr = (addr & ~TARGET_PAGE_MASK) + p->region_offset; +#if defined(TARGET_WORDS_BIGENDIAN) + if (endian == DEVICE_LITTLE_ENDIAN) { + val = bswap32(val); + } +#else + if (endian == DEVICE_BIG_ENDIAN) { + val = bswap32(val); + } +#endif io_mem_write[io_index][2](io_mem_opaque[io_index], addr, val); } else { unsigned long addr1; addr1 = (pd & TARGET_PAGE_MASK) + (addr & ~TARGET_PAGE_MASK); /* RAM case */ ptr = qemu_get_ram_ptr(addr1); - stl_p(ptr, val); + switch (endian) { + case DEVICE_LITTLE_ENDIAN: + stl_le_p(ptr, val); + break; + case DEVICE_BIG_ENDIAN: + stl_be_p(ptr, val); + break; + default: + stl_p(ptr, val); + break; + } if (!cpu_physical_memory_is_dirty(addr1)) { /* invalidate code */ tb_invalidate_phys_page_range(addr1, addr1 + 4, 0); @@ -4343,6 +4462,21 @@ void stl_phys(target_phys_addr_t addr, uint32_t val) } } +void stl_phys(target_phys_addr_t addr, uint32_t val) +{ + stl_phys_internal(addr, val, DEVICE_NATIVE_ENDIAN); +} + +void stl_le_phys(target_phys_addr_t addr, uint32_t val) +{ + stl_phys_internal(addr, val, DEVICE_LITTLE_ENDIAN); +} + +void stl_be_phys(target_phys_addr_t addr, uint32_t val) +{ + stl_phys_internal(addr, val, DEVICE_BIG_ENDIAN); +} + /* XXX: optimize */ void stb_phys(target_phys_addr_t addr, uint32_t val) { @@ -4351,7 +4485,8 @@ void stb_phys(target_phys_addr_t addr, uint32_t val) } /* warning: addr must be aligned */ -void stw_phys(target_phys_addr_t addr, uint32_t val) +static inline void stw_phys_internal(target_phys_addr_t addr, uint32_t val, + enum device_endian endian) { int io_index; uint8_t *ptr; @@ -4369,13 +4504,32 @@ void stw_phys(target_phys_addr_t addr, uint32_t val) io_index = (pd >> IO_MEM_SHIFT) & (IO_MEM_NB_ENTRIES - 1); if (p) addr = (addr & ~TARGET_PAGE_MASK) + p->region_offset; +#if defined(TARGET_WORDS_BIGENDIAN) + if (endian == DEVICE_LITTLE_ENDIAN) { + val = bswap16(val); + } +#else + if (endian == DEVICE_BIG_ENDIAN) { + val = bswap16(val); + } +#endif io_mem_write[io_index][1](io_mem_opaque[io_index], addr, val); } else { unsigned long addr1; addr1 = (pd & TARGET_PAGE_MASK) + (addr & ~TARGET_PAGE_MASK); /* RAM case */ ptr = qemu_get_ram_ptr(addr1); - stw_p(ptr, val); + switch (endian) { + case DEVICE_LITTLE_ENDIAN: + stw_le_p(ptr, val); + break; + case DEVICE_BIG_ENDIAN: + stw_be_p(ptr, val); + break; + default: + stw_p(ptr, val); + break; + } if (!cpu_physical_memory_is_dirty(addr1)) { /* invalidate code */ tb_invalidate_phys_page_range(addr1, addr1 + 2, 0); @@ -4386,6 +4540,21 @@ void stw_phys(target_phys_addr_t addr, uint32_t val) } } +void stw_phys(target_phys_addr_t addr, uint32_t val) +{ + stw_phys_internal(addr, val, DEVICE_NATIVE_ENDIAN); +} + +void stw_le_phys(target_phys_addr_t addr, uint32_t val) +{ + stw_phys_internal(addr, val, DEVICE_LITTLE_ENDIAN); +} + +void stw_be_phys(target_phys_addr_t addr, uint32_t val) +{ + stw_phys_internal(addr, val, DEVICE_BIG_ENDIAN); +} + /* XXX: optimize */ void stq_phys(target_phys_addr_t addr, uint64_t val) { @@ -4393,6 +4562,18 @@ void stq_phys(target_phys_addr_t addr, uint64_t val) cpu_physical_memory_write(addr, &val, 8); } +void stq_le_phys(target_phys_addr_t addr, uint64_t val) +{ + val = cpu_to_le64(val); + cpu_physical_memory_write(addr, &val, 8); +} + +void stq_be_phys(target_phys_addr_t addr, uint64_t val) +{ + val = cpu_to_be64(val); + cpu_physical_memory_write(addr, &val, 8); +} + /* virtual memory access for debug (includes writing to ROM) */ int cpu_memory_rw_debug(CPUState *env, target_ulong addr, uint8_t *buf, int len, int is_write) From 8517263fcbf2182ce97e3335f2a20b52d4e33fce Mon Sep 17 00:00:00 2001 From: Alexander Graf Date: Tue, 5 Jul 2011 18:28:03 +0200 Subject: [PATCH 083/209] hpet: use specific endian ld/st_phys Signed-off-by: Alexander Graf Signed-off-by: Blue Swirl --- hw/hpet.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hw/hpet.c b/hw/hpet.c index ef9a2a0cad..4eda33d900 100644 --- a/hw/hpet.c +++ b/hw/hpet.c @@ -192,7 +192,7 @@ static void update_irq(struct HPETTimer *timer, int set) qemu_irq_lower(s->irqs[route]); } } else if (timer_fsb_route(timer)) { - stl_phys(timer->fsb >> 32, timer->fsb & 0xffffffff); + stl_le_phys(timer->fsb >> 32, timer->fsb & 0xffffffff); } else if (timer->config & HPET_TN_TYPE_LEVEL) { s->isr |= mask; qemu_irq_raise(s->irqs[route]); From 6c7796e5c17bc23d0add756b5248fcf419e999a9 Mon Sep 17 00:00:00 2001 From: Alexander Graf Date: Tue, 5 Jul 2011 18:28:04 +0200 Subject: [PATCH 084/209] intel-hda: use specific endian ld/st_phys Signed-off-by: Alexander Graf Signed-off-by: Blue Swirl --- hw/intel-hda.c | 21 ++++----------------- 1 file changed, 4 insertions(+), 17 deletions(-) diff --git a/hw/intel-hda.c b/hw/intel-hda.c index 0ffffce90e..5a2bc3aaa9 100644 --- a/hw/intel-hda.c +++ b/hw/intel-hda.c @@ -224,19 +224,6 @@ static target_phys_addr_t intel_hda_addr(uint32_t lbase, uint32_t ubase) return addr; } -static void stl_phys_le(target_phys_addr_t addr, uint32_t value) -{ - uint32_t value_le = cpu_to_le32(value); - cpu_physical_memory_write(addr, (uint8_t*)(&value_le), sizeof(value_le)); -} - -static uint32_t ldl_phys_le(target_phys_addr_t addr) -{ - uint32_t value_le; - cpu_physical_memory_read(addr, (uint8_t*)(&value_le), sizeof(value_le)); - return le32_to_cpu(value_le); -} - static void intel_hda_update_int_sts(IntelHDAState *d) { uint32_t sts = 0; @@ -341,7 +328,7 @@ static void intel_hda_corb_run(IntelHDAState *d) rp = (d->corb_rp + 1) & 0xff; addr = intel_hda_addr(d->corb_lbase, d->corb_ubase); - verb = ldl_phys_le(addr + 4*rp); + verb = ldl_le_phys(addr + 4*rp); d->corb_rp = rp; dprint(d, 2, "%s: [rp 0x%x] verb 0x%08x\n", __FUNCTION__, rp, verb); @@ -373,8 +360,8 @@ static void intel_hda_response(HDACodecDevice *dev, bool solicited, uint32_t res ex = (solicited ? 0 : (1 << 4)) | dev->cad; wp = (d->rirb_wp + 1) & 0xff; addr = intel_hda_addr(d->rirb_lbase, d->rirb_ubase); - stl_phys_le(addr + 8*wp, response); - stl_phys_le(addr + 8*wp + 4, ex); + stl_le_phys(addr + 8*wp, response); + stl_le_phys(addr + 8*wp + 4, ex); d->rirb_wp = wp; dprint(d, 2, "%s: [wp 0x%x] response 0x%x, extra 0x%x\n", @@ -461,7 +448,7 @@ static bool intel_hda_xfer(HDACodecDevice *dev, uint32_t stnr, bool output, } if (d->dp_lbase & 0x01) { addr = intel_hda_addr(d->dp_lbase & ~0x01, d->dp_ubase); - stl_phys_le(addr + 8*s, st->lpib); + stl_le_phys(addr + 8*s, st->lpib); } dprint(d, 3, "dma: --\n"); From c5d29d2fecff299d4b65bfd5546d58915d8e25e0 Mon Sep 17 00:00:00 2001 From: Alexander Graf Date: Tue, 5 Jul 2011 18:28:05 +0200 Subject: [PATCH 085/209] msi: use specific endian ld/st_phys Signed-off-by: Alexander Graf Signed-off-by: Blue Swirl --- hw/msi.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hw/msi.c b/hw/msi.c index e8c56079aa..f214fcf579 100644 --- a/hw/msi.c +++ b/hw/msi.c @@ -249,7 +249,7 @@ void msi_notify(PCIDevice *dev, unsigned int vector) "notify vector 0x%x" " address: 0x%"PRIx64" data: 0x%"PRIx32"\n", vector, address, data); - stl_phys(address, data); + stl_le_phys(address, data); } /* call this function after updating configs by pci_default_write_config(). */ From ae5d3eb4745dbcc38a4d6bbaff563f4c2d0e7a16 Mon Sep 17 00:00:00 2001 From: Alexander Graf Date: Tue, 5 Jul 2011 18:28:06 +0200 Subject: [PATCH 086/209] msix: use specific endian ld/st_phys Signed-off-by: Alexander Graf Signed-off-by: Blue Swirl --- hw/msix.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hw/msix.c b/hw/msix.c index 03d7becaaf..e67e700a33 100644 --- a/hw/msix.c +++ b/hw/msix.c @@ -359,7 +359,7 @@ void msix_notify(PCIDevice *dev, unsigned vector) address = pci_get_quad(table_entry + PCI_MSIX_ENTRY_LOWER_ADDR); data = pci_get_long(table_entry + PCI_MSIX_ENTRY_DATA); - stl_phys(address, data); + stl_le_phys(address, data); } void msix_reset(PCIDevice *dev) From 75b0646f9e25e42cdf1d73f7d2407c4966be48f1 Mon Sep 17 00:00:00 2001 From: Alexander Graf Date: Tue, 5 Jul 2011 18:28:07 +0200 Subject: [PATCH 087/209] pl080: use specific endian ld/st_phys Signed-off-by: Alexander Graf Signed-off-by: Blue Swirl --- hw/pl080.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/hw/pl080.c b/hw/pl080.c index 901f04a844..dd8139ba96 100644 --- a/hw/pl080.c +++ b/hw/pl080.c @@ -199,10 +199,10 @@ again: if (size == 0) { /* Transfer complete. */ if (ch->lli) { - ch->src = ldl_phys(ch->lli); - ch->dest = ldl_phys(ch->lli + 4); - ch->ctrl = ldl_phys(ch->lli + 12); - ch->lli = ldl_phys(ch->lli + 8); + ch->src = ldl_le_phys(ch->lli); + ch->dest = ldl_le_phys(ch->lli + 4); + ch->ctrl = ldl_le_phys(ch->lli + 12); + ch->lli = ldl_le_phys(ch->lli + 8); } else { ch->conf &= ~PL080_CCONF_E; } From db663d0f7aae936d10e2452d0eab8bb3b0147e74 Mon Sep 17 00:00:00 2001 From: Alexander Graf Date: Tue, 5 Jul 2011 18:28:08 +0200 Subject: [PATCH 088/209] ppc405_uc: use specific endian ld/st_phys Signed-off-by: Alexander Graf Signed-off-by: Blue Swirl --- hw/ppc405_uc.c | 43 +++++++++++++++++++++++-------------------- 1 file changed, 23 insertions(+), 20 deletions(-) diff --git a/hw/ppc405_uc.c b/hw/ppc405_uc.c index 2ce79ee053..06a053bf07 100644 --- a/hw/ppc405_uc.c +++ b/hw/ppc405_uc.c @@ -51,39 +51,42 @@ ram_addr_t ppc405_set_bootinfo (CPUState *env, ppc4xx_bd_info_t *bd, bdloc = 0x01000000UL - sizeof(struct ppc4xx_bd_info_t); else bdloc = bd->bi_memsize - sizeof(struct ppc4xx_bd_info_t); - stl_phys(bdloc + 0x00, bd->bi_memstart); - stl_phys(bdloc + 0x04, bd->bi_memsize); - stl_phys(bdloc + 0x08, bd->bi_flashstart); - stl_phys(bdloc + 0x0C, bd->bi_flashsize); - stl_phys(bdloc + 0x10, bd->bi_flashoffset); - stl_phys(bdloc + 0x14, bd->bi_sramstart); - stl_phys(bdloc + 0x18, bd->bi_sramsize); - stl_phys(bdloc + 0x1C, bd->bi_bootflags); - stl_phys(bdloc + 0x20, bd->bi_ipaddr); - for (i = 0; i < 6; i++) + stl_be_phys(bdloc + 0x00, bd->bi_memstart); + stl_be_phys(bdloc + 0x04, bd->bi_memsize); + stl_be_phys(bdloc + 0x08, bd->bi_flashstart); + stl_be_phys(bdloc + 0x0C, bd->bi_flashsize); + stl_be_phys(bdloc + 0x10, bd->bi_flashoffset); + stl_be_phys(bdloc + 0x14, bd->bi_sramstart); + stl_be_phys(bdloc + 0x18, bd->bi_sramsize); + stl_be_phys(bdloc + 0x1C, bd->bi_bootflags); + stl_be_phys(bdloc + 0x20, bd->bi_ipaddr); + for (i = 0; i < 6; i++) { stb_phys(bdloc + 0x24 + i, bd->bi_enetaddr[i]); - stw_phys(bdloc + 0x2A, bd->bi_ethspeed); - stl_phys(bdloc + 0x2C, bd->bi_intfreq); - stl_phys(bdloc + 0x30, bd->bi_busfreq); - stl_phys(bdloc + 0x34, bd->bi_baudrate); - for (i = 0; i < 4; i++) + } + stw_be_phys(bdloc + 0x2A, bd->bi_ethspeed); + stl_be_phys(bdloc + 0x2C, bd->bi_intfreq); + stl_be_phys(bdloc + 0x30, bd->bi_busfreq); + stl_be_phys(bdloc + 0x34, bd->bi_baudrate); + for (i = 0; i < 4; i++) { stb_phys(bdloc + 0x38 + i, bd->bi_s_version[i]); + } for (i = 0; i < 32; i++) { stb_phys(bdloc + 0x3C + i, bd->bi_r_version[i]); } - stl_phys(bdloc + 0x5C, bd->bi_plb_busfreq); - stl_phys(bdloc + 0x60, bd->bi_pci_busfreq); - for (i = 0; i < 6; i++) + stl_be_phys(bdloc + 0x5C, bd->bi_plb_busfreq); + stl_be_phys(bdloc + 0x60, bd->bi_pci_busfreq); + for (i = 0; i < 6; i++) { stb_phys(bdloc + 0x64 + i, bd->bi_pci_enetaddr[i]); + } n = 0x6A; if (flags & 0x00000001) { for (i = 0; i < 6; i++) stb_phys(bdloc + n++, bd->bi_pci_enetaddr2[i]); } - stl_phys(bdloc + n, bd->bi_opbfreq); + stl_be_phys(bdloc + n, bd->bi_opbfreq); n += 4; for (i = 0; i < 2; i++) { - stl_phys(bdloc + n, bd->bi_iic_fast[i]); + stl_be_phys(bdloc + n, bd->bi_iic_fast[i]); n += 4; } From 04bc74edecef1b671c84be0b402998e96a282889 Mon Sep 17 00:00:00 2001 From: Alexander Graf Date: Tue, 5 Jul 2011 18:28:09 +0200 Subject: [PATCH 089/209] s390-virtio: use specific endian ld/st_phys Signed-off-by: Alexander Graf Signed-off-by: Blue Swirl --- hw/s390-virtio-bus.c | 10 +++++----- hw/s390-virtio.c | 6 +++--- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/hw/s390-virtio-bus.c b/hw/s390-virtio-bus.c index 2bf4821a11..e2f3e32aca 100644 --- a/hw/s390-virtio-bus.c +++ b/hw/s390-virtio-bus.c @@ -166,7 +166,7 @@ static uint64_t s390_virtio_device_vq_token(VirtIOS390Device *dev, int vq) (vq * VIRTIO_VQCONFIG_LEN) + VIRTIO_VQCONFIG_OFFS_TOKEN; - return ldq_phys(token_off); + return ldq_be_phys(token_off); } static ram_addr_t s390_virtio_device_num_vq(VirtIOS390Device *dev) @@ -220,8 +220,8 @@ void s390_virtio_device_sync(VirtIOS390Device *dev) vring = s390_virtio_next_ring(bus); virtio_queue_set_addr(dev->vdev, i, vring); virtio_queue_set_vector(dev->vdev, i, i); - stq_phys(vq + VIRTIO_VQCONFIG_OFFS_ADDRESS, vring); - stw_phys(vq + VIRTIO_VQCONFIG_OFFS_NUM, virtio_queue_get_num(dev->vdev, i)); + stq_be_phys(vq + VIRTIO_VQCONFIG_OFFS_ADDRESS, vring); + stw_be_phys(vq + VIRTIO_VQCONFIG_OFFS_NUM, virtio_queue_get_num(dev->vdev, i)); } cur_offs = dev->dev_offs; @@ -229,7 +229,7 @@ void s390_virtio_device_sync(VirtIOS390Device *dev) cur_offs += num_vq * VIRTIO_VQCONFIG_LEN; /* Sync feature bitmap */ - stl_phys(cur_offs, bswap32(dev->host_features)); + stl_le_phys(cur_offs, dev->host_features); dev->feat_offs = cur_offs + dev->feat_len; cur_offs += dev->feat_len * 2; @@ -253,7 +253,7 @@ void s390_virtio_device_update_status(VirtIOS390Device *dev) /* Update guest supported feature bitmap */ - features = bswap32(ldl_phys(dev->feat_offs)); + features = bswap32(ldl_be_phys(dev->feat_offs)); if (vdev->set_features) { vdev->set_features(vdev, features); } diff --git a/hw/s390-virtio.c b/hw/s390-virtio.c index 3eba7ea1e8..abe954dc88 100644 --- a/hw/s390-virtio.c +++ b/hw/s390-virtio.c @@ -193,7 +193,7 @@ static void s390_init(ram_addr_t my_ram_size, if (kernel_filename) { kernel_size = load_image(kernel_filename, qemu_get_ram_ptr(0)); - if (lduw_phys(KERN_IMAGE_START) != 0x0dd0) { + if (lduw_be_phys(KERN_IMAGE_START) != 0x0dd0) { fprintf(stderr, "Specified image is not an s390 boot image\n"); exit(1); } @@ -232,8 +232,8 @@ static void s390_init(ram_addr_t my_ram_size, } initrd_size = load_image(initrd_filename, qemu_get_ram_ptr(initrd_offset)); - stq_phys(INITRD_PARM_START, initrd_offset); - stq_phys(INITRD_PARM_SIZE, initrd_size); + stq_be_phys(INITRD_PARM_START, initrd_offset); + stq_be_phys(INITRD_PARM_SIZE, initrd_size); } if (kernel_cmdline) { From 06c46bbab04faa3b5a1c10756be7db4f09af1ffb Mon Sep 17 00:00:00 2001 From: Alexander Graf Date: Tue, 5 Jul 2011 18:28:10 +0200 Subject: [PATCH 090/209] spapr: use specific endian ld/st_phys Signed-off-by: Alexander Graf Signed-off-by: Blue Swirl --- hw/spapr.h | 4 ++-- hw/spapr_hcall.c | 12 ++++++------ 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/hw/spapr.h b/hw/spapr.h index b52133a4aa..263691b6fb 100644 --- a/hw/spapr.h +++ b/hw/spapr.h @@ -280,12 +280,12 @@ target_ulong spapr_hypercall(CPUState *env, target_ulong opcode, static inline uint32_t rtas_ld(target_ulong phys, int n) { - return ldl_phys(phys + 4*n); + return ldl_be_phys(phys + 4*n); } static inline void rtas_st(target_ulong phys, int n, uint32_t val) { - stl_phys(phys + 4*n, val); + stl_be_phys(phys + 4*n, val); } typedef void (*spapr_rtas_fn)(sPAPREnvironment *spapr, uint32_t token, diff --git a/hw/spapr_hcall.c b/hw/spapr_hcall.c index 84da8fc69c..5cd8d8f5ae 100644 --- a/hw/spapr_hcall.c +++ b/hw/spapr_hcall.c @@ -278,7 +278,7 @@ static target_ulong register_vpa(CPUState *env, target_ulong vpa) } /* FIXME: bounds check the address */ - size = lduw_phys(vpa + 0x4); + size = lduw_be_phys(vpa + 0x4); if (size < VPA_MIN_SIZE) { return H_PARAMETER; @@ -321,7 +321,7 @@ static target_ulong register_slb_shadow(CPUState *env, target_ulong addr) return H_HARDWARE; } - size = ldl_phys(addr + 0x4); + size = ldl_be_phys(addr + 0x4); if (size < 0x8) { return H_PARAMETER; } @@ -354,7 +354,7 @@ static target_ulong register_dtl(CPUState *env, target_ulong addr) return H_HARDWARE; } - size = ldl_phys(addr + 0x4); + size = ldl_be_phys(addr + 0x4); if (size < 48) { return H_PARAMETER; @@ -441,9 +441,9 @@ static target_ulong h_rtas(CPUState *env, sPAPREnvironment *spapr, target_ulong opcode, target_ulong *args) { target_ulong rtas_r3 = args[0]; - uint32_t token = ldl_phys(rtas_r3); - uint32_t nargs = ldl_phys(rtas_r3 + 4); - uint32_t nret = ldl_phys(rtas_r3 + 8); + uint32_t token = ldl_be_phys(rtas_r3); + uint32_t nargs = ldl_be_phys(rtas_r3 + 4); + uint32_t nret = ldl_be_phys(rtas_r3 + 8); return spapr_rtas_call(spapr, token, nargs, rtas_r3 + 12, nret, rtas_r3 + 12 + 4*nargs); From a884da8a06806d55fa83c8011bb17d6838583f9b Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Wed, 22 Jun 2011 11:58:25 +0100 Subject: [PATCH 091/209] exec.c: Fix calculation of code_gen_buffer_max_size When calculating the point at which we should not try to put another TB into the code gen buffer, we have to allow not just for OPC_MAX_SIZE but OPC_BUF_SIZE. This is because the target translate.c will only stop when an instruction has put it past the OPC_MAX_SIZE limit, so we have to include the MAX_OP_PER_INSTR margin which that final insn might have used. Signed-off-by: Peter Maydell Signed-off-by: Blue Swirl --- exec.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/exec.c b/exec.c index 9e6913e864..f1777e6239 100644 --- a/exec.c +++ b/exec.c @@ -555,8 +555,8 @@ static void code_gen_alloc(unsigned long tb_size) #endif #endif /* !USE_STATIC_CODE_GEN_BUFFER */ map_exec(code_gen_prologue, sizeof(code_gen_prologue)); - code_gen_buffer_max_size = code_gen_buffer_size - - (TCG_MAX_OP_SIZE * OPC_MAX_SIZE); + code_gen_buffer_max_size = code_gen_buffer_size - + (TCG_MAX_OP_SIZE * OPC_BUF_SIZE); code_gen_max_blocks = code_gen_buffer_size / CODE_GEN_AVG_BLOCK_SIZE; tbs = qemu_malloc(code_gen_max_blocks * sizeof(TranslationBlock)); } From 5b620fb698e69a5386f2f02c7c455bdbdd59a52b Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Wed, 22 Jun 2011 15:16:32 +0100 Subject: [PATCH 092/209] exec-all.h: Make MAX_OP_PER_INSTR large enough for target-arm's uses The target-arm frontend's worst-case TCG ops per instr is 194 (and in general many of the "load multiple registers" ARM instructions generate more than 100 TCG ops). Raise MAX_OP_PER_INSTR accordingly to avoid possible buffer overruns. Since it doesn't make any sense for the "64 bit guest on 32 bit host" case to have a smaller limit than the normal case, we collapse the two cases back into each other again. (This increase costs us about 14K in extra static buffer space and 21K of extra margin at the end of a 32MB codegen buffer.) Signed-off-by: Peter Maydell Signed-off-by: Blue Swirl --- exec-all.h | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/exec-all.h b/exec-all.h index 21a69d68ad..69acf3b97d 100644 --- a/exec-all.h +++ b/exec-all.h @@ -44,11 +44,7 @@ struct TranslationBlock; typedef struct TranslationBlock TranslationBlock; /* XXX: make safe guess about sizes */ -#if (HOST_LONG_BITS == 32) && (TARGET_LONG_BITS == 64) -#define MAX_OP_PER_INSTR 128 -#else -#define MAX_OP_PER_INSTR 96 -#endif +#define MAX_OP_PER_INSTR 208 #if HOST_LONG_BITS == 32 #define MAX_OPC_PARAM_PER_ARG 2 From 0d10193870b5a81c3bce13a602a5403c3a55cf6c Mon Sep 17 00:00:00 2001 From: Jan Kiszka Date: Sat, 2 Jul 2011 09:50:51 +0200 Subject: [PATCH 093/209] tcg: Reload local variables after return from longjmp Recent compilers look deep into cpu_exec, find longjmp as a noreturn function and decide to smash some stack variables as they won't be used again. This may lead to env becoming invalid after return from setjmp, causing crashes. Fix it by reloading env from cpu_single_env in that case. Signed-off-by: Jan Kiszka Signed-off-by: Blue Swirl --- cpu-exec.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/cpu-exec.c b/cpu-exec.c index 20e3ec41d7..de0d716da0 100644 --- a/cpu-exec.c +++ b/cpu-exec.c @@ -587,6 +587,10 @@ int cpu_exec(CPUState *env) /* reset soft MMU for next block (it can currently only be set by a memory fault) */ } /* for(;;) */ + } else { + /* Reload env after longjmp - the compiler may have smashed all + * local variables as longjmp is marked 'noreturn'. */ + env = cpu_single_env; } } /* for(;;) */ From 462df2887cf55bb95faa7a76a0fd07722c63bb0c Mon Sep 17 00:00:00 2001 From: Stefan Weil Date: Mon, 4 Jul 2011 20:52:38 +0200 Subject: [PATCH 094/209] Remove unneeded setjmp.h (fix compilation on Debian "lenny") Some versions of png.h cannot be included after setjmp.h, even when PNG_SKIP_SETJMP_CHECK was defined. setjmp.h was included from qemu-common.h and is not needed there. Removing the include statement fixes compilation of ui/vnc-enc-tight.c with CONFIG_VNC_PNG defined. Signed-off-by: Stefan Weil Signed-off-by: Blue Swirl --- qemu-common.h | 1 - 1 file changed, 1 deletion(-) diff --git a/qemu-common.h b/qemu-common.h index abd7a75b72..c2b79bd60e 100644 --- a/qemu-common.h +++ b/qemu-common.h @@ -117,7 +117,6 @@ static inline char *realpath(const char *path, char *resolved_path) /* FIXME: Remove NEED_CPU_H. */ #ifndef NEED_CPU_H -#include #include "osdep.h" #include "bswap.h" From f5fc40bb8133849618e0d05adc798c5f07f7b17f Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Mon, 4 Jul 2011 22:02:46 +0100 Subject: [PATCH 095/209] target-alpha, target-ppc: Remove unnecessary setjmp.h include Remove the include of setjmp.h from the cpu.h of target-alpha and target-ppc. This is unnecessary because cpu-defs.h already includes this header; this change brings these two targets into line with all the rest. Signed-off-by: Peter Maydell Signed-off-by: Blue Swirl --- target-alpha/cpu.h | 2 -- target-ppc/cpu.h | 2 -- 2 files changed, 4 deletions(-) diff --git a/target-alpha/cpu.h b/target-alpha/cpu.h index 411bd55328..78caa796b6 100644 --- a/target-alpha/cpu.h +++ b/target-alpha/cpu.h @@ -28,8 +28,6 @@ #include "cpu-defs.h" -#include - #include "softfloat.h" #define TARGET_HAS_ICE 1 diff --git a/target-ppc/cpu.h b/target-ppc/cpu.h index 84f8ff6748..d90336634d 100644 --- a/target-ppc/cpu.h +++ b/target-ppc/cpu.h @@ -75,8 +75,6 @@ #include "cpu-defs.h" -#include - #include "softfloat.h" #define TARGET_HAS_ICE 1 From 429bef6912bd3d504593b9aefdbcb39e981d387e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Herv=C3=A9=20Poussineau?= Date: Sat, 9 Jul 2011 16:44:41 +0200 Subject: [PATCH 096/209] esp: cancel current request only if some request is in flight MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This bug was introduced in 94d3f98a3f3caddd7875f9a11776daeb84962a7b: scsi_cancel_io was checking if some request was pending before trying to cancel it, while scsi_req_cancel always cancels the request. This may lead to a crash of Qemu due to dereferencing a NULL pointer, as exhibited by NetBSD 5.1 installer on MIPS Magnum emulation. Signed-off-by: Hervé Poussineau Signed-off-by: Blue Swirl --- hw/esp.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hw/esp.c b/hw/esp.c index 8e95672f0e..aa50800a56 100644 --- a/hw/esp.c +++ b/hw/esp.c @@ -219,7 +219,7 @@ static uint32_t get_cmd(ESPState *s, uint8_t *buf) s->ti_rptr = 0; s->ti_wptr = 0; - if (s->current_dev) { + if (s->current_req) { /* Started a new command before the old one finished. Cancel it. */ scsi_req_cancel(s->current_req); s->async_len = 0; From cc4662f9642995c78bed587707eeb9ad8500035b Mon Sep 17 00:00:00 2001 From: Stefan Hajnoczi Date: Sat, 9 Jul 2011 10:22:07 +0100 Subject: [PATCH 097/209] os-posix: set groups properly for -runas Andrew Griffiths reports that -runas does not set supplementary group IDs. This means that gid 0 (root) is not dropped when switching to an unprivileged user. Add an initgroups(3) call to use the -runas user's /etc/groups membership to update the supplementary group IDs. Signed-off-by: Stefan Hajnoczi Acked-by: Chris Wright Signed-off-by: Blue Swirl --- os-posix.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/os-posix.c b/os-posix.c index 7dfb27836b..6f8d4886ea 100644 --- a/os-posix.c +++ b/os-posix.c @@ -31,6 +31,7 @@ /*needed for MAP_POPULATE before including qemu-options.h */ #include #include +#include #include /* Needed early for CONFIG_BSD etc. */ @@ -199,6 +200,11 @@ static void change_process_uid(void) fprintf(stderr, "Failed to setgid(%d)\n", user_pwd->pw_gid); exit(1); } + if (initgroups(user_pwd->pw_name, user_pwd->pw_gid) < 0) { + fprintf(stderr, "Failed to initgroups(\"%s\", %d)\n", + user_pwd->pw_name, user_pwd->pw_gid); + exit(1); + } if (setuid(user_pwd->pw_uid) < 0) { fprintf(stderr, "Failed to setuid(%d)\n", user_pwd->pw_uid); exit(1); From c3edf3472faf3dad73a0c504db8e9d853debfde6 Mon Sep 17 00:00:00 2001 From: Riku Voipio Date: Tue, 12 Jul 2011 16:01:54 +0300 Subject: [PATCH 098/209] linux-user: correct syscall 123 on sh4 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit As reported by Cédric VINCENT: The syscall #123 on SH4 should be "TARGET_NR_cacheflush" instead of "TARGET_NR_modify_ldt" [1]. The only consequence of this misnaming is that many "Unsupported syscall" warnings are issued when emulating JIT compilers. Reported-by: Cédric VINCENT Signed-off-by: Riku Voipio --- linux-user/sh4/syscall_nr.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/linux-user/sh4/syscall_nr.h b/linux-user/sh4/syscall_nr.h index 6173a7c059..365db586c7 100644 --- a/linux-user/sh4/syscall_nr.h +++ b/linux-user/sh4/syscall_nr.h @@ -125,7 +125,7 @@ #define TARGET_NR_clone 120 #define TARGET_NR_setdomainname 121 #define TARGET_NR_uname 122 -#define TARGET_NR_modify_ldt 123 +#define TARGET_NR_cacheflush 123 #define TARGET_NR_adjtimex 124 #define TARGET_NR_mprotect 125 #define TARGET_NR_sigprocmask 126 From 48e515d4fa7c6ffdc21944ffc8620a161c772c09 Mon Sep 17 00:00:00 2001 From: Riku Voipio Date: Tue, 12 Jul 2011 15:40:51 +0300 Subject: [PATCH 099/209] linux-user: make MIPS and ARM eabi use same argument reordering MIPS uses similar calling convention than ARM eabi, where when using 64-bit values some registers are skipped. This patch makes MIPS and ARM eabi share the argument reordering code. This affects ftruncate64, creating insane sized fails (or just failing). Cc: Wesley W. Terpstra Signed-off-by: Riku Voipio --- linux-user/syscall.c | 38 ++++++++++++++++++-------------------- 1 file changed, 18 insertions(+), 20 deletions(-) diff --git a/linux-user/syscall.c b/linux-user/syscall.c index 9eb41a01ac..1dd7aad43c 100644 --- a/linux-user/syscall.c +++ b/linux-user/syscall.c @@ -580,6 +580,17 @@ extern int setfsuid(int); extern int setfsgid(int); extern int setgroups(int, gid_t *); +/* ARM EABI and MIPS expect 64bit types aligned even on pairs or registers */ +#ifdef TARGET_ARM +static inline int regpairs_aligned(void *cpu_env) { + return ((((CPUARMState *)cpu_env)->eabi) == 1) ; +} +#elif defined(TARGET_MIPS) +static inline int regpairs_aligned(void *cpu_env) { return 1; } +#else +static inline int regpairs_aligned(void *cpu_env) { return 0; } +#endif + #define ERRNO_TABLE_SIZE 1200 /* target_to_host_errno_table[] is initialized from @@ -4375,13 +4386,10 @@ static inline abi_long target_truncate64(void *cpu_env, const char *arg1, abi_long arg3, abi_long arg4) { -#ifdef TARGET_ARM - if (((CPUARMState *)cpu_env)->eabi) - { + if (regpairs_aligned(cpu_env)) { arg2 = arg3; arg3 = arg4; - } -#endif + } return get_errno(truncate64(arg1, target_offset64(arg2, arg3))); } #endif @@ -4392,13 +4400,10 @@ static inline abi_long target_ftruncate64(void *cpu_env, abi_long arg1, abi_long arg3, abi_long arg4) { -#ifdef TARGET_ARM - if (((CPUARMState *)cpu_env)->eabi) - { + if (regpairs_aligned(cpu_env)) { arg2 = arg3; arg3 = arg4; - } -#endif + } return get_errno(ftruncate64(arg1, target_offset64(arg2, arg3))); } #endif @@ -6857,20 +6862,16 @@ abi_long do_syscall(void *cpu_env, int num, abi_long arg1, #endif #ifdef TARGET_NR_pread case TARGET_NR_pread: -#ifdef TARGET_ARM - if (((CPUARMState *)cpu_env)->eabi) + if (regpairs_aligned(cpu_env)) arg4 = arg5; -#endif if (!(p = lock_user(VERIFY_WRITE, arg2, arg3, 0))) goto efault; ret = get_errno(pread(arg1, p, arg3, arg4)); unlock_user(p, arg2, ret); break; case TARGET_NR_pwrite: -#ifdef TARGET_ARM - if (((CPUARMState *)cpu_env)->eabi) + if (regpairs_aligned(cpu_env)) arg4 = arg5; -#endif if (!(p = lock_user(VERIFY_READ, arg2, arg3, 1))) goto efault; ret = get_errno(pwrite(arg1, p, arg3, arg4)); @@ -7621,14 +7622,11 @@ abi_long do_syscall(void *cpu_env, int num, abi_long arg1, #ifdef TARGET_NR_readahead case TARGET_NR_readahead: #if TARGET_ABI_BITS == 32 -#ifdef TARGET_ARM - if (((CPUARMState *)cpu_env)->eabi) - { + if (regpairs_aligned(cpu_env)) { arg2 = arg3; arg3 = arg4; arg4 = arg5; } -#endif ret = get_errno(readahead(arg1, ((off64_t)arg3 << 32) | arg2, arg4)); #else ret = get_errno(readahead(arg1, arg2, arg3)); From 6fea2ea462f4f726249f0e646012d21f24c024b5 Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Tue, 12 Jul 2011 21:27:15 +0100 Subject: [PATCH 100/209] linux-user/signal.c: Rename s390 target_ucontext fields to fix ia64 The ia64 sys/ucontext.h defines macros 'uc_link', 'uc_sigmask' and 'uc_stack'. Rename the s390 target_ucontext struct members to tuc_*, bringing them into line with the other targets and fixing a compile failure on ia64 hosts caused by this clash. Signed-off-by: Peter Maydell Signed-off-by: Riku Voipio --- linux-user/signal.c | 30 +++++++++++++++--------------- 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/linux-user/signal.c b/linux-user/signal.c index 7d168e100f..07ad07a58f 100644 --- a/linux-user/signal.c +++ b/linux-user/signal.c @@ -3662,11 +3662,11 @@ typedef struct { } sigframe; struct target_ucontext { - target_ulong uc_flags; - struct target_ucontext *uc_link; - target_stack_t uc_stack; - target_sigregs uc_mcontext; - target_sigset_t uc_sigmask; /* mask last for extensibility */ + target_ulong tuc_flags; + struct target_ucontext *tuc_link; + target_stack_t tuc_stack; + target_sigregs tuc_mcontext; + target_sigset_t tuc_sigmask; /* mask last for extensibility */ }; typedef struct { @@ -3814,16 +3814,16 @@ static void setup_rt_frame(int sig, struct target_sigaction *ka, } /* Create the ucontext. */ - __put_user(0, &frame->uc.uc_flags); - __put_user((abi_ulong)0, (abi_ulong *)&frame->uc.uc_link); - __put_user(target_sigaltstack_used.ss_sp, &frame->uc.uc_stack.ss_sp); + __put_user(0, &frame->uc.tuc_flags); + __put_user((abi_ulong)0, (abi_ulong *)&frame->uc.tuc_link); + __put_user(target_sigaltstack_used.ss_sp, &frame->uc.tuc_stack.ss_sp); __put_user(sas_ss_flags(get_sp_from_cpustate(env)), - &frame->uc.uc_stack.ss_flags); - __put_user(target_sigaltstack_used.ss_size, &frame->uc.uc_stack.ss_size); - save_sigregs(env, &frame->uc.uc_mcontext); + &frame->uc.tuc_stack.ss_flags); + __put_user(target_sigaltstack_used.ss_size, &frame->uc.tuc_stack.ss_size); + save_sigregs(env, &frame->uc.tuc_mcontext); for (i = 0; i < TARGET_NSIG_WORDS; i++) { __put_user((abi_ulong)set->sig[i], - (abi_ulong *)&frame->uc.uc_sigmask.sig[i]); + (abi_ulong *)&frame->uc.tuc_sigmask.sig[i]); } /* Set up to return from userspace. If provided, use a stub @@ -3928,15 +3928,15 @@ long do_rt_sigreturn(CPUState *env) if (!lock_user_struct(VERIFY_READ, frame, frame_addr, 1)) { goto badframe; } - target_to_host_sigset(&set, &frame->uc.uc_sigmask); + target_to_host_sigset(&set, &frame->uc.tuc_sigmask); sigprocmask(SIG_SETMASK, &set, NULL); /* ~_BLOCKABLE? */ - if (restore_sigregs(env, &frame->uc.uc_mcontext)) { + if (restore_sigregs(env, &frame->uc.tuc_mcontext)) { goto badframe; } - if (do_sigaltstack(frame_addr + offsetof(rt_sigframe, uc.uc_stack), 0, + if (do_sigaltstack(frame_addr + offsetof(rt_sigframe, uc.tuc_stack), 0, get_sp_from_cpustate(env)) == -EFAULT) { goto badframe; } From 4183f36df0bf8cb82dda423414aa3b1a82e91ce9 Mon Sep 17 00:00:00 2001 From: Tsuneo Saito Date: Thu, 14 Jul 2011 18:41:36 +0900 Subject: [PATCH 101/209] SPARC64: Implement ldfa/lddfa/ldqfa instructions properly This patch implements sparcv9 ldfa/lddfa/ldqfa instructions with non block-load ASIs. Signed-off-by: Tsuneo Saito Signed-off-by: Blue Swirl --- target-sparc/op_helper.c | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) diff --git a/target-sparc/op_helper.c b/target-sparc/op_helper.c index fd0cfbdb73..a75ac4f3d0 100644 --- a/target-sparc/op_helper.c +++ b/target-sparc/op_helper.c @@ -3331,7 +3331,7 @@ void helper_ldda_asi(target_ulong addr, int asi, int rd) void helper_ldf_asi(target_ulong addr, int asi, int size, int rd) { unsigned int i; - target_ulong val; + CPU_DoubleU u; helper_check_align(addr, 3); addr = asi_address_mask(env, asi, addr); @@ -3371,17 +3371,23 @@ void helper_ldf_asi(target_ulong addr, int asi, int size, int rd) break; } - val = helper_ld_asi(addr, asi, size, 0); switch(size) { default: case 4: - *((uint32_t *)&env->fpr[rd]) = val; + *((uint32_t *)&env->fpr[rd]) = helper_ld_asi(addr, asi, size, 0); break; case 8: - *((int64_t *)&DT0) = val; + u.ll = helper_ld_asi(addr, asi, size, 0); + *((uint32_t *)&env->fpr[rd++]) = u.l.upper; + *((uint32_t *)&env->fpr[rd++]) = u.l.lower; break; case 16: - // XXX + u.ll = helper_ld_asi(addr, asi, 8, 0); + *((uint32_t *)&env->fpr[rd++]) = u.l.upper; + *((uint32_t *)&env->fpr[rd++]) = u.l.lower; + u.ll = helper_ld_asi(addr + 8, asi, 8, 0); + *((uint32_t *)&env->fpr[rd++]) = u.l.upper; + *((uint32_t *)&env->fpr[rd++]) = u.l.lower; break; } } From 8872eb4f567b9ae36dbd9c320f6a86c53a776d43 Mon Sep 17 00:00:00 2001 From: Tsuneo Saito Date: Thu, 14 Jul 2011 18:41:37 +0900 Subject: [PATCH 102/209] SPARC64: fp_disabled checks on ldfa/lddfa/ldqfa ldfa/lddfa/ldqfa instructions should raise fp_disabled exceptions if %pstate.PEF==0 or %fprs.FEF==0. Signed-off-by: Tsuneo Saito Signed-off-by: Blue Swirl --- target-sparc/translate.c | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/target-sparc/translate.c b/target-sparc/translate.c index f32a674f35..1e7e68d397 100644 --- a/target-sparc/translate.c +++ b/target-sparc/translate.c @@ -4484,10 +4484,16 @@ static void disas_sparc_insn(DisasContext * dc) case 0x2d: /* V9 prefetch, no effect */ goto skip_move; case 0x30: /* V9 ldfa */ + if (gen_trap_ifnofpu(dc, cpu_cond)) { + goto jmp_insn; + } save_state(dc, cpu_cond); gen_ldf_asi(cpu_addr, insn, 4, rd); goto skip_move; case 0x33: /* V9 lddfa */ + if (gen_trap_ifnofpu(dc, cpu_cond)) { + goto jmp_insn; + } save_state(dc, cpu_cond); gen_ldf_asi(cpu_addr, insn, 8, DFPREG(rd)); goto skip_move; @@ -4495,6 +4501,9 @@ static void disas_sparc_insn(DisasContext * dc) goto skip_move; case 0x32: /* V9 ldqfa */ CHECK_FPU_FEATURE(dc, FLOAT128); + if (gen_trap_ifnofpu(dc, cpu_cond)) { + goto jmp_insn; + } save_state(dc, cpu_cond); gen_ldf_asi(cpu_addr, insn, 16, QFPREG(rd)); goto skip_move; From e1ef36c4a34082a075182ec0ba44c80a2dbf43c2 Mon Sep 17 00:00:00 2001 From: Tsuneo Saito Date: Thu, 14 Jul 2011 18:41:38 +0900 Subject: [PATCH 103/209] SPARC64: Implement stfa/stdfa/stqfa instrcutions properly This patch implements sparcv9 stfa/stdfa/stqfa instructions with non block-store ASIs. Signed-off-by: Tsuneo Saito Signed-off-by: Blue Swirl --- target-sparc/op_helper.c | 15 +++++++++++---- target-sparc/translate.c | 2 -- 2 files changed, 11 insertions(+), 6 deletions(-) diff --git a/target-sparc/op_helper.c b/target-sparc/op_helper.c index a75ac4f3d0..fe71829ecc 100644 --- a/target-sparc/op_helper.c +++ b/target-sparc/op_helper.c @@ -3396,6 +3396,7 @@ void helper_stf_asi(target_ulong addr, int asi, int size, int rd) { unsigned int i; target_ulong val = 0; + CPU_DoubleU u; helper_check_align(addr, 3); addr = asi_address_mask(env, asi, addr); @@ -3440,16 +3441,22 @@ void helper_stf_asi(target_ulong addr, int asi, int size, int rd) switch(size) { default: case 4: - val = *((uint32_t *)&env->fpr[rd]); + helper_st_asi(addr, *(uint32_t *)&env->fpr[rd], asi, size); break; case 8: - val = *((int64_t *)&DT0); + u.l.upper = *(uint32_t *)&env->fpr[rd++]; + u.l.lower = *(uint32_t *)&env->fpr[rd++]; + helper_st_asi(addr, u.ll, asi, size); break; case 16: - // XXX + u.l.upper = *(uint32_t *)&env->fpr[rd++]; + u.l.lower = *(uint32_t *)&env->fpr[rd++]; + helper_st_asi(addr, u.ll, asi, 8); + u.l.upper = *(uint32_t *)&env->fpr[rd++]; + u.l.lower = *(uint32_t *)&env->fpr[rd++]; + helper_st_asi(addr + 8, u.ll, asi, 8); break; } - helper_st_asi(addr, val, asi, size); } target_ulong helper_cas_asi(target_ulong addr, target_ulong val1, diff --git a/target-sparc/translate.c b/target-sparc/translate.c index 1e7e68d397..a5a8eaf308 100644 --- a/target-sparc/translate.c +++ b/target-sparc/translate.c @@ -4742,12 +4742,10 @@ static void disas_sparc_insn(DisasContext * dc) r_const = tcg_const_i32(7); gen_helper_check_align(cpu_addr, r_const); tcg_temp_free_i32(r_const); - gen_op_load_fpr_QT0(QFPREG(rd)); gen_stf_asi(cpu_addr, insn, 16, QFPREG(rd)); } break; case 0x37: /* V9 stdfa */ - gen_op_load_fpr_DT0(DFPREG(rd)); gen_stf_asi(cpu_addr, insn, 8, DFPREG(rd)); break; case 0x3c: /* V9 casa */ From 5f06b54718b063840e14a93cd1059c3a92e4603a Mon Sep 17 00:00:00 2001 From: Tsuneo Saito Date: Thu, 14 Jul 2011 18:41:39 +0900 Subject: [PATCH 104/209] SPARC64: fp_disabled checks on stfa/stdfa/stqfa stfa/stdfa/stqfa instructions should raise fp_disabled exceptions if %pstate.PEF==0 or %fprs.FEF==0. Signed-off-by: Tsuneo Saito Signed-off-by: Blue Swirl --- target-sparc/translate.c | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/target-sparc/translate.c b/target-sparc/translate.c index a5a8eaf308..5f92dbb819 100644 --- a/target-sparc/translate.c +++ b/target-sparc/translate.c @@ -4732,6 +4732,9 @@ static void disas_sparc_insn(DisasContext * dc) switch (xop) { #ifdef TARGET_SPARC64 case 0x34: /* V9 stfa */ + if (gen_trap_ifnofpu(dc, cpu_cond)) { + goto jmp_insn; + } gen_stf_asi(cpu_addr, insn, 4, rd); break; case 0x36: /* V9 stqfa */ @@ -4739,6 +4742,9 @@ static void disas_sparc_insn(DisasContext * dc) TCGv_i32 r_const; CHECK_FPU_FEATURE(dc, FLOAT128); + if (gen_trap_ifnofpu(dc, cpu_cond)) { + goto jmp_insn; + } r_const = tcg_const_i32(7); gen_helper_check_align(cpu_addr, r_const); tcg_temp_free_i32(r_const); @@ -4746,6 +4752,9 @@ static void disas_sparc_insn(DisasContext * dc) } break; case 0x37: /* V9 stdfa */ + if (gen_trap_ifnofpu(dc, cpu_cond)) { + goto jmp_insn; + } gen_stf_asi(cpu_addr, insn, 8, DFPREG(rd)); break; case 0x3c: /* V9 casa */ From 41317e2e2b58426ac9a5e1c3083c81840da3ae2b Mon Sep 17 00:00:00 2001 From: Tsuneo Saito Date: Thu, 14 Jul 2011 18:41:40 +0900 Subject: [PATCH 105/209] SPARC64: Add UA2007 ASI_BLK_AIU[PS]L? ASIs for ldfa Support UA2007 block load ASIs for ldfa instructions. Signed-off-by: Tsuneo Saito Signed-off-by: Blue Swirl --- target-sparc/op_helper.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/target-sparc/op_helper.c b/target-sparc/op_helper.c index fe71829ecc..b76ffb6ff0 100644 --- a/target-sparc/op_helper.c +++ b/target-sparc/op_helper.c @@ -3353,6 +3353,10 @@ void helper_ldf_asi(target_ulong addr, int asi, int size, int rd) } return; + case 0x16: /* UA2007 Block load primary, user privilege */ + case 0x17: /* UA2007 Block load secondary, user privilege */ + case 0x1e: /* UA2007 Block load primary LE, user privilege */ + case 0x1f: /* UA2007 Block load secondary LE, user privilege */ case 0x70: // Block load primary, user privilege case 0x71: // Block load secondary, user privilege if (rd & 7) { @@ -3361,7 +3365,7 @@ void helper_ldf_asi(target_ulong addr, int asi, int size, int rd) } helper_check_align(addr, 0x3f); for (i = 0; i < 16; i++) { - *(uint32_t *)&env->fpr[rd++] = helper_ld_asi(addr, asi & 0x1f, 4, + *(uint32_t *)&env->fpr[rd++] = helper_ld_asi(addr, asi & 0x19, 4, 0); addr += 4; } From 073a0444104767cce1668799476eb882f8badf59 Mon Sep 17 00:00:00 2001 From: Tsuneo Saito Date: Thu, 14 Jul 2011 18:41:41 +0900 Subject: [PATCH 106/209] SPARC64: Add UA2007 ASI_BLK_AIU[PS]L? ASIs for stfa Support UA2007 block store ASIs for stfa instructions. Signed-off-by: Tsuneo Saito Signed-off-by: Blue Swirl --- target-sparc/op_helper.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/target-sparc/op_helper.c b/target-sparc/op_helper.c index b76ffb6ff0..4faa7094a5 100644 --- a/target-sparc/op_helper.c +++ b/target-sparc/op_helper.c @@ -3424,6 +3424,10 @@ void helper_stf_asi(target_ulong addr, int asi, int size, int rd) } return; + case 0x16: /* UA2007 Block load primary, user privilege */ + case 0x17: /* UA2007 Block load secondary, user privilege */ + case 0x1e: /* UA2007 Block load primary LE, user privilege */ + case 0x1f: /* UA2007 Block load secondary LE, user privilege */ case 0x70: // Block store primary, user privilege case 0x71: // Block store secondary, user privilege if (rd & 7) { @@ -3433,7 +3437,7 @@ void helper_stf_asi(target_ulong addr, int asi, int size, int rd) helper_check_align(addr, 0x3f); for (i = 0; i < 16; i++) { val = *(uint32_t *)&env->fpr[rd++]; - helper_st_asi(addr, val, asi & 0x1f, 4); + helper_st_asi(addr, val, asi & 0x19, 4); addr += 4; } From d920bde9235f718f62e73f06be1922b36913f800 Mon Sep 17 00:00:00 2001 From: Tsuneo Saito Date: Thu, 14 Jul 2011 18:41:42 +0900 Subject: [PATCH 107/209] SPARC64: Add JPS1 ASI_BLK_AIU[PS]L ASIs for ldfa and stfa Support JPS1 little endian block transfer ASIs. Signed-off-by: Tsuneo Saito Signed-off-by: Blue Swirl --- target-sparc/op_helper.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/target-sparc/op_helper.c b/target-sparc/op_helper.c index 4faa7094a5..2a28d5f43c 100644 --- a/target-sparc/op_helper.c +++ b/target-sparc/op_helper.c @@ -3359,6 +3359,8 @@ void helper_ldf_asi(target_ulong addr, int asi, int size, int rd) case 0x1f: /* UA2007 Block load secondary LE, user privilege */ case 0x70: // Block load primary, user privilege case 0x71: // Block load secondary, user privilege + case 0x78: /* JPS1 Block load primary LE, user privilege */ + case 0x79: /* JPS1 Block load secondary LE, user privilege */ if (rd & 7) { raise_exception(TT_ILL_INSN); return; @@ -3430,6 +3432,8 @@ void helper_stf_asi(target_ulong addr, int asi, int size, int rd) case 0x1f: /* UA2007 Block load secondary LE, user privilege */ case 0x70: // Block store primary, user privilege case 0x71: // Block store secondary, user privilege + case 0x78: /* JPS1 Block load primary LE, user privilege */ + case 0x79: /* JPS1 Block load secondary LE, user privilege */ if (rd & 7) { raise_exception(TT_ILL_INSN); return; From d8e586ffccd73c9c7758166241564c63109fc8d3 Mon Sep 17 00:00:00 2001 From: Tsuneo Saito Date: Thu, 14 Jul 2011 18:41:43 +0900 Subject: [PATCH 108/209] SPARC64: C99 comment fix for block-transfer ASIs Fixed C99 comments on block-tranfer ASIs. Signed-off-by: Tsuneo Saito Signed-off-by: Blue Swirl --- target-sparc/op_helper.c | 28 ++++++++++++++-------------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/target-sparc/op_helper.c b/target-sparc/op_helper.c index 2a28d5f43c..15af27ba1f 100644 --- a/target-sparc/op_helper.c +++ b/target-sparc/op_helper.c @@ -3337,10 +3337,10 @@ void helper_ldf_asi(target_ulong addr, int asi, int size, int rd) addr = asi_address_mask(env, asi, addr); switch (asi) { - case 0xf0: // Block load primary - case 0xf1: // Block load secondary - case 0xf8: // Block load primary LE - case 0xf9: // Block load secondary LE + case 0xf0: /* UA2007/JPS1 Block load primary */ + case 0xf1: /* UA2007/JPS1 Block load secondary */ + case 0xf8: /* UA2007/JPS1 Block load primary LE */ + case 0xf9: /* UA2007/JPS1 Block load secondary LE */ if (rd & 7) { raise_exception(TT_ILL_INSN); return; @@ -3357,8 +3357,8 @@ void helper_ldf_asi(target_ulong addr, int asi, int size, int rd) case 0x17: /* UA2007 Block load secondary, user privilege */ case 0x1e: /* UA2007 Block load primary LE, user privilege */ case 0x1f: /* UA2007 Block load secondary LE, user privilege */ - case 0x70: // Block load primary, user privilege - case 0x71: // Block load secondary, user privilege + case 0x70: /* JPS1 Block load primary, user privilege */ + case 0x71: /* JPS1 Block load secondary, user privilege */ case 0x78: /* JPS1 Block load primary LE, user privilege */ case 0x79: /* JPS1 Block load secondary LE, user privilege */ if (rd & 7) { @@ -3408,12 +3408,12 @@ void helper_stf_asi(target_ulong addr, int asi, int size, int rd) addr = asi_address_mask(env, asi, addr); switch (asi) { - case 0xe0: // UA2007 Block commit store primary (cache flush) - case 0xe1: // UA2007 Block commit store secondary (cache flush) - case 0xf0: // Block store primary - case 0xf1: // Block store secondary - case 0xf8: // Block store primary LE - case 0xf9: // Block store secondary LE + case 0xe0: /* UA2007/JPS1 Block commit store primary (cache flush) */ + case 0xe1: /* UA2007/JPS1 Block commit store secondary (cache flush) */ + case 0xf0: /* UA2007/JPS1 Block store primary */ + case 0xf1: /* UA2007/JPS1 Block store secondary */ + case 0xf8: /* UA2007/JPS1 Block store primary LE */ + case 0xf9: /* UA2007/JPS1 Block store secondary LE */ if (rd & 7) { raise_exception(TT_ILL_INSN); return; @@ -3430,8 +3430,8 @@ void helper_stf_asi(target_ulong addr, int asi, int size, int rd) case 0x17: /* UA2007 Block load secondary, user privilege */ case 0x1e: /* UA2007 Block load primary LE, user privilege */ case 0x1f: /* UA2007 Block load secondary LE, user privilege */ - case 0x70: // Block store primary, user privilege - case 0x71: // Block store secondary, user privilege + case 0x70: /* JPS1 Block store primary, user privilege */ + case 0x71: /* JPS1 Block store secondary, user privilege */ case 0x78: /* JPS1 Block load primary LE, user privilege */ case 0x79: /* JPS1 Block load secondary LE, user privilege */ if (rd & 7) { From f838e2c535e47dc8e99ae7ec711407b849700cc9 Mon Sep 17 00:00:00 2001 From: Blue Swirl Date: Thu, 14 Jul 2011 17:30:43 +0000 Subject: [PATCH 109/209] Sparc: fix FPU and AM enable checks for translation Translation used incorrectly CPUState fields directly to check for FPU enable state and 32 bit address masking on Sparc64. Fix by using TB flags instead. Signed-off-by: Blue Swirl --- target-sparc/cpu.h | 49 +++++++++++++++++++++++++++------------- target-sparc/translate.c | 9 ++------ 2 files changed, 35 insertions(+), 23 deletions(-) diff --git a/target-sparc/cpu.h b/target-sparc/cpu.h index 4edae78ca3..22ee2743d7 100644 --- a/target-sparc/cpu.h +++ b/target-sparc/cpu.h @@ -606,17 +606,6 @@ static inline int cpu_pil_allowed(CPUState *env1, int pil) #endif } -static inline int cpu_fpu_enabled(CPUState *env1) -{ -#if defined(CONFIG_USER_ONLY) - return 1; -#elif !defined(TARGET_SPARC64) - return env1->psref; -#else - return ((env1->pstate & PS_PEF) != 0) && ((env1->fprs & FPRS_FEF) != 0); -#endif -} - #if defined(CONFIG_USER_ONLY) static inline void cpu_clone_regs(CPUState *env, target_ulong newsp) { @@ -639,6 +628,9 @@ void cpu_tick_set_limit(CPUTimer *timer, uint64_t limit); trap_state* cpu_tsptr(CPUState* env); #endif +#define TB_FLAG_FPU_ENABLED (1 << 4) +#define TB_FLAG_AM_ENABLED (1 << 5) + static inline void cpu_get_tb_cpu_state(CPUState *env, target_ulong *pc, target_ulong *cs_base, int *flags) { @@ -646,16 +638,41 @@ static inline void cpu_get_tb_cpu_state(CPUState *env, target_ulong *pc, *cs_base = env->npc; #ifdef TARGET_SPARC64 // AM . Combined FPU enable bits . PRIV . DMMU enabled . IMMU enabled - *flags = ((env->pstate & PS_AM) << 2) /* 5 */ - | (((env->pstate & PS_PEF) >> 1) /* 3 */ - | ((env->fprs & FPRS_FEF) << 2)) /* 4 */ - | (env->pstate & PS_PRIV) /* 2 */ + *flags = (env->pstate & PS_PRIV) /* 2 */ | ((env->lsu & (DMMU_E | IMMU_E)) >> 2) /* 1, 0 */ | ((env->tl & 0xff) << 8) | (env->dmmu.mmu_primary_context << 16); /* 16... */ + if (env->pstate & PS_AM) { + *flags |= TB_FLAG_AM_ENABLED; + } + if ((env->def->features & CPU_FEATURE_FLOAT) && (env->pstate & PS_PEF) + && (env->fprs & FPRS_FEF)) { + *flags |= TB_FLAG_FPU_ENABLED; + } #else // FPU enable . Supervisor - *flags = (env->psref << 4) | env->psrs; + *flags = env->psrs; + if ((env->def->features & CPU_FEATURE_FLOAT) && env->psref) { + *flags |= TB_FLAG_FPU_ENABLED; + } +#endif +} + +static inline bool tb_fpu_enabled(int tb_flags) +{ +#if defined(CONFIG_USER_ONLY) + return true; +#else + return tb_flags & TB_FLAG_FPU_ENABLED; +#endif +} + +static inline bool tb_am_enabled(int tb_flags) +{ +#ifndef TARGET_SPARC64 + return false; +#else + return tb_flags & TB_FLAG_AM_ENABLED; #endif } diff --git a/target-sparc/translate.c b/target-sparc/translate.c index 5f92dbb819..27c2cf98e8 100644 --- a/target-sparc/translate.c +++ b/target-sparc/translate.c @@ -4879,13 +4879,8 @@ static inline void gen_intermediate_code_internal(TranslationBlock * tb, dc->cc_op = CC_OP_DYNAMIC; dc->mem_idx = cpu_mmu_index(env); dc->def = env->def; - if ((dc->def->features & CPU_FEATURE_FLOAT)) - dc->fpu_enabled = cpu_fpu_enabled(env); - else - dc->fpu_enabled = 0; -#ifdef TARGET_SPARC64 - dc->address_mask_32bit = env->pstate & PS_AM; -#endif + dc->fpu_enabled = tb_fpu_enabled(tb->flags); + dc->address_mask_32bit = tb_am_enabled(tb->flags); dc->singlestep = (env->singlestep_enabled || singlestep); gen_opc_end = gen_opc_buf + OPC_MAX_SIZE; From cf973e469bd9d47c0460347764c1315a6180e13c Mon Sep 17 00:00:00 2001 From: Artyom Tarasenko Date: Thu, 14 Jul 2011 19:37:06 +0200 Subject: [PATCH 110/209] set ELF_HWCAP for SPARC and SPARC64 setting ELF_HWCAP fixes dynamic library loading for Linux/sparc64 This patch allows loading busybox from Debian 6 initrd Signed-off-by: Artyom Tarasenko Signed-off-by: Blue Swirl --- linux-user/elfload.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/linux-user/elfload.c b/linux-user/elfload.c index b2746f2558..443d246ada 100644 --- a/linux-user/elfload.c +++ b/linux-user/elfload.c @@ -417,7 +417,8 @@ static void elf_core_copy_regs(target_elf_gregset_t *regs, const CPUState *env) #ifdef TARGET_SPARC64 #define ELF_START_MMAP 0x80000000 - +#define ELF_HWCAP (HWCAP_SPARC_FLUSH | HWCAP_SPARC_STBAR | HWCAP_SPARC_SWAP \ + | HWCAP_SPARC_MULDIV | HWCAP_SPARC_V9) #ifndef TARGET_ABI32 #define elf_check_arch(x) ( (x) == EM_SPARCV9 || (x) == EM_SPARC32PLUS ) #else @@ -450,7 +451,8 @@ static inline void init_thread(struct target_pt_regs *regs, #else #define ELF_START_MMAP 0x80000000 - +#define ELF_HWCAP (HWCAP_SPARC_FLUSH | HWCAP_SPARC_STBAR | HWCAP_SPARC_SWAP \ + | HWCAP_SPARC_MULDIV) #define elf_check_arch(x) ( (x) == EM_SPARC ) #define ELF_CLASS ELFCLASS32 From 107a47cc2dac1b6c5edae0121831713161cb70c5 Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Wed, 22 Jun 2011 15:40:06 +0100 Subject: [PATCH 111/209] tcg/README: Expand advice on number of TCG ops per target insn Expand the note on the number of TCG ops generated per target insn, to be clearer about the range of applicability of the 20 op rule of thumb. Also add a note about the hard MAX_OP_PER_INSTR limit. Signed-off-by: Peter Maydell Signed-off-by: Blue Swirl --- tcg/README | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/tcg/README b/tcg/README index 660012281f..cfdfd96d09 100644 --- a/tcg/README +++ b/tcg/README @@ -504,7 +504,15 @@ register. - Don't hesitate to use helpers for complicated or seldom used target instructions. There is little performance advantage in using TCG to implement target instructions taking more than about twenty TCG - instructions. + instructions. Note that this rule of thumb is more applicable to + helpers doing complex logic or arithmetic, where the C compiler has + scope to do a good job of optimisation; it is less relevant where + the instruction is mostly doing loads and stores, and in those cases + inline TCG may still be faster for longer sequences. + +- The hard limit on the number of TCG instructions you can generate + per target instruction is set by MAX_OP_PER_INSTR in exec-all.h -- + you cannot exceed this without risking a buffer overrun. - Use the 'discard' instruction if you know that TCG won't be able to prove that a given global is "dead" at a given program point. The From 5664aed99a4ab6e661b40e475449a32150f46184 Mon Sep 17 00:00:00 2001 From: Alexandre Raymond Date: Tue, 14 Jun 2011 10:05:36 -0400 Subject: [PATCH 112/209] Fix signal handling when io-thread is disabled Changes since v1: - take pthread_sigmask() out of the ifdef as it is now common to both parts. This fix effectively blocks, in the main thread, the signals handled by signalfd or the compatibility signal thread. This way, such signals are received synchronously in the main thread through sigfd_handler() instead of triggering the signal handler directly, asynchronously. Signed-off-by: Alexandre Raymond Acked-by: Jan Kiszka Signed-off-by: Blue Swirl --- cpus.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cpus.c b/cpus.c index abd24ab31c..e02576cb66 100644 --- a/cpus.c +++ b/cpus.c @@ -401,7 +401,6 @@ static int qemu_signal_init(void) sigaddset(&set, SIGALRM); sigaddset(&set, SIG_IPI); sigaddset(&set, SIGBUS); - pthread_sigmask(SIG_BLOCK, &set, NULL); #else sigemptyset(&set); sigaddset(&set, SIGBUS); @@ -414,6 +413,7 @@ static int qemu_signal_init(void) sigaddset(&set, SIGALRM); } #endif + pthread_sigmask(SIG_BLOCK, &set, NULL); sigfd = qemu_signalfd(&set); if (sigfd == -1) { From 89b9ba661bd2d6155308f895ec075d813f0e129b Mon Sep 17 00:00:00 2001 From: Alexandre Raymond Date: Wed, 15 Jun 2011 01:20:31 -0400 Subject: [PATCH 113/209] Fix signal handling of SIG_IPI when io-thread is enabled Both the signal thread (via sigwait()) and the cpu thread (via a normal signal handler) were attempting to catch SIG_IPI. This resulted in random freezes under Darwin. This patch separates SIG_IPI from the rest of the signals handled by the signal thread, because it is independently caught by the cpu thread. Signed-off-by: Alexandre Raymond Acked-by: Jan Kiszka Signed-off-by: Blue Swirl --- cpus.c | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/cpus.c b/cpus.c index e02576cb66..3035314486 100644 --- a/cpus.c +++ b/cpus.c @@ -396,10 +396,18 @@ static int qemu_signal_init(void) sigaddset(&set, SIGUSR2); pthread_sigmask(SIG_UNBLOCK, &set, NULL); + /* + * SIG_IPI must be blocked in the main thread and must not be caught + * by sigwait() in the signal thread. Otherwise, the cpu thread will + * not catch it reliably. + */ + sigemptyset(&set); + sigaddset(&set, SIG_IPI); + pthread_sigmask(SIG_BLOCK, &set, NULL); + sigemptyset(&set); sigaddset(&set, SIGIO); sigaddset(&set, SIGALRM); - sigaddset(&set, SIG_IPI); sigaddset(&set, SIGBUS); #else sigemptyset(&set); From 6dbd588a412ce1ac2a4ba640b418278e92a71890 Mon Sep 17 00:00:00 2001 From: Jan Kiszka Date: Tue, 21 Jun 2011 22:59:07 +0200 Subject: [PATCH 114/209] xen: Clean up build system Introduce CONFIG_XEN_BACKEND so that this new config solely controls the target-independent backend build and CONFIG_XEN can focus on per-target building. Signed-off-by: Jan Kiszka Signed-off-by: Alexander Graf --- Makefile.objs | 4 ++-- Makefile.target | 13 +++---------- configure | 2 +- 3 files changed, 6 insertions(+), 13 deletions(-) diff --git a/Makefile.objs b/Makefile.objs index cea15e4a82..1635df6e2f 100644 --- a/Makefile.objs +++ b/Makefile.objs @@ -155,8 +155,8 @@ slirp-obj-y += tcp_subr.o tcp_timer.o udp.o bootp.o tftp.o common-obj-$(CONFIG_SLIRP) += $(addprefix slirp/, $(slirp-obj-y)) # xen backend driver support -common-obj-$(CONFIG_XEN) += xen_backend.o xen_devconfig.o -common-obj-$(CONFIG_XEN) += xen_console.o xenfb.o xen_disk.o xen_nic.o +common-obj-$(CONFIG_XEN_BACKEND) += xen_backend.o xen_devconfig.o +common-obj-$(CONFIG_XEN_BACKEND) += xen_console.o xenfb.o xen_disk.o xen_nic.o ###################################################################### # libuser diff --git a/Makefile.target b/Makefile.target index a53a2ff6ed..c347176c89 100644 --- a/Makefile.target +++ b/Makefile.target @@ -3,6 +3,8 @@ GENERATED_HEADERS = config-target.h CONFIG_NO_PCI = $(if $(subst n,,$(CONFIG_PCI)),n,y) CONFIG_NO_KVM = $(if $(subst n,,$(CONFIG_KVM)),n,y) +CONFIG_NO_XEN = $(if $(subst n,,$(CONFIG_XEN)),n,y) +CONFIG_NO_XEN_MAPCACHE = $(if $(subst n,,$(CONFIG_XEN_MAPCACHE)),n,y) include ../config-host.mak include config-devices.mak @@ -204,17 +206,8 @@ QEMU_CFLAGS += $(VNC_SASL_CFLAGS) QEMU_CFLAGS += $(VNC_JPEG_CFLAGS) QEMU_CFLAGS += $(VNC_PNG_CFLAGS) -# xen backend driver support -obj-i386-$(CONFIG_XEN) += xen_machine_pv.o xen_domainbuild.o - -ifeq ($(TARGET_BASE_ARCH), i386) - CONFIG_NO_XEN = $(if $(subst n,,$(CONFIG_XEN)),n,y) -else - CONFIG_NO_XEN = y -endif # xen support -CONFIG_NO_XEN_MAPCACHE = $(if $(subst n,,$(CONFIG_XEN_MAPCACHE)),n,y) -obj-i386-$(CONFIG_XEN) += xen-all.o +obj-$(CONFIG_XEN) += xen-all.o xen_machine_pv.o xen_domainbuild.o obj-$(CONFIG_NO_XEN) += xen-stub.o obj-i386-$(CONFIG_XEN_MAPCACHE) += xen-mapcache.o obj-$(CONFIG_NO_XEN_MAPCACHE) += xen-mapcache-stub.o diff --git a/configure b/configure index 88159acde2..e57efb179c 100755 --- a/configure +++ b/configure @@ -2850,7 +2850,7 @@ if test "$bluez" = "yes" ; then echo "BLUEZ_CFLAGS=$bluez_cflags" >> $config_host_mak fi if test "$xen" = "yes" ; then - echo "CONFIG_XEN=y" >> $config_host_mak + echo "CONFIG_XEN_BACKEND=y" >> $config_host_mak echo "CONFIG_XEN_CTRL_INTERFACE_VERSION=$xen_ctrl_version" >> $config_host_mak fi if test "$io_thread" = "yes" ; then From e41d7c691ad7f4ad78573f72048eecb16f78974a Mon Sep 17 00:00:00 2001 From: Jan Kiszka Date: Tue, 21 Jun 2011 22:59:08 +0200 Subject: [PATCH 115/209] xen: Clean up map cache API naming The map cache is a Xen thing, so its API should make this clear. Signed-off-by: Jan Kiszka Signed-off-by: Alexander Graf --- exec.c | 18 ++++++++++-------- trace-events | 6 +++--- xen-all.c | 4 ++-- xen-mapcache-stub.c | 11 ++++++----- xen-mapcache.c | 41 +++++++++++++++++++++++------------------ xen-mapcache.h | 14 ++++++-------- 6 files changed, 50 insertions(+), 44 deletions(-) diff --git a/exec.c b/exec.c index f1777e6239..50bccd82d0 100644 --- a/exec.c +++ b/exec.c @@ -3020,7 +3020,7 @@ void qemu_ram_free(ram_addr_t addr) munmap(block->host, block->length); #else if (xen_mapcache_enabled()) { - qemu_invalidate_entry(block->host); + xen_invalidate_map_cache_entry(block->host); } else { qemu_vfree(block->host); } @@ -3118,9 +3118,10 @@ void *qemu_get_ram_ptr(ram_addr_t addr) * In that case just map until the end of the page. */ if (block->offset == 0) { - return qemu_map_cache(addr, 0, 0); + return xen_map_cache(addr, 0, 0); } else if (block->host == NULL) { - block->host = qemu_map_cache(block->offset, block->length, 1); + block->host = + xen_map_cache(block->offset, block->length, 1); } } return block->host + (addr - block->offset); @@ -3148,9 +3149,10 @@ void *qemu_safe_ram_ptr(ram_addr_t addr) * In that case just map until the end of the page. */ if (block->offset == 0) { - return qemu_map_cache(addr, 0, 0); + return xen_map_cache(addr, 0, 0); } else if (block->host == NULL) { - block->host = qemu_map_cache(block->offset, block->length, 1); + block->host = + xen_map_cache(block->offset, block->length, 1); } } return block->host + (addr - block->offset); @@ -3168,7 +3170,7 @@ void *qemu_safe_ram_ptr(ram_addr_t addr) void *qemu_ram_ptr_length(target_phys_addr_t addr, target_phys_addr_t *size) { if (xen_mapcache_enabled()) - return qemu_map_cache(addr, *size, 1); + return xen_map_cache(addr, *size, 1); else { RAMBlock *block; @@ -3199,7 +3201,7 @@ int qemu_ram_addr_from_host(void *ptr, ram_addr_t *ram_addr) uint8_t *host = ptr; if (xen_mapcache_enabled()) { - *ram_addr = qemu_ram_addr_from_mapcache(ptr); + *ram_addr = xen_ram_addr_from_mapcache(ptr); return 0; } @@ -4114,7 +4116,7 @@ void cpu_physical_memory_unmap(void *buffer, target_phys_addr_t len, } } if (xen_mapcache_enabled()) { - qemu_invalidate_entry(buffer); + xen_invalidate_map_cache_entry(buffer); } return; } diff --git a/trace-events b/trace-events index bebf612fe9..2372385282 100644 --- a/trace-events +++ b/trace-events @@ -399,9 +399,9 @@ disable xen_ram_alloc(unsigned long ram_addr, unsigned long size) "requested: %# disable xen_client_set_memory(uint64_t start_addr, unsigned long size, unsigned long phys_offset, bool log_dirty) "%#"PRIx64" size %#lx, offset %#lx, log_dirty %i" # xen-mapcache.c -disable qemu_map_cache(uint64_t phys_addr) "want %#"PRIx64"" -disable qemu_remap_bucket(uint64_t index) "index %#"PRIx64"" -disable qemu_map_cache_return(void* ptr) "%p" +disable xen_map_cache(uint64_t phys_addr) "want %#"PRIx64"" +disable xen_remap_bucket(uint64_t index) "index %#"PRIx64"" +disable xen_map_cache_return(void* ptr) "%p" disable xen_map_block(uint64_t phys_addr, uint64_t size) "%#"PRIx64", size %#"PRIx64"" disable xen_unmap_block(void* addr, unsigned long size) "%p, size %#lx" diff --git a/xen-all.c b/xen-all.c index fcb106f815..4827d6a7ba 100644 --- a/xen-all.c +++ b/xen-all.c @@ -644,7 +644,7 @@ static void handle_ioreq(ioreq_t *req) case IOREQ_TYPE_TIMEOFFSET: break; case IOREQ_TYPE_INVALIDATE: - qemu_invalidate_map_cache(); + xen_invalidate_map_cache(); break; default: hw_error("Invalid ioreq type 0x%x\n", req->type); @@ -852,7 +852,7 @@ int xen_hvm_init(void) } /* Init RAM management */ - qemu_map_cache_init(); + xen_map_cache_init(); xen_ram_init(ram_size); qemu_add_vm_change_state_handler(xen_vm_change_state_handler, state); diff --git a/xen-mapcache-stub.c b/xen-mapcache-stub.c index 90a994dc2c..f0370168e1 100644 --- a/xen-mapcache-stub.c +++ b/xen-mapcache-stub.c @@ -13,24 +13,25 @@ #include "cpu-common.h" #include "xen-mapcache.h" -void qemu_map_cache_init(void) +void xen_map_cache_init(void) { } -uint8_t *qemu_map_cache(target_phys_addr_t phys_addr, target_phys_addr_t size, uint8_t lock) +uint8_t *xen_map_cache(target_phys_addr_t phys_addr, target_phys_addr_t size, + uint8_t lock) { return qemu_get_ram_ptr(phys_addr); } -ram_addr_t qemu_ram_addr_from_mapcache(void *ptr) +ram_addr_t xen_ram_addr_from_mapcache(void *ptr) { return -1; } -void qemu_invalidate_map_cache(void) +void xen_invalidate_map_cache(void) { } -void qemu_invalidate_entry(uint8_t *buffer) +void xen_invalidate_map_cache_entry(uint8_t *buffer) { } diff --git a/xen-mapcache.c b/xen-mapcache.c index fac47cd9be..007136af26 100644 --- a/xen-mapcache.c +++ b/xen-mapcache.c @@ -40,6 +40,9 @@ #endif #define MCACHE_BUCKET_SIZE (1UL << MCACHE_BUCKET_SHIFT) +#define mapcache_lock() ((void)0) +#define mapcache_unlock() ((void)0) + typedef struct MapCacheEntry { target_phys_addr_t paddr_index; uint8_t *vaddr_base; @@ -79,7 +82,7 @@ static inline int test_bits(int nr, int size, const unsigned long *addr) return 0; } -void qemu_map_cache_init(void) +void xen_map_cache_init(void) { unsigned long size; struct rlimit rlimit_as; @@ -106,13 +109,14 @@ void qemu_map_cache_init(void) size = mapcache->nr_buckets * sizeof (MapCacheEntry); size = (size + XC_PAGE_SIZE - 1) & ~(XC_PAGE_SIZE - 1); - DPRINTF("qemu_map_cache_init, nr_buckets = %lx size %lu\n", mapcache->nr_buckets, size); + DPRINTF("%s, nr_buckets = %lx size %lu\n", __func__, + mapcache->nr_buckets, size); mapcache->entry = qemu_mallocz(size); } -static void qemu_remap_bucket(MapCacheEntry *entry, - target_phys_addr_t size, - target_phys_addr_t address_index) +static void xen_remap_bucket(MapCacheEntry *entry, + target_phys_addr_t size, + target_phys_addr_t address_index) { uint8_t *vaddr_base; xen_pfn_t *pfns; @@ -120,7 +124,7 @@ static void qemu_remap_bucket(MapCacheEntry *entry, unsigned int i; target_phys_addr_t nb_pfn = size >> XC_PAGE_SHIFT; - trace_qemu_remap_bucket(address_index); + trace_xen_remap_bucket(address_index); pfns = qemu_mallocz(nb_pfn * sizeof (xen_pfn_t)); err = qemu_mallocz(nb_pfn * sizeof (int)); @@ -164,17 +168,18 @@ static void qemu_remap_bucket(MapCacheEntry *entry, qemu_free(err); } -uint8_t *qemu_map_cache(target_phys_addr_t phys_addr, target_phys_addr_t size, uint8_t lock) +uint8_t *xen_map_cache(target_phys_addr_t phys_addr, target_phys_addr_t size, + uint8_t lock) { MapCacheEntry *entry, *pentry = NULL; target_phys_addr_t address_index = phys_addr >> MCACHE_BUCKET_SHIFT; target_phys_addr_t address_offset = phys_addr & (MCACHE_BUCKET_SIZE - 1); target_phys_addr_t __size = size; - trace_qemu_map_cache(phys_addr); + trace_xen_map_cache(phys_addr); if (address_index == mapcache->last_address_index && !lock && !__size) { - trace_qemu_map_cache_return(mapcache->last_address_vaddr + address_offset); + trace_xen_map_cache_return(mapcache->last_address_vaddr + address_offset); return mapcache->last_address_vaddr + address_offset; } @@ -198,20 +203,20 @@ uint8_t *qemu_map_cache(target_phys_addr_t phys_addr, target_phys_addr_t size, u if (!entry) { entry = qemu_mallocz(sizeof (MapCacheEntry)); pentry->next = entry; - qemu_remap_bucket(entry, __size, address_index); + xen_remap_bucket(entry, __size, address_index); } else if (!entry->lock) { if (!entry->vaddr_base || entry->paddr_index != address_index || entry->size != __size || !test_bits(address_offset >> XC_PAGE_SHIFT, size >> XC_PAGE_SHIFT, entry->valid_mapping)) { - qemu_remap_bucket(entry, __size, address_index); + xen_remap_bucket(entry, __size, address_index); } } if(!test_bits(address_offset >> XC_PAGE_SHIFT, size >> XC_PAGE_SHIFT, entry->valid_mapping)) { mapcache->last_address_index = -1; - trace_qemu_map_cache_return(NULL); + trace_xen_map_cache_return(NULL); return NULL; } @@ -226,11 +231,11 @@ uint8_t *qemu_map_cache(target_phys_addr_t phys_addr, target_phys_addr_t size, u QTAILQ_INSERT_HEAD(&mapcache->locked_entries, reventry, next); } - trace_qemu_map_cache_return(mapcache->last_address_vaddr + address_offset); + trace_xen_map_cache_return(mapcache->last_address_vaddr + address_offset); return mapcache->last_address_vaddr + address_offset; } -ram_addr_t qemu_ram_addr_from_mapcache(void *ptr) +ram_addr_t xen_ram_addr_from_mapcache(void *ptr) { MapCacheEntry *entry = NULL, *pentry = NULL; MapCacheRev *reventry; @@ -247,7 +252,7 @@ ram_addr_t qemu_ram_addr_from_mapcache(void *ptr) } } if (!found) { - fprintf(stderr, "qemu_ram_addr_from_mapcache, could not find %p\n", ptr); + fprintf(stderr, "%s, could not find %p\n", __func__, ptr); QTAILQ_FOREACH(reventry, &mapcache->locked_entries, next) { DPRINTF(" "TARGET_FMT_plx" -> %p is present\n", reventry->paddr_index, reventry->vaddr_req); @@ -269,7 +274,7 @@ ram_addr_t qemu_ram_addr_from_mapcache(void *ptr) ((unsigned long) ptr - (unsigned long) entry->vaddr_base); } -void qemu_invalidate_entry(uint8_t *buffer) +void xen_invalidate_map_cache_entry(uint8_t *buffer) { MapCacheEntry *entry = NULL, *pentry = NULL; MapCacheRev *reventry; @@ -290,7 +295,7 @@ void qemu_invalidate_entry(uint8_t *buffer) } } if (!found) { - DPRINTF("qemu_invalidate_entry, could not find %p\n", buffer); + DPRINTF("%s, could not find %p\n", __func__, buffer); QTAILQ_FOREACH(reventry, &mapcache->locked_entries, next) { DPRINTF(" "TARGET_FMT_plx" -> %p is present\n", reventry->paddr_index, reventry->vaddr_req); } @@ -322,7 +327,7 @@ void qemu_invalidate_entry(uint8_t *buffer) qemu_free(entry); } -void qemu_invalidate_map_cache(void) +void xen_invalidate_map_cache(void) { unsigned long i; MapCacheRev *reventry; diff --git a/xen-mapcache.h b/xen-mapcache.h index 6216cc3be7..606b8afc52 100644 --- a/xen-mapcache.h +++ b/xen-mapcache.h @@ -9,13 +9,11 @@ #ifndef XEN_MAPCACHE_H #define XEN_MAPCACHE_H -void qemu_map_cache_init(void); -uint8_t *qemu_map_cache(target_phys_addr_t phys_addr, target_phys_addr_t size, uint8_t lock); -ram_addr_t qemu_ram_addr_from_mapcache(void *ptr); -void qemu_invalidate_entry(uint8_t *buffer); -void qemu_invalidate_map_cache(void); - -#define mapcache_lock() ((void)0) -#define mapcache_unlock() ((void)0) +void xen_map_cache_init(void); +uint8_t *xen_map_cache(target_phys_addr_t phys_addr, target_phys_addr_t size, + uint8_t lock); +ram_addr_t xen_ram_addr_from_mapcache(void *ptr); +void xen_invalidate_map_cache_entry(uint8_t *buffer); +void xen_invalidate_map_cache(void); #endif /* !XEN_MAPCACHE_H */ From 868bb33faa643c7ee291bb308a829122b72d4845 Mon Sep 17 00:00:00 2001 From: Jan Kiszka Date: Tue, 21 Jun 2011 22:59:09 +0200 Subject: [PATCH 116/209] xen: Fold CONFIG_XEN_MAPCACHE into CONFIG_XEN Xen won't be enabled if there is no backend support available for the host. And that also means the map cache will work. So drop the separate config switch and move the required stubs over to xen-stub.c. Signed-off-by: Jan Kiszka Signed-off-by: Alexander Graf --- Makefile.target | 5 +---- exec.c | 16 ++++++++-------- hw/xen.h | 9 --------- xen-mapcache-stub.c | 37 ------------------------------------- 4 files changed, 9 insertions(+), 58 deletions(-) delete mode 100644 xen-mapcache-stub.c diff --git a/Makefile.target b/Makefile.target index c347176c89..c566eb1149 100644 --- a/Makefile.target +++ b/Makefile.target @@ -4,7 +4,6 @@ GENERATED_HEADERS = config-target.h CONFIG_NO_PCI = $(if $(subst n,,$(CONFIG_PCI)),n,y) CONFIG_NO_KVM = $(if $(subst n,,$(CONFIG_KVM)),n,y) CONFIG_NO_XEN = $(if $(subst n,,$(CONFIG_XEN)),n,y) -CONFIG_NO_XEN_MAPCACHE = $(if $(subst n,,$(CONFIG_XEN_MAPCACHE)),n,y) include ../config-host.mak include config-devices.mak @@ -207,10 +206,8 @@ QEMU_CFLAGS += $(VNC_JPEG_CFLAGS) QEMU_CFLAGS += $(VNC_PNG_CFLAGS) # xen support -obj-$(CONFIG_XEN) += xen-all.o xen_machine_pv.o xen_domainbuild.o +obj-$(CONFIG_XEN) += xen-all.o xen_machine_pv.o xen_domainbuild.o xen-mapcache.o obj-$(CONFIG_NO_XEN) += xen-stub.o -obj-i386-$(CONFIG_XEN_MAPCACHE) += xen-mapcache.o -obj-$(CONFIG_NO_XEN_MAPCACHE) += xen-mapcache-stub.o obj-i386-$(CONFIG_XEN) += xen_platform.o diff --git a/exec.c b/exec.c index 50bccd82d0..067bb343e1 100644 --- a/exec.c +++ b/exec.c @@ -2953,7 +2953,7 @@ ram_addr_t qemu_ram_alloc_from_ptr(DeviceState *dev, const char *name, abort(); } #else - if (xen_mapcache_enabled()) { + if (xen_enabled()) { xen_ram_alloc(new_block->offset, size); } else { new_block->host = qemu_vmalloc(size); @@ -3019,7 +3019,7 @@ void qemu_ram_free(ram_addr_t addr) #if defined(TARGET_S390X) && defined(CONFIG_KVM) munmap(block->host, block->length); #else - if (xen_mapcache_enabled()) { + if (xen_enabled()) { xen_invalidate_map_cache_entry(block->host); } else { qemu_vfree(block->host); @@ -3112,7 +3112,7 @@ void *qemu_get_ram_ptr(ram_addr_t addr) QLIST_REMOVE(block, next); QLIST_INSERT_HEAD(&ram_list.blocks, block, next); } - if (xen_mapcache_enabled()) { + if (xen_enabled()) { /* We need to check if the requested address is in the RAM * because we don't want to map the entire memory in QEMU. * In that case just map until the end of the page. @@ -3143,7 +3143,7 @@ void *qemu_safe_ram_ptr(ram_addr_t addr) QLIST_FOREACH(block, &ram_list.blocks, next) { if (addr - block->offset < block->length) { - if (xen_mapcache_enabled()) { + if (xen_enabled()) { /* We need to check if the requested address is in the RAM * because we don't want to map the entire memory in QEMU. * In that case just map until the end of the page. @@ -3169,9 +3169,9 @@ void *qemu_safe_ram_ptr(ram_addr_t addr) * but takes a size argument */ void *qemu_ram_ptr_length(target_phys_addr_t addr, target_phys_addr_t *size) { - if (xen_mapcache_enabled()) + if (xen_enabled()) { return xen_map_cache(addr, *size, 1); - else { + } else { RAMBlock *block; QLIST_FOREACH(block, &ram_list.blocks, next) { @@ -3200,7 +3200,7 @@ int qemu_ram_addr_from_host(void *ptr, ram_addr_t *ram_addr) RAMBlock *block; uint8_t *host = ptr; - if (xen_mapcache_enabled()) { + if (xen_enabled()) { *ram_addr = xen_ram_addr_from_mapcache(ptr); return 0; } @@ -4115,7 +4115,7 @@ void cpu_physical_memory_unmap(void *buffer, target_phys_addr_t len, access_len -= l; } } - if (xen_mapcache_enabled()) { + if (xen_enabled()) { xen_invalidate_map_cache_entry(buffer); } return; diff --git a/hw/xen.h b/hw/xen.h index d435ca0ce5..95029bb164 100644 --- a/hw/xen.h +++ b/hw/xen.h @@ -31,15 +31,6 @@ static inline int xen_enabled(void) #endif } -static inline int xen_mapcache_enabled(void) -{ -#ifdef CONFIG_XEN_MAPCACHE - return xen_enabled(); -#else - return 0; -#endif -} - int xen_pci_slot_get_pirq(PCIDevice *pci_dev, int irq_num); void xen_piix3_set_irq(void *opaque, int irq_num, int level); void xen_piix_pci_write_config_client(uint32_t address, uint32_t val, int len); diff --git a/xen-mapcache-stub.c b/xen-mapcache-stub.c deleted file mode 100644 index f0370168e1..0000000000 --- a/xen-mapcache-stub.c +++ /dev/null @@ -1,37 +0,0 @@ -/* - * Copyright (C) 2011 Citrix Ltd. - * - * This work is licensed under the terms of the GNU GPL, version 2. See - * the COPYING file in the top-level directory. - * - */ - -#include "config.h" - -#include "cpu.h" -#include "qemu-common.h" -#include "cpu-common.h" -#include "xen-mapcache.h" - -void xen_map_cache_init(void) -{ -} - -uint8_t *xen_map_cache(target_phys_addr_t phys_addr, target_phys_addr_t size, - uint8_t lock) -{ - return qemu_get_ram_ptr(phys_addr); -} - -ram_addr_t xen_ram_addr_from_mapcache(void *ptr) -{ - return -1; -} - -void xen_invalidate_map_cache(void) -{ -} - -void xen_invalidate_map_cache_entry(uint8_t *buffer) -{ -} From ad35a7da1a0a40cd8920ba829640bd43e1613ec1 Mon Sep 17 00:00:00 2001 From: Stefano Stabellini Date: Fri, 24 Jun 2011 15:54:48 +0100 Subject: [PATCH 117/209] xen: enable console and disk backend in HVM mode Initialize the Xen console backend and the Xen disk backend even when running in HVM mode so that PV on HVM drivers can connect to them. Signed-off-by: Stefano Stabellini Signed-off-by: Alexander Graf --- xen-all.c | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/xen-all.c b/xen-all.c index 4827d6a7ba..3d40ab0ece 100644 --- a/xen-all.c +++ b/xen-all.c @@ -862,6 +862,14 @@ int xen_hvm_init(void) cpu_register_phys_memory_client(&state->client); state->log_for_dirtybit = NULL; + /* Initialize backend core & drivers */ + if (xen_be_init() != 0) { + fprintf(stderr, "%s: xen backend core setup failed\n", __FUNCTION__); + exit(1); + } + xen_be_register("console", &xen_console_ops); + xen_be_register("qdisk", &xen_blkdev_ops); + return 0; } From 5e6b701aba8689a336297dda047bf760ffc05291 Mon Sep 17 00:00:00 2001 From: Stefano Stabellini Date: Fri, 24 Jun 2011 16:59:46 +0100 Subject: [PATCH 118/209] xen_console: fix memory leak con_init leaks the string "type", fix it. Signed-off-by: Stefano Stabellini Signed-off-by: Alexander Graf --- hw/xen_console.c | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/hw/xen_console.c b/hw/xen_console.c index c6c8163813..2d613ee6e5 100644 --- a/hw/xen_console.c +++ b/hw/xen_console.c @@ -180,6 +180,7 @@ static int con_init(struct XenDevice *xendev) { struct XenConsole *con = container_of(xendev, struct XenConsole, xendev); char *type, *dom; + int ret = 0; /* setup */ dom = xs_get_domain_path(xenstore, con->xendev.dom); @@ -189,7 +190,8 @@ static int con_init(struct XenDevice *xendev) type = xenstore_read_str(con->console, "type"); if (!type || strcmp(type, "ioemu") != 0) { xen_be_printf(xendev, 1, "not for me (type=%s)\n", type); - return -1; + ret = -1; + goto out; } if (!serial_hds[con->xendev.dev]) @@ -198,7 +200,9 @@ static int con_init(struct XenDevice *xendev) else con->chr = serial_hds[con->xendev.dev]; - return 0; +out: + qemu_free(type); + return ret; } static int con_connect(struct XenDevice *xendev) From 37cdfcf194825d03334542297bee3a3a4723e6e3 Mon Sep 17 00:00:00 2001 From: Stefano Stabellini Date: Fri, 24 Jun 2011 17:36:11 +0100 Subject: [PATCH 119/209] xen: add vkbd support for PV on HVM guests Register the vkbd backend even when running as device emulator for HVM guests: it is useful because it doesn't need a frequent timer like usb. Check whether the XenInput DisplayState has been set in the initialise state, rather than the input state. In case the DisplayState hasn't been set and there is no vfb for this domain, then set the XenInput DisplayState to the default one. Changed in v2: - use qemu_free instead of free; Signed-off-by: Stefano Stabellini Signed-off-by: Alexander Graf --- hw/xenfb.c | 19 ++++++++++++------- xen-all.c | 1 + 2 files changed, 13 insertions(+), 7 deletions(-) diff --git a/hw/xenfb.c b/hw/xenfb.c index 1db75fbe49..0a01ae30cc 100644 --- a/hw/xenfb.c +++ b/hw/xenfb.c @@ -347,13 +347,6 @@ static void xenfb_mouse_event(void *opaque, static int input_init(struct XenDevice *xendev) { - struct XenInput *in = container_of(xendev, struct XenInput, c.xendev); - - if (!in->c.ds) { - xen_be_printf(xendev, 1, "ds not set (yet)\n"); - return -1; - } - xenstore_write_be_int(xendev, "feature-abs-pointer", 1); return 0; } @@ -367,6 +360,18 @@ static int input_connect(struct XenDevice *xendev) &in->abs_pointer_wanted) == -1) in->abs_pointer_wanted = 0; + if (!in->c.ds) { + char *vfb = xenstore_read_str(NULL, "device/vfb"); + if (vfb == NULL) { + /* there is no vfb, run vkbd on its own */ + in->c.ds = get_displaystate(); + } else { + qemu_free(vfb); + xen_be_printf(xendev, 1, "ds not set (yet)\n"); + return -1; + } + } + rc = common_bind(&in->c); if (rc != 0) return rc; diff --git a/xen-all.c b/xen-all.c index 3d40ab0ece..fb9bcc8f72 100644 --- a/xen-all.c +++ b/xen-all.c @@ -868,6 +868,7 @@ int xen_hvm_init(void) exit(1); } xen_be_register("console", &xen_console_ops); + xen_be_register("vkbd", &xen_kbdmouse_ops); xen_be_register("qdisk", &xen_blkdev_ops); return 0; From 5ea3c2b405e1a0937e8ccfdb57345628eb904525 Mon Sep 17 00:00:00 2001 From: Stefano Stabellini Date: Mon, 27 Jun 2011 16:10:01 +0100 Subject: [PATCH 120/209] xen_disk: cope with missing xenstore "params" node When disk is a cdrom and the drive is empty the "params" node in xenstore might be missing completely: cope with it instead of segfaulting. Updated in v2: - actually removed the strchr(blkdev->params, ':') that caused the segfault; - free all the allocated strings from xenstore before returning; Updated in v3: - set blkdev fields to NULL after free'ing them. Signed-off-by: Stefano Stabellini Signed-off-by: Alexander Graf --- hw/xen_disk.c | 34 +++++++++++++++++++++++++++------- 1 file changed, 27 insertions(+), 7 deletions(-) diff --git a/hw/xen_disk.c b/hw/xen_disk.c index 0c298afa8d..f14e5a6169 100644 --- a/hw/xen_disk.c +++ b/hw/xen_disk.c @@ -616,12 +616,14 @@ static int blk_init(struct XenDevice *xendev) { struct XenBlkDev *blkdev = container_of(xendev, struct XenBlkDev, xendev); int index, qflags, have_barriers, info = 0; - char *h; /* read xenstore entries */ if (blkdev->params == NULL) { + char *h = NULL; blkdev->params = xenstore_read_be_str(&blkdev->xendev, "params"); - h = strchr(blkdev->params, ':'); + if (blkdev->params != NULL) { + h = strchr(blkdev->params, ':'); + } if (h != NULL) { blkdev->fileproto = blkdev->params; blkdev->filename = h+1; @@ -649,7 +651,7 @@ static int blk_init(struct XenDevice *xendev) blkdev->mode == NULL || blkdev->type == NULL || blkdev->dev == NULL) { - return -1; + goto out_error; } /* read-only ? */ @@ -672,10 +674,15 @@ static int blk_init(struct XenDevice *xendev) /* setup via xenbus -> create new block driver instance */ xen_be_printf(&blkdev->xendev, 2, "create new bdrv (xenbus setup)\n"); blkdev->bs = bdrv_new(blkdev->dev); - if (bdrv_open(blkdev->bs, blkdev->filename, qflags, - bdrv_find_whitelisted_format(blkdev->fileproto)) != 0) { - bdrv_delete(blkdev->bs); - return -1; + if (blkdev->bs) { + if (bdrv_open(blkdev->bs, blkdev->filename, qflags, + bdrv_find_whitelisted_format(blkdev->fileproto)) != 0) { + bdrv_delete(blkdev->bs); + blkdev->bs = NULL; + } + } + if (!blkdev->bs) { + goto out_error; } } else { /* setup via qemu cmdline -> already setup for us */ @@ -704,6 +711,19 @@ static int blk_init(struct XenDevice *xendev) xenstore_write_be_int(&blkdev->xendev, "sectors", blkdev->file_size / blkdev->file_blk); return 0; + +out_error: + qemu_free(blkdev->params); + blkdev->params = NULL; + qemu_free(blkdev->mode); + blkdev->mode = NULL; + qemu_free(blkdev->type); + blkdev->type = NULL; + qemu_free(blkdev->dev); + blkdev->dev = NULL; + qemu_free(blkdev->devtype); + blkdev->devtype = NULL; + return -1; } static int blk_connect(struct XenDevice *xendev) From 8ab934f93b5ad3d0af4ad419d2531235a75d672c Mon Sep 17 00:00:00 2001 From: Stefano Stabellini Date: Mon, 27 Jun 2011 18:26:06 +0100 Subject: [PATCH 121/209] qemu_ram_ptr_length: take ram_addr_t as arguments qemu_ram_ptr_length should take ram_addr_t as argument rather than target_phys_addr_t because is doing comparisons with RAMBlock addresses. cpu_physical_memory_map should create a ram_addr_t address to pass to qemu_ram_ptr_length from PhysPageDesc phys_offset. Remove code after abort() in qemu_ram_ptr_length. Changes in v2: - handle 0 size in qemu_ram_ptr_length; - rename addr1 to raddr; - initialize raddr to ULONG_MAX. Signed-off-by: Stefano Stabellini Reviewed-by: Peter Maydell Signed-off-by: Alexander Graf --- cpu-common.h | 2 +- exec.c | 21 ++++++++++++++------- 2 files changed, 15 insertions(+), 8 deletions(-) diff --git a/cpu-common.h b/cpu-common.h index c6a2b5fbb9..a5b80e1351 100644 --- a/cpu-common.h +++ b/cpu-common.h @@ -65,7 +65,7 @@ void qemu_ram_free_from_ptr(ram_addr_t addr); void qemu_ram_remap(ram_addr_t addr, ram_addr_t length); /* This should only be used for ram local to a device. */ void *qemu_get_ram_ptr(ram_addr_t addr); -void *qemu_ram_ptr_length(target_phys_addr_t addr, target_phys_addr_t *size); +void *qemu_ram_ptr_length(ram_addr_t addr, ram_addr_t *size); /* Same but slower, to use for migration, where the order of * RAMBlocks must not change. */ void *qemu_safe_ram_ptr(ram_addr_t addr); diff --git a/exec.c b/exec.c index 067bb343e1..827790088b 100644 --- a/exec.c +++ b/exec.c @@ -3167,8 +3167,11 @@ void *qemu_safe_ram_ptr(ram_addr_t addr) /* Return a host pointer to guest's ram. Similar to qemu_get_ram_ptr * but takes a size argument */ -void *qemu_ram_ptr_length(target_phys_addr_t addr, target_phys_addr_t *size) +void *qemu_ram_ptr_length(ram_addr_t addr, ram_addr_t *size) { + if (*size == 0) { + return NULL; + } if (xen_enabled()) { return xen_map_cache(addr, *size, 1); } else { @@ -3184,9 +3187,6 @@ void *qemu_ram_ptr_length(target_phys_addr_t addr, target_phys_addr_t *size) fprintf(stderr, "Bad ram offset %" PRIx64 "\n", (uint64_t)addr); abort(); - - *size = 0; - return NULL; } } @@ -4052,7 +4052,9 @@ void *cpu_physical_memory_map(target_phys_addr_t addr, target_phys_addr_t page; unsigned long pd; PhysPageDesc *p; - target_phys_addr_t addr1 = addr; + ram_addr_t raddr = ULONG_MAX; + ram_addr_t rlen; + void *ret; while (len > 0) { page = addr & TARGET_PAGE_MASK; @@ -4080,13 +4082,18 @@ void *cpu_physical_memory_map(target_phys_addr_t addr, *plen = l; return bounce.buffer; } + if (!todo) { + raddr = (pd & TARGET_PAGE_MASK) + (addr & ~TARGET_PAGE_MASK); + } len -= l; addr += l; todo += l; } - *plen = todo; - return qemu_ram_ptr_length(addr1, plen); + rlen = todo; + ret = qemu_ram_ptr_length(raddr, &rlen); + *plen = rlen; + return ret; } /* Unmaps a memory region previously mapped by cpu_physical_memory_map(). From 7cef3f4fdbe813ac7d495e15a81531f1181b19f9 Mon Sep 17 00:00:00 2001 From: Stefano Stabellini Date: Thu, 30 Jun 2011 15:42:31 +0100 Subject: [PATCH 122/209] xen_disk: treat "aio" as "raw" Sometimes the toolstack uses "aio" without an additional format identifier, in such cases use "raw". Updated in v2: - fix code style. Signed-off-by: Stefano Stabellini Signed-off-by: Alexander Graf --- hw/xen_disk.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/hw/xen_disk.c b/hw/xen_disk.c index f14e5a6169..add815f273 100644 --- a/hw/xen_disk.c +++ b/hw/xen_disk.c @@ -633,6 +633,9 @@ static int blk_init(struct XenDevice *xendev) blkdev->filename = blkdev->params; } } + if (!strcmp("aio", blkdev->fileproto)) { + blkdev->fileproto = "raw"; + } if (blkdev->mode == NULL) { blkdev->mode = xenstore_read_be_str(&blkdev->xendev, "mode"); } From 9fbe4784449f6248224ac95e0aa9e56a0bfdd155 Mon Sep 17 00:00:00 2001 From: Alexander Graf Date: Wed, 29 Jun 2011 08:04:27 +0200 Subject: [PATCH 123/209] checkpatch: don't error out on },{ lines When having code like this: static PCIDeviceInfo piix_ide_info[] = { { .qdev.name = "piix3-ide", .qdev.size = sizeof(PCIIDEState), .qdev.no_user = 1, .no_hotplug = 1, .init = pci_piix_ide_initfn, .vendor_id = PCI_VENDOR_ID_INTEL, .device_id = PCI_DEVICE_ID_INTEL_82371SB_1, .class_id = PCI_CLASS_STORAGE_IDE, },{ .qdev.name = "piix4-ide", .qdev.size = sizeof(PCIIDEState), .qdev.no_user = 1, .no_hotplug = 1, .init = pci_piix_ide_initfn, .vendor_id = PCI_VENDOR_ID_INTEL, .device_id = PCI_DEVICE_ID_INTEL_82371AB, .class_id = PCI_CLASS_STORAGE_IDE, },{ /* end of list */ } }; checkpatch currently errors out, claiming that spaces need to follow commas. However, this particular style of defining structs is pretty common in qemu code and very readable. So let's declare it as supported for the above case. Reported-by: Kevin Wolf Signed-off-by: Alexander Graf --- scripts/checkpatch.pl | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/scripts/checkpatch.pl b/scripts/checkpatch.pl index 075b6149a3..70a2111d19 100755 --- a/scripts/checkpatch.pl +++ b/scripts/checkpatch.pl @@ -2068,8 +2068,10 @@ sub process { } # , must have a space on the right. + # not required when having a single },{ on one line } elsif ($op eq ',') { - if ($ctx !~ /.x[WEC]/ && $cc !~ /^}/) { + if ($ctx !~ /.x[WEC]/ && $cc !~ /^}/ && + ($elements[$n] . $elements[$n + 2]) !~ " *}{") { ERROR("space required after that '$op' $at\n" . $hereptr); } From 0f51726adcdb620214405a88b2601d9edd059db4 Mon Sep 17 00:00:00 2001 From: Stefano Stabellini Date: Thu, 30 Jun 2011 18:26:29 +0100 Subject: [PATCH 124/209] xen_console: support the new extended xenstore protocol Since CS 21994 on xen-unstable.hg and CS 466608f3a32e1f9808acdf832a5843af37e5fcec on qemu-xen-unstable.git, few changes have been introduced to the PV console xenstore protocol, as described by the document docs/misc/console.txt under xen-unstable.hg. From the Qemu point of view, very few modifications are needed to correctly support the protocol: read from xenstore the "output" node that tell us what the output of the PV console is going to be. In case the output is a tty, write to xenstore the device name. Changes in v2: - fix error paths: free malloc'ed strings and close the xenstore connection before returning; - remove useless snprintf in xenstore_store_pv_console_info if i == 0. Changes in v3: - replace xs_daemon_open/xs_daemon_close with xs_open/xs_close. Changes in v4: - add a compatibility implementation of xs_open/xs_close. Changes in v5: - fix code style. [agraf] fix build error due to missing stub Signed-off-by: Stefano Stabellini Signed-off-by: Alexander Graf --- hw/xen.h | 1 + hw/xen_common.h | 12 ++++++++++ hw/xen_console.c | 16 ++++++++----- xen-all.c | 60 ++++++++++++++++++++++++++++++++++++++++++++++++ xen-stub.c | 4 ++++ 5 files changed, 87 insertions(+), 6 deletions(-) diff --git a/hw/xen.h b/hw/xen.h index 95029bb164..e432705f45 100644 --- a/hw/xen.h +++ b/hw/xen.h @@ -41,6 +41,7 @@ qemu_irq *xen_interrupt_controller_init(void); int xen_init(void); int xen_hvm_init(void); void xen_vcpu_init(void); +void xenstore_store_pv_console_info(int i, struct CharDriverState *chr); #if defined(NEED_CPU_H) && !defined(CONFIG_USER_ONLY) void xen_ram_alloc(ram_addr_t ram_addr, ram_addr_t size); diff --git a/hw/xen_common.h b/hw/xen_common.h index 2c79af64d0..0409ac7971 100644 --- a/hw/xen_common.h +++ b/hw/xen_common.h @@ -85,6 +85,18 @@ static inline int xc_domain_add_to_physmap(int xc_handle, uint32_t domid, return xc_memory_op(xc_handle, XENMEM_add_to_physmap, &xatp); } +static inline struct xs_handle *xs_open(unsigned long flags) +{ + return xs_daemon_open(); +} + +static inline void xs_close(struct xs_handle *xsh) +{ + if (xsh != NULL) { + xs_daemon_close(xsh); + } +} + /* Xen 4.1 */ #else diff --git a/hw/xen_console.c b/hw/xen_console.c index 2d613ee6e5..bdb8540542 100644 --- a/hw/xen_console.c +++ b/hw/xen_console.c @@ -179,8 +179,9 @@ static void xencons_send(struct XenConsole *con) static int con_init(struct XenDevice *xendev) { struct XenConsole *con = container_of(xendev, struct XenConsole, xendev); - char *type, *dom; + char *type, *dom, label[32]; int ret = 0; + const char *output; /* setup */ dom = xs_get_domain_path(xenstore, con->xendev.dom); @@ -194,11 +195,14 @@ static int con_init(struct XenDevice *xendev) goto out; } - if (!serial_hds[con->xendev.dev]) - xen_be_printf(xendev, 1, "WARNING: serial line %d not configured\n", - con->xendev.dev); - else - con->chr = serial_hds[con->xendev.dev]; + output = xenstore_read_str(con->console, "output"); + /* output is a pty by default */ + if (output == NULL) { + output = "pty"; + } + snprintf(label, sizeof(label), "xencons%d", con->xendev.dev); + con->chr = qemu_chr_open(label, output, NULL); + xenstore_store_pv_console_info(con->xendev.dev, con->chr); out: qemu_free(type); diff --git a/xen-all.c b/xen-all.c index fb9bcc8f72..8105c83683 100644 --- a/xen-all.c +++ b/xen-all.c @@ -737,6 +737,66 @@ static void cpu_handle_ioreq(void *opaque) } } +static int store_dev_info(int domid, CharDriverState *cs, const char *string) +{ + struct xs_handle *xs = NULL; + char *path = NULL; + char *newpath = NULL; + char *pts = NULL; + int ret = -1; + + /* Only continue if we're talking to a pty. */ + if (strncmp(cs->filename, "pty:", 4)) { + return 0; + } + pts = cs->filename + 4; + + /* We now have everything we need to set the xenstore entry. */ + xs = xs_open(0); + if (xs == NULL) { + fprintf(stderr, "Could not contact XenStore\n"); + goto out; + } + + path = xs_get_domain_path(xs, domid); + if (path == NULL) { + fprintf(stderr, "xs_get_domain_path() error\n"); + goto out; + } + newpath = realloc(path, (strlen(path) + strlen(string) + + strlen("/tty") + 1)); + if (newpath == NULL) { + fprintf(stderr, "realloc error\n"); + goto out; + } + path = newpath; + + strcat(path, string); + strcat(path, "/tty"); + if (!xs_write(xs, XBT_NULL, path, pts, strlen(pts))) { + fprintf(stderr, "xs_write for '%s' fail", string); + goto out; + } + ret = 0; + +out: + free(path); + xs_close(xs); + + return ret; +} + +void xenstore_store_pv_console_info(int i, CharDriverState *chr) +{ + if (i == 0) { + store_dev_info(xen_domid, chr, "/console"); + } else { + char buf[32]; + snprintf(buf, sizeof(buf), "/device/console/%d", i); + store_dev_info(xen_domid, chr, buf); + } +} + static void xenstore_record_dm_state(XenIOState *s, const char *state) { char path[50]; diff --git a/xen-stub.c b/xen-stub.c index a4f35a19fb..efe2ab55f2 100644 --- a/xen-stub.c +++ b/xen-stub.c @@ -9,6 +9,10 @@ #include "qemu-common.h" #include "hw/xen.h" +void xenstore_store_pv_console_info(int i, CharDriverState *chr) +{ +} + int xen_pci_slot_get_pirq(PCIDevice *pci_dev, int irq_num) { return -1; From 25a118130fde0d20b0f5a223642849b392b2f2ee Mon Sep 17 00:00:00 2001 From: Alexander Graf Date: Sun, 3 Jul 2011 09:44:48 +0200 Subject: [PATCH 125/209] xen_console: fall back to qemu serial device The new xen_console protocol changed the default xen_console output device from whatever Qemu chose to whatever xenstore choses and "pty" as fallback. This is not how Qemu works. It has its own serial redirection semantics. So it xenstore doesn't contain information on what to do, Qemu is the place to ask. Signed-off-by: Alexander Graf --- hw/xen_console.c | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/hw/xen_console.c b/hw/xen_console.c index bdb8540542..8ef104c9ac 100644 --- a/hw/xen_console.c +++ b/hw/xen_console.c @@ -196,12 +196,15 @@ static int con_init(struct XenDevice *xendev) } output = xenstore_read_str(con->console, "output"); - /* output is a pty by default */ + + /* no Xen override, use qemu output device */ if (output == NULL) { - output = "pty"; + con->chr = serial_hds[con->xendev.dev]; + } else { + snprintf(label, sizeof(label), "xencons%d", con->xendev.dev); + con->chr = qemu_chr_open(label, output, NULL); } - snprintf(label, sizeof(label), "xencons%d", con->xendev.dev); - con->chr = qemu_chr_open(label, output, NULL); + xenstore_store_pv_console_info(con->xendev.dev, con->chr); out: From c1be973ae1135588ed77b365bfd3bf063bac78ae Mon Sep 17 00:00:00 2001 From: "Michael S. Tsirkin" Date: Tue, 21 Jun 2011 20:34:17 +0300 Subject: [PATCH 126/209] vhost: fix double free on device stop vhost dev stop failed to clear the log field. Typically not an issue as dev start overwrites this field, but if logging gets disabled before the following start, it doesn't so this causes a double free. Signed-off-by: Michael S. Tsirkin --- hw/vhost.c | 1 + 1 file changed, 1 insertion(+) diff --git a/hw/vhost.c b/hw/vhost.c index 80f771e448..c3d88214fe 100644 --- a/hw/vhost.c +++ b/hw/vhost.c @@ -784,5 +784,6 @@ void vhost_dev_stop(struct vhost_dev *hdev, VirtIODevice *vdev) hdev->started = false; qemu_free(hdev->log); + hdev->log = NULL; hdev->log_size = 0; } From 33d5ad53c1039989c4ecf583ed52a584d3888495 Mon Sep 17 00:00:00 2001 From: "Michael S. Tsirkin" Date: Sun, 26 Jun 2011 16:17:27 +0300 Subject: [PATCH 127/209] pci_ids: tweak names to match linux/pci_ids.h Sync xen names to ones used by linux. Add xen platform device id as well. Signed-off-by: Michael S. Tsirkin --- hw/pci_ids.h | 3 ++- hw/xen_platform.c | 8 ++++---- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/hw/pci_ids.h b/hw/pci_ids.h index d94578c87d..b49c602844 100644 --- a/hw/pci_ids.h +++ b/hw/pci_ids.h @@ -110,4 +110,5 @@ #define PCI_DEVICE_ID_INTEL_82371AB_2 0x7112 #define PCI_DEVICE_ID_INTEL_82371AB_3 0x7113 -#define PCI_VENDOR_ID_XENSOURCE 0x5853 +#define PCI_VENDOR_ID_XEN 0x5853 +#define PCI_DEVICE_ID_XEN_PLATFORM 0x0001 diff --git a/hw/xen_platform.c b/hw/xen_platform.c index b167eee1ff..9a01735d31 100644 --- a/hw/xen_platform.c +++ b/hw/xen_platform.c @@ -290,10 +290,10 @@ static int xen_platform_initfn(PCIDevice *dev) pci_conf = d->pci_dev.config; - pci_config_set_vendor_id(pci_conf, PCI_VENDOR_ID_XENSOURCE); - pci_config_set_device_id(pci_conf, 0x0001); - pci_set_word(pci_conf + PCI_SUBSYSTEM_VENDOR_ID, PCI_VENDOR_ID_XENSOURCE); - pci_set_word(pci_conf + PCI_SUBSYSTEM_ID, 0x0001); + pci_config_set_vendor_id(pci_conf, PCI_VENDOR_ID_XEN); + pci_config_set_device_id(pci_conf, PCI_DEVICE_ID_XEN_PLATFORM); + pci_set_word(pci_conf + PCI_SUBSYSTEM_VENDOR_ID, PCI_VENDOR_ID_XEN); + pci_set_word(pci_conf + PCI_SUBSYSTEM_ID, PCI_DEVICE_ID_XEN_PLATFORM); pci_set_word(pci_conf + PCI_COMMAND, PCI_COMMAND_IO | PCI_COMMAND_MEMORY); From ce4fd422a6c5119baf4a2c3115c7ea63efb0f68e Mon Sep 17 00:00:00 2001 From: Anthony PERARD Date: Wed, 22 Jun 2011 16:58:31 +0100 Subject: [PATCH 128/209] hw/piix_pci.c: Fix PIIX3-xen to initialize ids Signed-off-by: Anthony PERARD Signed-off-by: Michael S. Tsirkin --- hw/piix_pci.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/hw/piix_pci.c b/hw/piix_pci.c index 26ce90451a..d08b31a266 100644 --- a/hw/piix_pci.c +++ b/hw/piix_pci.c @@ -478,6 +478,9 @@ static PCIDeviceInfo i440fx_info[] = { .no_hotplug = 1, .init = piix3_initfn, .config_write = piix3_write_config_xen, + .vendor_id = PCI_VENDOR_ID_INTEL, + .device_id = PCI_DEVICE_ID_INTEL_82371SB_0, // 82371SB PIIX3 PCI-to-ISA bridge (Step A1) + .class_id = PCI_CLASS_BRIDGE_ISA, },{ /* end of list */ } From 0d2b962d16feaf1eb1a4658a4c1b85642418cd07 Mon Sep 17 00:00:00 2001 From: "Michael S. Tsirkin" Date: Sun, 26 Jun 2011 16:30:45 +0300 Subject: [PATCH 129/209] xen: move to new pci initializers move ids to pci info structure Signed-off-by: Michael S. Tsirkin --- hw/xen_platform.c | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/hw/xen_platform.c b/hw/xen_platform.c index 9a01735d31..f43e175b4e 100644 --- a/hw/xen_platform.c +++ b/hw/xen_platform.c @@ -290,18 +290,10 @@ static int xen_platform_initfn(PCIDevice *dev) pci_conf = d->pci_dev.config; - pci_config_set_vendor_id(pci_conf, PCI_VENDOR_ID_XEN); - pci_config_set_device_id(pci_conf, PCI_DEVICE_ID_XEN_PLATFORM); - pci_set_word(pci_conf + PCI_SUBSYSTEM_VENDOR_ID, PCI_VENDOR_ID_XEN); - pci_set_word(pci_conf + PCI_SUBSYSTEM_ID, PCI_DEVICE_ID_XEN_PLATFORM); - pci_set_word(pci_conf + PCI_COMMAND, PCI_COMMAND_IO | PCI_COMMAND_MEMORY); - pci_config_set_revision(pci_conf, 1); pci_config_set_prog_interface(pci_conf, 0); - pci_config_set_class(pci_conf, PCI_CLASS_OTHERS << 8 | 0x80); - pci_conf[PCI_INTERRUPT_PIN] = 1; pci_register_bar(&d->pci_dev, 0, 0x100, @@ -330,6 +322,13 @@ static PCIDeviceInfo xen_platform_info = { .qdev.size = sizeof(PCIXenPlatformState), .qdev.vmsd = &vmstate_xen_platform, .qdev.reset = platform_reset, + + .vendor_id = PCI_VENDOR_ID_XEN, + .device_id = PCI_DEVICE_ID_XEN_PLATFORM, + .class_id = PCI_CLASS_OTHERS << 8 | 0x80, + .subsystem_vendor_id = PCI_VENDOR_ID_XEN, + .subsystem_id = PCI_DEVICE_ID_XEN_PLATFORM, + .revision = 1, }; static void xen_platform_register(void) From c8eac1cfa1e9104a658b4614ada758861b8d823a Mon Sep 17 00:00:00 2001 From: "Michael S. Tsirkin" Date: Mon, 20 Jun 2011 13:42:27 +0300 Subject: [PATCH 130/209] virtio: fix indirect descriptor buffer overflow We were previously allowing arbitrarily-long indirect descriptors, which could lead to a buffer overflow in qemu-kvm process. CVE-2011-2212 Signed-off-by: Michael S. Tsirkin --- hw/virtio.c | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/hw/virtio.c b/hw/virtio.c index cc47a06a4e..a8f4940da2 100644 --- a/hw/virtio.c +++ b/hw/virtio.c @@ -449,9 +449,17 @@ int virtqueue_pop(VirtQueue *vq, VirtQueueElement *elem) struct iovec *sg; if (vring_desc_flags(desc_pa, i) & VRING_DESC_F_WRITE) { + if (elem->in_num >= ARRAY_SIZE(elem->in_sg)) { + error_report("Too many write descriptors in indirect table"); + exit(1); + } elem->in_addr[elem->in_num] = vring_desc_addr(desc_pa, i); sg = &elem->in_sg[elem->in_num++]; } else { + if (elem->out_num >= ARRAY_SIZE(elem->out_sg)) { + error_report("Too many read descriptors in indirect table"); + exit(1); + } elem->out_addr[elem->out_num] = vring_desc_addr(desc_pa, i); sg = &elem->out_sg[elem->out_num++]; } From 49e3fdd7f2f3ecccc6f75d976246124d0edaec3f Mon Sep 17 00:00:00 2001 From: Amit Shah Date: Tue, 5 Jul 2011 16:36:39 +0530 Subject: [PATCH 131/209] virtio-serial-bus: Add trace events Add some trace events for messages passed between the guest and host. Signed-off-by: Amit Shah --- hw/virtio-serial-bus.c | 7 +++++++ trace-events | 6 ++++++ 2 files changed, 13 insertions(+) diff --git a/hw/virtio-serial-bus.c b/hw/virtio-serial-bus.c index 7f6db7bffe..9859f9fb95 100644 --- a/hw/virtio-serial-bus.c +++ b/hw/virtio-serial-bus.c @@ -19,6 +19,7 @@ #include "monitor.h" #include "qemu-queue.h" #include "sysbus.h" +#include "trace.h" #include "virtio-serial.h" /* The virtio-serial bus on top of which the ports will ride as devices */ @@ -221,6 +222,7 @@ static size_t send_control_event(VirtIOSerialPort *port, uint16_t event, stw_p(&cpkt.event, event); stw_p(&cpkt.value, value); + trace_virtio_serial_send_control_event(port->id, event, value); return send_control_msg(port, &cpkt, sizeof(cpkt)); } @@ -302,6 +304,7 @@ void virtio_serial_throttle_port(VirtIOSerialPort *port, bool throttle) return; } + trace_virtio_serial_throttle_port(port->id, throttle); port->throttled = throttle; if (throttle) { return; @@ -328,6 +331,8 @@ static void handle_control_message(VirtIOSerial *vser, void *buf, size_t len) cpkt.event = lduw_p(&gcpkt->event); cpkt.value = lduw_p(&gcpkt->value); + trace_virtio_serial_handle_control_message(cpkt.event, cpkt.value); + if (cpkt.event == VIRTIO_CONSOLE_DEVICE_READY) { if (!cpkt.value) { error_report("virtio-serial-bus: Guest failure in adding device %s", @@ -351,6 +356,8 @@ static void handle_control_message(VirtIOSerial *vser, void *buf, size_t len) return; } + trace_virtio_serial_handle_control_message_port(port->id); + info = DO_UPCAST(VirtIOSerialPortInfo, qdev, port->dev.info); switch(cpkt.event) { diff --git a/trace-events b/trace-events index bebf612fe9..59420e8a6e 100644 --- a/trace-events +++ b/trace-events @@ -46,6 +46,12 @@ disable virtio_queue_notify(void *vdev, int n, void *vq) "vdev %p n %d vq %p" disable virtio_irq(void *vq) "vq %p" disable virtio_notify(void *vdev, void *vq) "vdev %p vq %p" +# hw/virtio-serial-bus.c +disable virtio_serial_send_control_event(unsigned int port, uint16_t event, uint16_t value) "port %u, event %u, value %u" +disable virtio_serial_throttle_port(unsigned int port, bool throttle) "port %u, throttle %d" +disable virtio_serial_handle_control_message(uint16_t event, uint16_t value) "event %u, value %u" +disable virtio_serial_handle_control_message_port(unsigned int port) "port %u" + # block.c disable multiwrite_cb(void *mcb, int ret) "mcb %p ret %d" disable bdrv_aio_multiwrite(void *mcb, int num_callbacks, int num_reqs) "mcb %p num_callbacks %d num_reqs %d" From d02e4fa4a8e180c0a312b7c520a167894a0c8a83 Mon Sep 17 00:00:00 2001 From: Amit Shah Date: Tue, 5 Jul 2011 16:37:49 +0530 Subject: [PATCH 132/209] virtio-console: Add some trace events Add some trace events for messages passed between the char layer and the virtio-serial bus. Signed-off-by: Amit Shah --- hw/virtio-console.c | 9 ++++++++- trace-events | 5 +++++ 2 files changed, 13 insertions(+), 1 deletion(-) diff --git a/hw/virtio-console.c b/hw/virtio-console.c index b076331d37..713a761dfa 100644 --- a/hw/virtio-console.c +++ b/hw/virtio-console.c @@ -12,6 +12,7 @@ #include "qemu-char.h" #include "qemu-error.h" +#include "trace.h" #include "virtio-serial.h" typedef struct VirtConsole { @@ -24,8 +25,12 @@ typedef struct VirtConsole { static ssize_t flush_buf(VirtIOSerialPort *port, const uint8_t *buf, size_t len) { VirtConsole *vcon = DO_UPCAST(VirtConsole, port, port); + ssize_t ret; - return qemu_chr_write(vcon->chr, buf, len); + ret = qemu_chr_write(vcon->chr, buf, len); + + trace_virtio_console_flush_buf(port->id, len, ret); + return ret; } /* Callback function that's called when the guest opens the port */ @@ -57,6 +62,7 @@ static void chr_read(void *opaque, const uint8_t *buf, int size) { VirtConsole *vcon = opaque; + trace_virtio_console_chr_read(vcon->port.id, size); virtio_serial_write(&vcon->port, buf, size); } @@ -64,6 +70,7 @@ static void chr_event(void *opaque, int event) { VirtConsole *vcon = opaque; + trace_virtio_console_chr_event(vcon->port.id, event); switch (event) { case CHR_EVENT_OPENED: virtio_serial_open(&vcon->port); diff --git a/trace-events b/trace-events index 59420e8a6e..765a15ed77 100644 --- a/trace-events +++ b/trace-events @@ -52,6 +52,11 @@ disable virtio_serial_throttle_port(unsigned int port, bool throttle) "port %u, disable virtio_serial_handle_control_message(uint16_t event, uint16_t value) "event %u, value %u" disable virtio_serial_handle_control_message_port(unsigned int port) "port %u" +# hw/virtio-console.c +disable virtio_console_flush_buf(unsigned int port, size_t len, ssize_t ret) "port %u, in_len %zu, out_len %zd" +disable virtio_console_chr_read(unsigned int port, int size) "port %u, size %d" +disable virtio_console_chr_event(unsigned int port, int event) "port %u, event %d" + # block.c disable multiwrite_cb(void *mcb, int ret) "mcb %p ret %d" disable bdrv_aio_multiwrite(void *mcb, int num_callbacks, int num_reqs) "mcb %p num_callbacks %d num_reqs %d" From 95c9cde2dff9de9f4afa0450697bd70f56715be1 Mon Sep 17 00:00:00 2001 From: Amit Shah Date: Thu, 7 Jul 2011 18:16:13 +0530 Subject: [PATCH 133/209] virtio-serial-bus: Fix trailing \n in error_report string Markus fixed offenders in the file but one instance sneaked in via another patch. Fix it. Signed-off-by: Amit Shah --- hw/virtio-serial-bus.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hw/virtio-serial-bus.c b/hw/virtio-serial-bus.c index 9859f9fb95..6d73386441 100644 --- a/hw/virtio-serial-bus.c +++ b/hw/virtio-serial-bus.c @@ -351,7 +351,7 @@ static void handle_control_message(VirtIOSerial *vser, void *buf, size_t len) port = find_port_by_id(vser, ldl_p(&gcpkt->id)); if (!port) { - error_report("virtio-serial-bus: Unexpected port id %u for device %s\n", + error_report("virtio-serial-bus: Unexpected port id %u for device %s", ldl_p(&gcpkt->id), vser->bus.qbus.name); return; } From 0219d73283b6399a737ef5a098f849b956618eaa Mon Sep 17 00:00:00 2001 From: Amit Shah Date: Thu, 7 Jul 2011 17:35:27 +0530 Subject: [PATCH 134/209] virtio-console: Prevent abort()s in case of host chardev close A host chardev could close just before the guest sends some data to be written. This will cause an -EPIPE error. This shouldn't be propagated to virtio-serial-bus. Ideally we should close the port once -EPIPE is received, but since the chardev interface doesn't return such meaningful values to its users, all we get is -1 for any kind of error. Just return 0 for now and wait for chardevs to return better error messages to act better on the return messages. Signed-off-by: Amit Shah --- hw/virtio-console.c | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/hw/virtio-console.c b/hw/virtio-console.c index 713a761dfa..7ebfa26516 100644 --- a/hw/virtio-console.c +++ b/hw/virtio-console.c @@ -28,8 +28,22 @@ static ssize_t flush_buf(VirtIOSerialPort *port, const uint8_t *buf, size_t len) ssize_t ret; ret = qemu_chr_write(vcon->chr, buf, len); - trace_virtio_console_flush_buf(port->id, len, ret); + + if (ret < 0) { + /* + * Ideally we'd get a better error code than just -1, but + * that's what the chardev interface gives us right now. If + * we had a finer-grained message, like -EPIPE, we could close + * this connection. Absent such error messages, the most we + * can do is to return 0 here. + * + * This will prevent stray -1 values to go to + * virtio-serial-bus.c and cause abort()s in + * do_flush_queued_data(). + */ + ret = 0; + } return ret; } From a8e0fdd71596631141e8ad3947b115cf8dd3b681 Mon Sep 17 00:00:00 2001 From: MORITA Kazutaka Date: Wed, 6 Jul 2011 03:38:48 +0900 Subject: [PATCH 135/209] sheepdog: add full data preallocation support This introduces qemu-img create option for sheepdog which allows the data to be fully preallocated (note that sheepdog always preallocates metadata). The option is disabled by default and you need to enable it like the following: qemu-img create sheepdog:test -o preallocation=full 1G Signed-off-by: MORITA Kazutaka Signed-off-by: FUJITA Tomonori Signed-off-by: Kevin Wolf --- block/sheepdog.c | 71 ++++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 68 insertions(+), 3 deletions(-) diff --git a/block/sheepdog.c b/block/sheepdog.c index 80d106c2b2..77a4de5100 100644 --- a/block/sheepdog.c +++ b/block/sheepdog.c @@ -1286,6 +1286,49 @@ static int do_sd_create(char *filename, int64_t vdi_size, return 0; } +static int sd_prealloc(const char *filename) +{ + BlockDriverState *bs = NULL; + uint32_t idx, max_idx; + int64_t vdi_size; + void *buf = qemu_mallocz(SD_DATA_OBJ_SIZE); + int ret; + + ret = bdrv_file_open(&bs, filename, BDRV_O_RDWR); + if (ret < 0) { + goto out; + } + + vdi_size = bdrv_getlength(bs); + if (vdi_size < 0) { + ret = vdi_size; + goto out; + } + max_idx = DIV_ROUND_UP(vdi_size, SD_DATA_OBJ_SIZE); + + for (idx = 0; idx < max_idx; idx++) { + /* + * The created image can be a cloned image, so we need to read + * a data from the source image. + */ + ret = bdrv_pread(bs, idx * SD_DATA_OBJ_SIZE, buf, SD_DATA_OBJ_SIZE); + if (ret < 0) { + goto out; + } + ret = bdrv_pwrite(bs, idx * SD_DATA_OBJ_SIZE, buf, SD_DATA_OBJ_SIZE); + if (ret < 0) { + goto out; + } + } +out: + if (bs) { + bdrv_delete(bs); + } + qemu_free(buf); + + return ret; +} + static int sd_create(const char *filename, QEMUOptionParameter *options) { int ret; @@ -1295,13 +1338,15 @@ static int sd_create(const char *filename, QEMUOptionParameter *options) BDRVSheepdogState s; char vdi[SD_MAX_VDI_LEN], tag[SD_MAX_VDI_TAG_LEN]; uint32_t snapid; + int prealloc = 0; + const char *vdiname; - strstart(filename, "sheepdog:", (const char **)&filename); + strstart(filename, "sheepdog:", &vdiname); memset(&s, 0, sizeof(s)); memset(vdi, 0, sizeof(vdi)); memset(tag, 0, sizeof(tag)); - if (parse_vdiname(&s, filename, vdi, &snapid, tag) < 0) { + if (parse_vdiname(&s, vdiname, vdi, &snapid, tag) < 0) { error_report("invalid filename"); return -EINVAL; } @@ -1311,6 +1356,16 @@ static int sd_create(const char *filename, QEMUOptionParameter *options) vdi_size = options->value.n; } else if (!strcmp(options->name, BLOCK_OPT_BACKING_FILE)) { backing_file = options->value.s; + } else if (!strcmp(options->name, BLOCK_OPT_PREALLOC)) { + if (!options->value.s || !strcmp(options->value.s, "off")) { + prealloc = 0; + } else if (!strcmp(options->value.s, "full")) { + prealloc = 1; + } else { + error_report("Invalid preallocation mode: '%s'", + options->value.s); + return -EINVAL; + } } options++; } @@ -1348,7 +1403,12 @@ static int sd_create(const char *filename, QEMUOptionParameter *options) bdrv_delete(bs); } - return do_sd_create((char *)vdi, vdi_size, base_vid, &vid, 0, s.addr, s.port); + ret = do_sd_create(vdi, vdi_size, base_vid, &vid, 0, s.addr, s.port); + if (!prealloc || ret) { + return ret; + } + + return sd_prealloc(filename); } static void sd_close(BlockDriverState *bs) @@ -1984,6 +2044,11 @@ static QEMUOptionParameter sd_create_options[] = { .type = OPT_STRING, .help = "File name of a base image" }, + { + .name = BLOCK_OPT_PREALLOC, + .type = OPT_STRING, + .help = "Preallocation mode (allowed values: off, full)" + }, { NULL } }; From 43642b3803bef28018ca7736be83f2f886d8e42c Mon Sep 17 00:00:00 2001 From: Devin Nakamura Date: Mon, 11 Jul 2011 11:22:16 -0400 Subject: [PATCH 136/209] qemu-io: Fix formatting Replaced tabs with spaces, 8 space indentations with 4 space indentation, and other fixes to better adhere to CODING_STYLE Signed-off-by: Devin Nakamura Signed-off-by: Kevin Wolf --- qemu-io.c | 2420 +++++++++++++++++++++++++++-------------------------- 1 file changed, 1214 insertions(+), 1206 deletions(-) diff --git a/qemu-io.c b/qemu-io.c index dd4ebf537a..e3c825f7e9 100644 --- a/qemu-io.c +++ b/qemu-io.c @@ -20,7 +20,7 @@ #define VERSION "0.0.1" -#define CMD_NOFILE_OK 0x01 +#define CMD_NOFILE_OK 0x01 char *progname; static BlockDriverState *bs; @@ -35,16 +35,16 @@ static int misalign; */ static int parse_pattern(const char *arg) { - char *endptr = NULL; - long pattern; + char *endptr = NULL; + long pattern; - pattern = strtol(arg, &endptr, 0); - if (pattern < 0 || pattern > UCHAR_MAX || *endptr != '\0') { - printf("%s is not a valid pattern byte\n", arg); - return -1; - } + pattern = strtol(arg, &endptr, 0); + if (pattern < 0 || pattern > UCHAR_MAX || *endptr != '\0') { + printf("%s is not a valid pattern byte\n", arg); + return -1; + } - return pattern; + return pattern; } /* @@ -54,70 +54,73 @@ static int parse_pattern(const char *arg) * that is specified on the command line. */ -#define MISALIGN_OFFSET 16 +#define MISALIGN_OFFSET 16 static void *qemu_io_alloc(size_t len, int pattern) { - void *buf; + void *buf; - if (misalign) - len += MISALIGN_OFFSET; - buf = qemu_blockalign(bs, len); - memset(buf, pattern, len); - if (misalign) - buf += MISALIGN_OFFSET; - return buf; + if (misalign) { + len += MISALIGN_OFFSET; + } + buf = qemu_blockalign(bs, len); + memset(buf, pattern, len); + if (misalign) { + buf += MISALIGN_OFFSET; + } + return buf; } static void qemu_io_free(void *p) { - if (misalign) - p -= MISALIGN_OFFSET; - qemu_vfree(p); + if (misalign) { + p -= MISALIGN_OFFSET; + } + qemu_vfree(p); } -static void -dump_buffer(const void *buffer, int64_t offset, int len) +static void dump_buffer(const void *buffer, int64_t offset, int len) { - int i, j; - const uint8_t *p; + int i, j; + const uint8_t *p; - for (i = 0, p = buffer; i < len; i += 16) { - const uint8_t *s = p; + for (i = 0, p = buffer; i < len; i += 16) { + const uint8_t *s = p; - printf("%08" PRIx64 ": ", offset + i); - for (j = 0; j < 16 && i + j < len; j++, p++) - printf("%02x ", *p); - printf(" "); - for (j = 0; j < 16 && i + j < len; j++, s++) { - if (isalnum(*s)) - printf("%c", *s); - else - printf("."); - } - printf("\n"); - } + printf("%08" PRIx64 ": ", offset + i); + for (j = 0; j < 16 && i + j < len; j++, p++) { + printf("%02x ", *p); + } + printf(" "); + for (j = 0; j < 16 && i + j < len; j++, s++) { + if (isalnum(*s)) { + printf("%c", *s); + } else { + printf("."); + } + } + printf("\n"); + } } -static void -print_report(const char *op, struct timeval *t, int64_t offset, - int count, int total, int cnt, int Cflag) +static void print_report(const char *op, struct timeval *t, int64_t offset, + int count, int total, int cnt, int Cflag) { - char s1[64], s2[64], ts[64]; + char s1[64], s2[64], ts[64]; - timestr(t, ts, sizeof(ts), Cflag ? VERBOSE_FIXED_TIME : 0); - if (!Cflag) { - cvtstr((double)total, s1, sizeof(s1)); - cvtstr(tdiv((double)total, *t), s2, sizeof(s2)); - printf("%s %d/%d bytes at offset %" PRId64 "\n", - op, total, count, offset); - printf("%s, %d ops; %s (%s/sec and %.4f ops/sec)\n", - s1, cnt, ts, s2, tdiv((double)cnt, *t)); - } else {/* bytes,ops,time,bytes/sec,ops/sec */ - printf("%d,%d,%s,%.3f,%.3f\n", - total, cnt, ts, - tdiv((double)total, *t), - tdiv((double)cnt, *t)); - } + timestr(t, ts, sizeof(ts), Cflag ? VERBOSE_FIXED_TIME : 0); + if (!Cflag) { + cvtstr((double)total, s1, sizeof(s1)); + cvtstr(tdiv((double)total, *t), s2, sizeof(s2)); + printf("%s %d/%d bytes at offset %" PRId64 "\n", + op, total, count, offset); + printf("%s, %d ops; %s (%s/sec and %.4f ops/sec)\n", + s1, cnt, ts, s2, tdiv((double)cnt, *t)); + } else {/* bytes,ops,time,bytes/sec,ops/sec */ + printf("%d,%d,%s,%.3f,%.3f\n", + total, cnt, ts, + tdiv((double)total, *t), + tdiv((double)cnt, *t)); + } } /* @@ -127,192 +130,200 @@ print_report(const char *op, struct timeval *t, int64_t offset, static void * create_iovec(QEMUIOVector *qiov, char **argv, int nr_iov, int pattern) { - size_t *sizes = calloc(nr_iov, sizeof(size_t)); - size_t count = 0; - void *buf = NULL; - void *p; - int i; + size_t *sizes = calloc(nr_iov, sizeof(size_t)); + size_t count = 0; + void *buf = NULL; + void *p; + int i; - for (i = 0; i < nr_iov; i++) { - char *arg = argv[i]; - int64_t len; + for (i = 0; i < nr_iov; i++) { + char *arg = argv[i]; + int64_t len; - len = cvtnum(arg); - if (len < 0) { - printf("non-numeric length argument -- %s\n", arg); - goto fail; - } + len = cvtnum(arg); + if (len < 0) { + printf("non-numeric length argument -- %s\n", arg); + goto fail; + } - /* should be SIZE_T_MAX, but that doesn't exist */ - if (len > INT_MAX) { - printf("too large length argument -- %s\n", arg); - goto fail; - } + /* should be SIZE_T_MAX, but that doesn't exist */ + if (len > INT_MAX) { + printf("too large length argument -- %s\n", arg); + goto fail; + } - if (len & 0x1ff) { - printf("length argument %" PRId64 - " is not sector aligned\n", len); - goto fail; - } + if (len & 0x1ff) { + printf("length argument %" PRId64 + " is not sector aligned\n", len); + goto fail; + } - sizes[i] = len; - count += len; - } + sizes[i] = len; + count += len; + } - qemu_iovec_init(qiov, nr_iov); + qemu_iovec_init(qiov, nr_iov); - buf = p = qemu_io_alloc(count, pattern); + buf = p = qemu_io_alloc(count, pattern); - for (i = 0; i < nr_iov; i++) { - qemu_iovec_add(qiov, p, sizes[i]); - p += sizes[i]; - } + for (i = 0; i < nr_iov; i++) { + qemu_iovec_add(qiov, p, sizes[i]); + p += sizes[i]; + } fail: - free(sizes); - return buf; + free(sizes); + return buf; } static int do_read(char *buf, int64_t offset, int count, int *total) { - int ret; + int ret; - ret = bdrv_read(bs, offset >> 9, (uint8_t *)buf, count >> 9); - if (ret < 0) - return ret; - *total = count; - return 1; + ret = bdrv_read(bs, offset >> 9, (uint8_t *)buf, count >> 9); + if (ret < 0) { + return ret; + } + *total = count; + return 1; } static int do_write(char *buf, int64_t offset, int count, int *total) { - int ret; + int ret; - ret = bdrv_write(bs, offset >> 9, (uint8_t *)buf, count >> 9); - if (ret < 0) - return ret; - *total = count; - return 1; + ret = bdrv_write(bs, offset >> 9, (uint8_t *)buf, count >> 9); + if (ret < 0) { + return ret; + } + *total = count; + return 1; } static int do_pread(char *buf, int64_t offset, int count, int *total) { - *total = bdrv_pread(bs, offset, (uint8_t *)buf, count); - if (*total < 0) - return *total; - return 1; + *total = bdrv_pread(bs, offset, (uint8_t *)buf, count); + if (*total < 0) { + return *total; + } + return 1; } static int do_pwrite(char *buf, int64_t offset, int count, int *total) { - *total = bdrv_pwrite(bs, offset, (uint8_t *)buf, count); - if (*total < 0) - return *total; - return 1; + *total = bdrv_pwrite(bs, offset, (uint8_t *)buf, count); + if (*total < 0) { + return *total; + } + return 1; } static int do_load_vmstate(char *buf, int64_t offset, int count, int *total) { - *total = bdrv_load_vmstate(bs, (uint8_t *)buf, offset, count); - if (*total < 0) - return *total; - return 1; + *total = bdrv_load_vmstate(bs, (uint8_t *)buf, offset, count); + if (*total < 0) { + return *total; + } + return 1; } static int do_save_vmstate(char *buf, int64_t offset, int count, int *total) { - *total = bdrv_save_vmstate(bs, (uint8_t *)buf, offset, count); - if (*total < 0) - return *total; - return 1; + *total = bdrv_save_vmstate(bs, (uint8_t *)buf, offset, count); + if (*total < 0) { + return *total; + } + return 1; } #define NOT_DONE 0x7fffffff static void aio_rw_done(void *opaque, int ret) { - *(int *)opaque = ret; + *(int *)opaque = ret; } static int do_aio_readv(QEMUIOVector *qiov, int64_t offset, int *total) { - BlockDriverAIOCB *acb; - int async_ret = NOT_DONE; + BlockDriverAIOCB *acb; + int async_ret = NOT_DONE; - acb = bdrv_aio_readv(bs, offset >> 9, qiov, qiov->size >> 9, - aio_rw_done, &async_ret); - if (!acb) - return -EIO; + acb = bdrv_aio_readv(bs, offset >> 9, qiov, qiov->size >> 9, + aio_rw_done, &async_ret); + if (!acb) { + return -EIO; + } + while (async_ret == NOT_DONE) { + qemu_aio_wait(); + } - while (async_ret == NOT_DONE) - qemu_aio_wait(); - - *total = qiov->size; - return async_ret < 0 ? async_ret : 1; + *total = qiov->size; + return async_ret < 0 ? async_ret : 1; } static int do_aio_writev(QEMUIOVector *qiov, int64_t offset, int *total) { - BlockDriverAIOCB *acb; - int async_ret = NOT_DONE; + BlockDriverAIOCB *acb; + int async_ret = NOT_DONE; - acb = bdrv_aio_writev(bs, offset >> 9, qiov, qiov->size >> 9, - aio_rw_done, &async_ret); - if (!acb) - return -EIO; + acb = bdrv_aio_writev(bs, offset >> 9, qiov, qiov->size >> 9, + aio_rw_done, &async_ret); + if (!acb) { + return -EIO; + } - while (async_ret == NOT_DONE) - qemu_aio_wait(); + while (async_ret == NOT_DONE) { + qemu_aio_wait(); + } - *total = qiov->size; - return async_ret < 0 ? async_ret : 1; + *total = qiov->size; + return async_ret < 0 ? async_ret : 1; } struct multiwrite_async_ret { - int num_done; - int error; + int num_done; + int error; }; static void multiwrite_cb(void *opaque, int ret) { - struct multiwrite_async_ret *async_ret = opaque; + struct multiwrite_async_ret *async_ret = opaque; - async_ret->num_done++; - if (ret < 0) { - async_ret->error = ret; - } + async_ret->num_done++; + if (ret < 0) { + async_ret->error = ret; + } } static int do_aio_multiwrite(BlockRequest* reqs, int num_reqs, int *total) { - int i, ret; - struct multiwrite_async_ret async_ret = { - .num_done = 0, - .error = 0, - }; + int i, ret; + struct multiwrite_async_ret async_ret = { + .num_done = 0, + .error = 0, + }; - *total = 0; - for (i = 0; i < num_reqs; i++) { - reqs[i].cb = multiwrite_cb; - reqs[i].opaque = &async_ret; - *total += reqs[i].qiov->size; - } + *total = 0; + for (i = 0; i < num_reqs; i++) { + reqs[i].cb = multiwrite_cb; + reqs[i].opaque = &async_ret; + *total += reqs[i].qiov->size; + } - ret = bdrv_aio_multiwrite(bs, reqs, num_reqs); - if (ret < 0) { - return ret; - } + ret = bdrv_aio_multiwrite(bs, reqs, num_reqs); + if (ret < 0) { + return ret; + } - while (async_ret.num_done < num_reqs) { - qemu_aio_wait(); - } + while (async_ret.num_done < num_reqs) { + qemu_aio_wait(); + } - return async_ret.error < 0 ? async_ret.error : 1; + return async_ret.error < 0 ? async_ret.error : 1; } -static void -read_help(void) +static void read_help(void) { - printf( + printf( "\n" " reads a range of bytes from the given offset\n" "\n" @@ -335,94 +346,95 @@ read_help(void) static int read_f(int argc, char **argv); static const cmdinfo_t read_cmd = { - .name = "read", - .altname = "r", - .cfunc = read_f, - .argmin = 2, - .argmax = -1, - .args = "[-abCpqv] [-P pattern [-s off] [-l len]] off len", - .oneline = "reads a number of bytes at a specified offset", - .help = read_help, + .name = "read", + .altname = "r", + .cfunc = read_f, + .argmin = 2, + .argmax = -1, + .args = "[-abCpqv] [-P pattern [-s off] [-l len]] off len", + .oneline = "reads a number of bytes at a specified offset", + .help = read_help, }; -static int -read_f(int argc, char **argv) +static int read_f(int argc, char **argv) { - struct timeval t1, t2; - int Cflag = 0, pflag = 0, qflag = 0, vflag = 0; - int Pflag = 0, sflag = 0, lflag = 0, bflag = 0; - int c, cnt; - char *buf; - int64_t offset; - int count; - /* Some compilers get confused and warn if this is not initialized. */ - int total = 0; - int pattern = 0, pattern_offset = 0, pattern_count = 0; + struct timeval t1, t2; + int Cflag = 0, pflag = 0, qflag = 0, vflag = 0; + int Pflag = 0, sflag = 0, lflag = 0, bflag = 0; + int c, cnt; + char *buf; + int64_t offset; + int count; + /* Some compilers get confused and warn if this is not initialized. */ + int total = 0; + int pattern = 0, pattern_offset = 0, pattern_count = 0; - while ((c = getopt(argc, argv, "bCl:pP:qs:v")) != EOF) { - switch (c) { - case 'b': - bflag = 1; - break; - case 'C': - Cflag = 1; - break; - case 'l': - lflag = 1; - pattern_count = cvtnum(optarg); - if (pattern_count < 0) { - printf("non-numeric length argument -- %s\n", optarg); - return 0; - } - break; - case 'p': - pflag = 1; - break; - case 'P': - Pflag = 1; - pattern = parse_pattern(optarg); - if (pattern < 0) - return 0; - break; - case 'q': - qflag = 1; - break; - case 's': - sflag = 1; - pattern_offset = cvtnum(optarg); - if (pattern_offset < 0) { - printf("non-numeric length argument -- %s\n", optarg); - return 0; - } - break; - case 'v': - vflag = 1; - break; - default: - return command_usage(&read_cmd); - } - } + while ((c = getopt(argc, argv, "bCl:pP:qs:v")) != EOF) { + switch (c) { + case 'b': + bflag = 1; + break; + case 'C': + Cflag = 1; + break; + case 'l': + lflag = 1; + pattern_count = cvtnum(optarg); + if (pattern_count < 0) { + printf("non-numeric length argument -- %s\n", optarg); + return 0; + } + break; + case 'p': + pflag = 1; + break; + case 'P': + Pflag = 1; + pattern = parse_pattern(optarg); + if (pattern < 0) { + return 0; + } + break; + case 'q': + qflag = 1; + break; + case 's': + sflag = 1; + pattern_offset = cvtnum(optarg); + if (pattern_offset < 0) { + printf("non-numeric length argument -- %s\n", optarg); + return 0; + } + break; + case 'v': + vflag = 1; + break; + default: + return command_usage(&read_cmd); + } + } - if (optind != argc - 2) - return command_usage(&read_cmd); + if (optind != argc - 2) { + return command_usage(&read_cmd); + } - if (bflag && pflag) { - printf("-b and -p cannot be specified at the same time\n"); - return 0; - } + if (bflag && pflag) { + printf("-b and -p cannot be specified at the same time\n"); + return 0; + } - offset = cvtnum(argv[optind]); - if (offset < 0) { - printf("non-numeric length argument -- %s\n", argv[optind]); - return 0; - } + offset = cvtnum(argv[optind]); + if (offset < 0) { + printf("non-numeric length argument -- %s\n", argv[optind]); + return 0; + } - optind++; - count = cvtnum(argv[optind]); - if (count < 0) { - printf("non-numeric length argument -- %s\n", argv[optind]); - return 0; - } + optind++; + count = cvtnum(argv[optind]); + if (count < 0) { + printf("non-numeric length argument -- %s\n", argv[optind]); + return 0; + } if (!Pflag && (lflag || sflag)) { return command_usage(&read_cmd); @@ -437,66 +449,67 @@ read_f(int argc, char **argv) return 0; } - if (!pflag) - if (offset & 0x1ff) { - printf("offset %" PRId64 " is not sector aligned\n", - offset); - return 0; + if (!pflag) + if (offset & 0x1ff) { + printf("offset %" PRId64 " is not sector aligned\n", + offset); + return 0; + } + if (count & 0x1ff) { + printf("count %d is not sector aligned\n", + count); + return 0; + } - if (count & 0x1ff) { - printf("count %d is not sector aligned\n", - count); - return 0; - } - } + buf = qemu_io_alloc(count, 0xab); - buf = qemu_io_alloc(count, 0xab); + gettimeofday(&t1, NULL); + if (pflag) { + cnt = do_pread(buf, offset, count, &total); + } else if (bflag) { + cnt = do_load_vmstate(buf, offset, count, &total); + } else { + cnt = do_read(buf, offset, count, &total); + } + gettimeofday(&t2, NULL); - gettimeofday(&t1, NULL); - if (pflag) - cnt = do_pread(buf, offset, count, &total); - else if (bflag) - cnt = do_load_vmstate(buf, offset, count, &total); - else - cnt = do_read(buf, offset, count, &total); - gettimeofday(&t2, NULL); + if (cnt < 0) { + printf("read failed: %s\n", strerror(-cnt)); + goto out; + } - if (cnt < 0) { - printf("read failed: %s\n", strerror(-cnt)); - goto out; - } + if (Pflag) { + void *cmp_buf = malloc(pattern_count); + memset(cmp_buf, pattern, pattern_count); + if (memcmp(buf + pattern_offset, cmp_buf, pattern_count)) { + printf("Pattern verification failed at offset %" + PRId64 ", %d bytes\n", + offset + pattern_offset, pattern_count); + } + free(cmp_buf); + } - if (Pflag) { - void* cmp_buf = malloc(pattern_count); - memset(cmp_buf, pattern, pattern_count); - if (memcmp(buf + pattern_offset, cmp_buf, pattern_count)) { - printf("Pattern verification failed at offset %" - PRId64 ", %d bytes\n", - offset + pattern_offset, pattern_count); - } - free(cmp_buf); - } + if (qflag) { + goto out; + } - if (qflag) - goto out; + if (vflag) { + dump_buffer(buf, offset, count); + } - if (vflag) - dump_buffer(buf, offset, count); - - /* Finally, report back -- -C gives a parsable format */ - t2 = tsub(t2, t1); - print_report("read", &t2, offset, count, total, cnt, Cflag); + /* Finally, report back -- -C gives a parsable format */ + t2 = tsub(t2, t1); + print_report("read", &t2, offset, count, total, cnt, Cflag); out: - qemu_io_free(buf); + qemu_io_free(buf); - return 0; + return 0; } -static void -readv_help(void) +static void readv_help(void) { - printf( + printf( "\n" " reads a range of bytes from the given offset into multiple buffers\n" "\n" @@ -516,111 +529,112 @@ readv_help(void) static int readv_f(int argc, char **argv); static const cmdinfo_t readv_cmd = { - .name = "readv", - .cfunc = readv_f, - .argmin = 2, - .argmax = -1, - .args = "[-Cqv] [-P pattern ] off len [len..]", - .oneline = "reads a number of bytes at a specified offset", - .help = readv_help, + .name = "readv", + .cfunc = readv_f, + .argmin = 2, + .argmax = -1, + .args = "[-Cqv] [-P pattern ] off len [len..]", + .oneline = "reads a number of bytes at a specified offset", + .help = readv_help, }; -static int -readv_f(int argc, char **argv) +static int readv_f(int argc, char **argv) { - struct timeval t1, t2; - int Cflag = 0, qflag = 0, vflag = 0; - int c, cnt; - char *buf; - int64_t offset; - /* Some compilers get confused and warn if this is not initialized. */ - int total = 0; - int nr_iov; - QEMUIOVector qiov; - int pattern = 0; - int Pflag = 0; + struct timeval t1, t2; + int Cflag = 0, qflag = 0, vflag = 0; + int c, cnt; + char *buf; + int64_t offset; + /* Some compilers get confused and warn if this is not initialized. */ + int total = 0; + int nr_iov; + QEMUIOVector qiov; + int pattern = 0; + int Pflag = 0; - while ((c = getopt(argc, argv, "CP:qv")) != EOF) { - switch (c) { - case 'C': - Cflag = 1; - break; - case 'P': - Pflag = 1; - pattern = parse_pattern(optarg); - if (pattern < 0) - return 0; - break; - case 'q': - qflag = 1; - break; - case 'v': - vflag = 1; - break; - default: - return command_usage(&readv_cmd); - } - } + while ((c = getopt(argc, argv, "CP:qv")) != EOF) { + switch (c) { + case 'C': + Cflag = 1; + break; + case 'P': + Pflag = 1; + pattern = parse_pattern(optarg); + if (pattern < 0) { + return 0; + } + break; + case 'q': + qflag = 1; + break; + case 'v': + vflag = 1; + break; + default: + return command_usage(&readv_cmd); + } + } - if (optind > argc - 2) - return command_usage(&readv_cmd); + if (optind > argc - 2) { + return command_usage(&readv_cmd); + } - offset = cvtnum(argv[optind]); - if (offset < 0) { - printf("non-numeric length argument -- %s\n", argv[optind]); - return 0; - } - optind++; + offset = cvtnum(argv[optind]); + if (offset < 0) { + printf("non-numeric length argument -- %s\n", argv[optind]); + return 0; + } + optind++; - if (offset & 0x1ff) { - printf("offset %" PRId64 " is not sector aligned\n", - offset); - return 0; - } + if (offset & 0x1ff) { + printf("offset %" PRId64 " is not sector aligned\n", + offset); + return 0; + } - nr_iov = argc - optind; - buf = create_iovec(&qiov, &argv[optind], nr_iov, 0xab); + nr_iov = argc - optind; + buf = create_iovec(&qiov, &argv[optind], nr_iov, 0xab); - gettimeofday(&t1, NULL); - cnt = do_aio_readv(&qiov, offset, &total); - gettimeofday(&t2, NULL); + gettimeofday(&t1, NULL); + cnt = do_aio_readv(&qiov, offset, &total); + gettimeofday(&t2, NULL); - if (cnt < 0) { - printf("readv failed: %s\n", strerror(-cnt)); - goto out; - } + if (cnt < 0) { + printf("readv failed: %s\n", strerror(-cnt)); + goto out; + } - if (Pflag) { - void* cmp_buf = malloc(qiov.size); - memset(cmp_buf, pattern, qiov.size); - if (memcmp(buf, cmp_buf, qiov.size)) { - printf("Pattern verification failed at offset %" - PRId64 ", %zd bytes\n", - offset, qiov.size); - } - free(cmp_buf); - } + if (Pflag) { + void *cmp_buf = malloc(qiov.size); + memset(cmp_buf, pattern, qiov.size); + if (memcmp(buf, cmp_buf, qiov.size)) { + printf("Pattern verification failed at offset %" + PRId64 ", %zd bytes\n", offset, qiov.size); + } + free(cmp_buf); + } - if (qflag) - goto out; + if (qflag) { + goto out; + } - if (vflag) - dump_buffer(buf, offset, qiov.size); + if (vflag) { + dump_buffer(buf, offset, qiov.size); + } - /* Finally, report back -- -C gives a parsable format */ - t2 = tsub(t2, t1); - print_report("read", &t2, offset, qiov.size, total, cnt, Cflag); + /* Finally, report back -- -C gives a parsable format */ + t2 = tsub(t2, t1); + print_report("read", &t2, offset, qiov.size, total, cnt, Cflag); out: - qemu_io_free(buf); - return 0; + qemu_io_free(buf); + return 0; } -static void -write_help(void) +static void write_help(void) { - printf( + printf( "\n" " writes a range of bytes from the given offset\n" "\n" @@ -640,121 +654,124 @@ write_help(void) static int write_f(int argc, char **argv); static const cmdinfo_t write_cmd = { - .name = "write", - .altname = "w", - .cfunc = write_f, - .argmin = 2, - .argmax = -1, - .args = "[-abCpq] [-P pattern ] off len", - .oneline = "writes a number of bytes at a specified offset", - .help = write_help, + .name = "write", + .altname = "w", + .cfunc = write_f, + .argmin = 2, + .argmax = -1, + .args = "[-abCpq] [-P pattern ] off len", + .oneline = "writes a number of bytes at a specified offset", + .help = write_help, }; -static int -write_f(int argc, char **argv) +static int write_f(int argc, char **argv) { - struct timeval t1, t2; - int Cflag = 0, pflag = 0, qflag = 0, bflag = 0; - int c, cnt; - char *buf; - int64_t offset; - int count; - /* Some compilers get confused and warn if this is not initialized. */ - int total = 0; - int pattern = 0xcd; + struct timeval t1, t2; + int Cflag = 0, pflag = 0, qflag = 0, bflag = 0; + int c, cnt; + char *buf; + int64_t offset; + int count; + /* Some compilers get confused and warn if this is not initialized. */ + int total = 0; + int pattern = 0xcd; - while ((c = getopt(argc, argv, "bCpP:q")) != EOF) { - switch (c) { - case 'b': - bflag = 1; - break; - case 'C': - Cflag = 1; - break; - case 'p': - pflag = 1; - break; - case 'P': - pattern = parse_pattern(optarg); - if (pattern < 0) - return 0; - break; - case 'q': - qflag = 1; - break; - default: - return command_usage(&write_cmd); - } - } + while ((c = getopt(argc, argv, "bCpP:q")) != EOF) { + switch (c) { + case 'b': + bflag = 1; + break; + case 'C': + Cflag = 1; + break; + case 'p': + pflag = 1; + break; + case 'P': + pattern = parse_pattern(optarg); + if (pattern < 0) { + return 0; + } + break; + case 'q': + qflag = 1; + break; + default: + return command_usage(&write_cmd); + } + } - if (optind != argc - 2) - return command_usage(&write_cmd); + if (optind != argc - 2) { + return command_usage(&write_cmd); + } - if (bflag && pflag) { - printf("-b and -p cannot be specified at the same time\n"); - return 0; - } + if (bflag && pflag) { + printf("-b and -p cannot be specified at the same time\n"); + return 0; + } - offset = cvtnum(argv[optind]); - if (offset < 0) { - printf("non-numeric length argument -- %s\n", argv[optind]); - return 0; - } + offset = cvtnum(argv[optind]); + if (offset < 0) { + printf("non-numeric length argument -- %s\n", argv[optind]); + return 0; + } - optind++; - count = cvtnum(argv[optind]); - if (count < 0) { - printf("non-numeric length argument -- %s\n", argv[optind]); - return 0; - } + optind++; + count = cvtnum(argv[optind]); + if (count < 0) { + printf("non-numeric length argument -- %s\n", argv[optind]); + return 0; + } - if (!pflag) { - if (offset & 0x1ff) { - printf("offset %" PRId64 " is not sector aligned\n", - offset); - return 0; - } + if (!pflag) { + if (offset & 0x1ff) { + printf("offset %" PRId64 " is not sector aligned\n", + offset); + return 0; + } - if (count & 0x1ff) { - printf("count %d is not sector aligned\n", - count); - return 0; - } - } + if (count & 0x1ff) { + printf("count %d is not sector aligned\n", + count); + return 0; + } + } - buf = qemu_io_alloc(count, pattern); + buf = qemu_io_alloc(count, pattern); - gettimeofday(&t1, NULL); - if (pflag) - cnt = do_pwrite(buf, offset, count, &total); - else if (bflag) - cnt = do_save_vmstate(buf, offset, count, &total); - else - cnt = do_write(buf, offset, count, &total); - gettimeofday(&t2, NULL); + gettimeofday(&t1, NULL); + if (pflag) { + cnt = do_pwrite(buf, offset, count, &total); + } else if (bflag) { + cnt = do_save_vmstate(buf, offset, count, &total); + } else { + cnt = do_write(buf, offset, count, &total); + } + gettimeofday(&t2, NULL); - if (cnt < 0) { - printf("write failed: %s\n", strerror(-cnt)); - goto out; - } + if (cnt < 0) { + printf("write failed: %s\n", strerror(-cnt)); + goto out; + } - if (qflag) - goto out; + if (qflag) { + goto out; + } - /* Finally, report back -- -C gives a parsable format */ - t2 = tsub(t2, t1); - print_report("wrote", &t2, offset, count, total, cnt, Cflag); + /* Finally, report back -- -C gives a parsable format */ + t2 = tsub(t2, t1); + print_report("wrote", &t2, offset, count, total, cnt, Cflag); out: - qemu_io_free(buf); + qemu_io_free(buf); - return 0; + return 0; } static void writev_help(void) { - printf( + printf( "\n" " writes a range of bytes from the given offset source from multiple buffers\n" "\n" @@ -772,90 +789,91 @@ writev_help(void) static int writev_f(int argc, char **argv); static const cmdinfo_t writev_cmd = { - .name = "writev", - .cfunc = writev_f, - .argmin = 2, - .argmax = -1, - .args = "[-Cq] [-P pattern ] off len [len..]", - .oneline = "writes a number of bytes at a specified offset", - .help = writev_help, + .name = "writev", + .cfunc = writev_f, + .argmin = 2, + .argmax = -1, + .args = "[-Cq] [-P pattern ] off len [len..]", + .oneline = "writes a number of bytes at a specified offset", + .help = writev_help, }; -static int -writev_f(int argc, char **argv) +static int writev_f(int argc, char **argv) { - struct timeval t1, t2; - int Cflag = 0, qflag = 0; - int c, cnt; - char *buf; - int64_t offset; - /* Some compilers get confused and warn if this is not initialized. */ - int total = 0; - int nr_iov; - int pattern = 0xcd; - QEMUIOVector qiov; + struct timeval t1, t2; + int Cflag = 0, qflag = 0; + int c, cnt; + char *buf; + int64_t offset; + /* Some compilers get confused and warn if this is not initialized. */ + int total = 0; + int nr_iov; + int pattern = 0xcd; + QEMUIOVector qiov; - while ((c = getopt(argc, argv, "CqP:")) != EOF) { - switch (c) { - case 'C': - Cflag = 1; - break; - case 'q': - qflag = 1; - break; - case 'P': - pattern = parse_pattern(optarg); - if (pattern < 0) - return 0; - break; - default: - return command_usage(&writev_cmd); - } - } + while ((c = getopt(argc, argv, "CqP:")) != EOF) { + switch (c) { + case 'C': + Cflag = 1; + break; + case 'q': + qflag = 1; + break; + case 'P': + pattern = parse_pattern(optarg); + if (pattern < 0) { + return 0; + } + break; + default: + return command_usage(&writev_cmd); + } + } - if (optind > argc - 2) - return command_usage(&writev_cmd); + if (optind > argc - 2) { + return command_usage(&writev_cmd); + } - offset = cvtnum(argv[optind]); - if (offset < 0) { - printf("non-numeric length argument -- %s\n", argv[optind]); - return 0; - } - optind++; + offset = cvtnum(argv[optind]); + if (offset < 0) { + printf("non-numeric length argument -- %s\n", argv[optind]); + return 0; + } + optind++; - if (offset & 0x1ff) { - printf("offset %" PRId64 " is not sector aligned\n", - offset); - return 0; - } + if (offset & 0x1ff) { + printf("offset %" PRId64 " is not sector aligned\n", + offset); + return 0; + } - nr_iov = argc - optind; - buf = create_iovec(&qiov, &argv[optind], nr_iov, pattern); + nr_iov = argc - optind; + buf = create_iovec(&qiov, &argv[optind], nr_iov, pattern); - gettimeofday(&t1, NULL); - cnt = do_aio_writev(&qiov, offset, &total); - gettimeofday(&t2, NULL); + gettimeofday(&t1, NULL); + cnt = do_aio_writev(&qiov, offset, &total); + gettimeofday(&t2, NULL); - if (cnt < 0) { - printf("writev failed: %s\n", strerror(-cnt)); - goto out; - } + if (cnt < 0) { + printf("writev failed: %s\n", strerror(-cnt)); + goto out; + } - if (qflag) - goto out; + if (qflag) { + goto out; + } - /* Finally, report back -- -C gives a parsable format */ - t2 = tsub(t2, t1); - print_report("wrote", &t2, offset, qiov.size, total, cnt, Cflag); + /* Finally, report back -- -C gives a parsable format */ + t2 = tsub(t2, t1); + print_report("wrote", &t2, offset, qiov.size, total, cnt, Cflag); out: - qemu_io_free(buf); - return 0; + qemu_io_free(buf); + return 0; } -static void -multiwrite_help(void) +static void multiwrite_help(void) { - printf( + printf( "\n" " writes a range of bytes from the given offset source from multiple buffers,\n" " in a batch of requests that may be merged by qemu\n" @@ -876,217 +894,215 @@ multiwrite_help(void) static int multiwrite_f(int argc, char **argv); static const cmdinfo_t multiwrite_cmd = { - .name = "multiwrite", - .cfunc = multiwrite_f, - .argmin = 2, - .argmax = -1, - .args = "[-Cq] [-P pattern ] off len [len..] [; off len [len..]..]", - .oneline = "issues multiple write requests at once", - .help = multiwrite_help, + .name = "multiwrite", + .cfunc = multiwrite_f, + .argmin = 2, + .argmax = -1, + .args = "[-Cq] [-P pattern ] off len [len..] [; off len [len..]..]", + .oneline = "issues multiple write requests at once", + .help = multiwrite_help, }; -static int -multiwrite_f(int argc, char **argv) +static int multiwrite_f(int argc, char **argv) { - struct timeval t1, t2; - int Cflag = 0, qflag = 0; - int c, cnt; - char **buf; - int64_t offset, first_offset = 0; - /* Some compilers get confused and warn if this is not initialized. */ - int total = 0; - int nr_iov; - int nr_reqs; - int pattern = 0xcd; - QEMUIOVector *qiovs; - int i; - BlockRequest *reqs; + struct timeval t1, t2; + int Cflag = 0, qflag = 0; + int c, cnt; + char **buf; + int64_t offset, first_offset = 0; + /* Some compilers get confused and warn if this is not initialized. */ + int total = 0; + int nr_iov; + int nr_reqs; + int pattern = 0xcd; + QEMUIOVector *qiovs; + int i; + BlockRequest *reqs; - while ((c = getopt(argc, argv, "CqP:")) != EOF) { - switch (c) { - case 'C': - Cflag = 1; - break; - case 'q': - qflag = 1; - break; - case 'P': - pattern = parse_pattern(optarg); - if (pattern < 0) - return 0; - break; - default: - return command_usage(&writev_cmd); - } - } + while ((c = getopt(argc, argv, "CqP:")) != EOF) { + switch (c) { + case 'C': + Cflag = 1; + break; + case 'q': + qflag = 1; + break; + case 'P': + pattern = parse_pattern(optarg); + if (pattern < 0) { + return 0; + } + break; + default: + return command_usage(&writev_cmd); + } + } - if (optind > argc - 2) - return command_usage(&writev_cmd); + if (optind > argc - 2) { + return command_usage(&writev_cmd); + } - nr_reqs = 1; - for (i = optind; i < argc; i++) { - if (!strcmp(argv[i], ";")) { - nr_reqs++; - } - } + nr_reqs = 1; + for (i = optind; i < argc; i++) { + if (!strcmp(argv[i], ";")) { + nr_reqs++; + } + } - reqs = qemu_malloc(nr_reqs * sizeof(*reqs)); - buf = qemu_malloc(nr_reqs * sizeof(*buf)); - qiovs = qemu_malloc(nr_reqs * sizeof(*qiovs)); + reqs = qemu_malloc(nr_reqs * sizeof(*reqs)); + buf = qemu_malloc(nr_reqs * sizeof(*buf)); + qiovs = qemu_malloc(nr_reqs * sizeof(*qiovs)); - for (i = 0; i < nr_reqs; i++) { - int j; + for (i = 0; i < nr_reqs; i++) { + int j; - /* Read the offset of the request */ - offset = cvtnum(argv[optind]); - if (offset < 0) { - printf("non-numeric offset argument -- %s\n", argv[optind]); - return 0; - } - optind++; + /* Read the offset of the request */ + offset = cvtnum(argv[optind]); + if (offset < 0) { + printf("non-numeric offset argument -- %s\n", argv[optind]); + return 0; + } + optind++; - if (offset & 0x1ff) { - printf("offset %lld is not sector aligned\n", - (long long)offset); - return 0; - } + if (offset & 0x1ff) { + printf("offset %lld is not sector aligned\n", + (long long)offset); + return 0; + } if (i == 0) { first_offset = offset; } - /* Read lengths for qiov entries */ - for (j = optind; j < argc; j++) { - if (!strcmp(argv[j], ";")) { - break; - } - } + /* Read lengths for qiov entries */ + for (j = optind; j < argc; j++) { + if (!strcmp(argv[j], ";")) { + break; + } + } - nr_iov = j - optind; + nr_iov = j - optind; - /* Build request */ - reqs[i].qiov = &qiovs[i]; - buf[i] = create_iovec(reqs[i].qiov, &argv[optind], nr_iov, pattern); - reqs[i].sector = offset >> 9; - reqs[i].nb_sectors = reqs[i].qiov->size >> 9; + /* Build request */ + reqs[i].qiov = &qiovs[i]; + buf[i] = create_iovec(reqs[i].qiov, &argv[optind], nr_iov, pattern); + reqs[i].sector = offset >> 9; + reqs[i].nb_sectors = reqs[i].qiov->size >> 9; - optind = j + 1; + optind = j + 1; - offset += reqs[i].qiov->size; - pattern++; - } + offset += reqs[i].qiov->size; + pattern++; + } - gettimeofday(&t1, NULL); - cnt = do_aio_multiwrite(reqs, nr_reqs, &total); - gettimeofday(&t2, NULL); + gettimeofday(&t1, NULL); + cnt = do_aio_multiwrite(reqs, nr_reqs, &total); + gettimeofday(&t2, NULL); - if (cnt < 0) { - printf("aio_multiwrite failed: %s\n", strerror(-cnt)); - goto out; - } + if (cnt < 0) { + printf("aio_multiwrite failed: %s\n", strerror(-cnt)); + goto out; + } - if (qflag) - goto out; + if (qflag) { + goto out; + } - /* Finally, report back -- -C gives a parsable format */ - t2 = tsub(t2, t1); - print_report("wrote", &t2, first_offset, total, total, cnt, Cflag); + /* Finally, report back -- -C gives a parsable format */ + t2 = tsub(t2, t1); + print_report("wrote", &t2, first_offset, total, total, cnt, Cflag); out: - for (i = 0; i < nr_reqs; i++) { - qemu_io_free(buf[i]); - qemu_iovec_destroy(&qiovs[i]); - } - qemu_free(buf); - qemu_free(reqs); - qemu_free(qiovs); - return 0; + for (i = 0; i < nr_reqs; i++) { + qemu_io_free(buf[i]); + qemu_iovec_destroy(&qiovs[i]); + } + qemu_free(buf); + qemu_free(reqs); + qemu_free(qiovs); + return 0; } struct aio_ctx { - QEMUIOVector qiov; - int64_t offset; - char *buf; - int qflag; - int vflag; - int Cflag; - int Pflag; - int pattern; - struct timeval t1; + QEMUIOVector qiov; + int64_t offset; + char *buf; + int qflag; + int vflag; + int Cflag; + int Pflag; + int pattern; + struct timeval t1; }; -static void -aio_write_done(void *opaque, int ret) +static void aio_write_done(void *opaque, int ret) { - struct aio_ctx *ctx = opaque; - struct timeval t2; + struct aio_ctx *ctx = opaque; + struct timeval t2; - gettimeofday(&t2, NULL); + gettimeofday(&t2, NULL); - if (ret < 0) { - printf("aio_write failed: %s\n", strerror(-ret)); - goto out; - } + if (ret < 0) { + printf("aio_write failed: %s\n", strerror(-ret)); + goto out; + } - if (ctx->qflag) { - goto out; - } + if (ctx->qflag) { + goto out; + } - /* Finally, report back -- -C gives a parsable format */ - t2 = tsub(t2, ctx->t1); - print_report("wrote", &t2, ctx->offset, ctx->qiov.size, - ctx->qiov.size, 1, ctx->Cflag); + /* Finally, report back -- -C gives a parsable format */ + t2 = tsub(t2, ctx->t1); + print_report("wrote", &t2, ctx->offset, ctx->qiov.size, + ctx->qiov.size, 1, ctx->Cflag); out: - qemu_io_free(ctx->buf); - free(ctx); + qemu_io_free(ctx->buf); + free(ctx); } -static void -aio_read_done(void *opaque, int ret) +static void aio_read_done(void *opaque, int ret) { - struct aio_ctx *ctx = opaque; - struct timeval t2; + struct aio_ctx *ctx = opaque; + struct timeval t2; - gettimeofday(&t2, NULL); + gettimeofday(&t2, NULL); - if (ret < 0) { - printf("readv failed: %s\n", strerror(-ret)); - goto out; - } + if (ret < 0) { + printf("readv failed: %s\n", strerror(-ret)); + goto out; + } - if (ctx->Pflag) { - void *cmp_buf = malloc(ctx->qiov.size); + if (ctx->Pflag) { + void *cmp_buf = malloc(ctx->qiov.size); - memset(cmp_buf, ctx->pattern, ctx->qiov.size); - if (memcmp(ctx->buf, cmp_buf, ctx->qiov.size)) { - printf("Pattern verification failed at offset %" - PRId64 ", %zd bytes\n", - ctx->offset, ctx->qiov.size); - } - free(cmp_buf); - } + memset(cmp_buf, ctx->pattern, ctx->qiov.size); + if (memcmp(ctx->buf, cmp_buf, ctx->qiov.size)) { + printf("Pattern verification failed at offset %" + PRId64 ", %zd bytes\n", ctx->offset, ctx->qiov.size); + } + free(cmp_buf); + } - if (ctx->qflag) { - goto out; - } + if (ctx->qflag) { + goto out; + } - if (ctx->vflag) { - dump_buffer(ctx->buf, ctx->offset, ctx->qiov.size); - } + if (ctx->vflag) { + dump_buffer(ctx->buf, ctx->offset, ctx->qiov.size); + } - /* Finally, report back -- -C gives a parsable format */ - t2 = tsub(t2, ctx->t1); - print_report("read", &t2, ctx->offset, ctx->qiov.size, - ctx->qiov.size, 1, ctx->Cflag); + /* Finally, report back -- -C gives a parsable format */ + t2 = tsub(t2, ctx->t1); + print_report("read", &t2, ctx->offset, ctx->qiov.size, + ctx->qiov.size, 1, ctx->Cflag); out: - qemu_io_free(ctx->buf); - free(ctx); + qemu_io_free(ctx->buf); + free(ctx); } -static void -aio_read_help(void) +static void aio_read_help(void) { - printf( + printf( "\n" " asynchronously reads a range of bytes from the given offset\n" "\n" @@ -1107,88 +1123,86 @@ aio_read_help(void) static int aio_read_f(int argc, char **argv); static const cmdinfo_t aio_read_cmd = { - .name = "aio_read", - .cfunc = aio_read_f, - .argmin = 2, - .argmax = -1, - .args = "[-Cqv] [-P pattern ] off len [len..]", - .oneline = "asynchronously reads a number of bytes", - .help = aio_read_help, + .name = "aio_read", + .cfunc = aio_read_f, + .argmin = 2, + .argmax = -1, + .args = "[-Cqv] [-P pattern ] off len [len..]", + .oneline = "asynchronously reads a number of bytes", + .help = aio_read_help, }; -static int -aio_read_f(int argc, char **argv) +static int aio_read_f(int argc, char **argv) { - int nr_iov, c; - struct aio_ctx *ctx = calloc(1, sizeof(struct aio_ctx)); - BlockDriverAIOCB *acb; + int nr_iov, c; + struct aio_ctx *ctx = calloc(1, sizeof(struct aio_ctx)); + BlockDriverAIOCB *acb; - while ((c = getopt(argc, argv, "CP:qv")) != EOF) { - switch (c) { - case 'C': - ctx->Cflag = 1; - break; - case 'P': - ctx->Pflag = 1; - ctx->pattern = parse_pattern(optarg); - if (ctx->pattern < 0) { - free(ctx); - return 0; - } - break; - case 'q': - ctx->qflag = 1; - break; - case 'v': - ctx->vflag = 1; - break; - default: - free(ctx); - return command_usage(&aio_read_cmd); - } - } + while ((c = getopt(argc, argv, "CP:qv")) != EOF) { + switch (c) { + case 'C': + ctx->Cflag = 1; + break; + case 'P': + ctx->Pflag = 1; + ctx->pattern = parse_pattern(optarg); + if (ctx->pattern < 0) { + free(ctx); + return 0; + } + break; + case 'q': + ctx->qflag = 1; + break; + case 'v': + ctx->vflag = 1; + break; + default: + free(ctx); + return command_usage(&aio_read_cmd); + } + } - if (optind > argc - 2) { - free(ctx); - return command_usage(&aio_read_cmd); - } + if (optind > argc - 2) { + free(ctx); + return command_usage(&aio_read_cmd); + } - ctx->offset = cvtnum(argv[optind]); - if (ctx->offset < 0) { - printf("non-numeric length argument -- %s\n", argv[optind]); - free(ctx); - return 0; - } - optind++; + ctx->offset = cvtnum(argv[optind]); + if (ctx->offset < 0) { + printf("non-numeric length argument -- %s\n", argv[optind]); + free(ctx); + return 0; + } + optind++; - if (ctx->offset & 0x1ff) { - printf("offset %" PRId64 " is not sector aligned\n", - ctx->offset); - free(ctx); - return 0; - } + if (ctx->offset & 0x1ff) { + printf("offset %" PRId64 " is not sector aligned\n", + ctx->offset); + free(ctx); + return 0; + } - nr_iov = argc - optind; - ctx->buf = create_iovec(&ctx->qiov, &argv[optind], nr_iov, 0xab); + nr_iov = argc - optind; + ctx->buf = create_iovec(&ctx->qiov, &argv[optind], nr_iov, 0xab); - gettimeofday(&ctx->t1, NULL); - acb = bdrv_aio_readv(bs, ctx->offset >> 9, &ctx->qiov, - ctx->qiov.size >> 9, aio_read_done, ctx); - if (!acb) { - free(ctx->buf); - free(ctx); - return -EIO; - } + gettimeofday(&ctx->t1, NULL); + acb = bdrv_aio_readv(bs, ctx->offset >> 9, &ctx->qiov, + ctx->qiov.size >> 9, aio_read_done, ctx); + if (!acb) { + free(ctx->buf); + free(ctx); + return -EIO; + } - return 0; + return 0; } -static void -aio_write_help(void) +static void aio_write_help(void) { - printf( + printf( "\n" -" asynchronously writes a range of bytes from the given offset source \n" +" asynchronously writes a range of bytes from the given offset source\n" " from multiple buffers\n" "\n" " Example:\n" @@ -1207,199 +1221,196 @@ aio_write_help(void) static int aio_write_f(int argc, char **argv); static const cmdinfo_t aio_write_cmd = { - .name = "aio_write", - .cfunc = aio_write_f, - .argmin = 2, - .argmax = -1, - .args = "[-Cq] [-P pattern ] off len [len..]", - .oneline = "asynchronously writes a number of bytes", - .help = aio_write_help, + .name = "aio_write", + .cfunc = aio_write_f, + .argmin = 2, + .argmax = -1, + .args = "[-Cq] [-P pattern ] off len [len..]", + .oneline = "asynchronously writes a number of bytes", + .help = aio_write_help, }; -static int -aio_write_f(int argc, char **argv) +static int aio_write_f(int argc, char **argv) { - int nr_iov, c; - int pattern = 0xcd; - struct aio_ctx *ctx = calloc(1, sizeof(struct aio_ctx)); - BlockDriverAIOCB *acb; + int nr_iov, c; + int pattern = 0xcd; + struct aio_ctx *ctx = calloc(1, sizeof(struct aio_ctx)); + BlockDriverAIOCB *acb; - while ((c = getopt(argc, argv, "CqP:")) != EOF) { - switch (c) { - case 'C': - ctx->Cflag = 1; - break; - case 'q': - ctx->qflag = 1; - break; - case 'P': - pattern = parse_pattern(optarg); - if (pattern < 0) - return 0; - break; - default: - free(ctx); - return command_usage(&aio_write_cmd); - } - } + while ((c = getopt(argc, argv, "CqP:")) != EOF) { + switch (c) { + case 'C': + ctx->Cflag = 1; + break; + case 'q': + ctx->qflag = 1; + break; + case 'P': + pattern = parse_pattern(optarg); + if (pattern < 0) { + return 0; + } + break; + default: + free(ctx); + return command_usage(&aio_write_cmd); + } + } - if (optind > argc - 2) { - free(ctx); - return command_usage(&aio_write_cmd); - } + if (optind > argc - 2) { + free(ctx); + return command_usage(&aio_write_cmd); + } - ctx->offset = cvtnum(argv[optind]); - if (ctx->offset < 0) { - printf("non-numeric length argument -- %s\n", argv[optind]); - free(ctx); - return 0; - } - optind++; + ctx->offset = cvtnum(argv[optind]); + if (ctx->offset < 0) { + printf("non-numeric length argument -- %s\n", argv[optind]); + free(ctx); + return 0; + } + optind++; - if (ctx->offset & 0x1ff) { - printf("offset %" PRId64 " is not sector aligned\n", - ctx->offset); - free(ctx); - return 0; - } + if (ctx->offset & 0x1ff) { + printf("offset %" PRId64 " is not sector aligned\n", + ctx->offset); + free(ctx); + return 0; + } - nr_iov = argc - optind; - ctx->buf = create_iovec(&ctx->qiov, &argv[optind], nr_iov, pattern); + nr_iov = argc - optind; + ctx->buf = create_iovec(&ctx->qiov, &argv[optind], nr_iov, pattern); - gettimeofday(&ctx->t1, NULL); - acb = bdrv_aio_writev(bs, ctx->offset >> 9, &ctx->qiov, - ctx->qiov.size >> 9, aio_write_done, ctx); - if (!acb) { - free(ctx->buf); - free(ctx); - return -EIO; - } + gettimeofday(&ctx->t1, NULL); + acb = bdrv_aio_writev(bs, ctx->offset >> 9, &ctx->qiov, + ctx->qiov.size >> 9, aio_write_done, ctx); + if (!acb) { + free(ctx->buf); + free(ctx); + return -EIO; + } - return 0; + return 0; } -static int -aio_flush_f(int argc, char **argv) +static int aio_flush_f(int argc, char **argv) { - qemu_aio_flush(); - return 0; + qemu_aio_flush(); + return 0; } static const cmdinfo_t aio_flush_cmd = { - .name = "aio_flush", - .cfunc = aio_flush_f, - .oneline = "completes all outstanding aio requests" + .name = "aio_flush", + .cfunc = aio_flush_f, + .oneline = "completes all outstanding aio requests" }; -static int -flush_f(int argc, char **argv) +static int flush_f(int argc, char **argv) { - bdrv_flush(bs); - return 0; + bdrv_flush(bs); + return 0; } static const cmdinfo_t flush_cmd = { - .name = "flush", - .altname = "f", - .cfunc = flush_f, - .oneline = "flush all in-core file state to disk", + .name = "flush", + .altname = "f", + .cfunc = flush_f, + .oneline = "flush all in-core file state to disk", }; -static int -truncate_f(int argc, char **argv) +static int truncate_f(int argc, char **argv) { - int64_t offset; - int ret; + int64_t offset; + int ret; - offset = cvtnum(argv[1]); - if (offset < 0) { - printf("non-numeric truncate argument -- %s\n", argv[1]); - return 0; - } + offset = cvtnum(argv[1]); + if (offset < 0) { + printf("non-numeric truncate argument -- %s\n", argv[1]); + return 0; + } - ret = bdrv_truncate(bs, offset); - if (ret < 0) { - printf("truncate: %s\n", strerror(-ret)); - return 0; - } + ret = bdrv_truncate(bs, offset); + if (ret < 0) { + printf("truncate: %s\n", strerror(-ret)); + return 0; + } - return 0; + return 0; } static const cmdinfo_t truncate_cmd = { - .name = "truncate", - .altname = "t", - .cfunc = truncate_f, - .argmin = 1, - .argmax = 1, - .args = "off", - .oneline = "truncates the current file at the given offset", + .name = "truncate", + .altname = "t", + .cfunc = truncate_f, + .argmin = 1, + .argmax = 1, + .args = "off", + .oneline = "truncates the current file at the given offset", }; -static int -length_f(int argc, char **argv) +static int length_f(int argc, char **argv) { - int64_t size; - char s1[64]; + int64_t size; + char s1[64]; - size = bdrv_getlength(bs); - if (size < 0) { - printf("getlength: %s\n", strerror(-size)); - return 0; - } + size = bdrv_getlength(bs); + if (size < 0) { + printf("getlength: %s\n", strerror(-size)); + return 0; + } - cvtstr(size, s1, sizeof(s1)); - printf("%s\n", s1); - return 0; + cvtstr(size, s1, sizeof(s1)); + printf("%s\n", s1); + return 0; } static const cmdinfo_t length_cmd = { - .name = "length", - .altname = "l", - .cfunc = length_f, - .oneline = "gets the length of the current file", + .name = "length", + .altname = "l", + .cfunc = length_f, + .oneline = "gets the length of the current file", }; -static int -info_f(int argc, char **argv) +static int info_f(int argc, char **argv) { - BlockDriverInfo bdi; - char s1[64], s2[64]; - int ret; + BlockDriverInfo bdi; + char s1[64], s2[64]; + int ret; - if (bs->drv && bs->drv->format_name) - printf("format name: %s\n", bs->drv->format_name); - if (bs->drv && bs->drv->protocol_name) - printf("format name: %s\n", bs->drv->protocol_name); + if (bs->drv && bs->drv->format_name) { + printf("format name: %s\n", bs->drv->format_name); + } + if (bs->drv && bs->drv->protocol_name) { + printf("format name: %s\n", bs->drv->protocol_name); + } - ret = bdrv_get_info(bs, &bdi); - if (ret) - return 0; + ret = bdrv_get_info(bs, &bdi); + if (ret) { + return 0; + } - cvtstr(bdi.cluster_size, s1, sizeof(s1)); - cvtstr(bdi.vm_state_offset, s2, sizeof(s2)); + cvtstr(bdi.cluster_size, s1, sizeof(s1)); + cvtstr(bdi.vm_state_offset, s2, sizeof(s2)); - printf("cluster size: %s\n", s1); - printf("vm state offset: %s\n", s2); + printf("cluster size: %s\n", s1); + printf("vm state offset: %s\n", s2); - return 0; + return 0; } static const cmdinfo_t info_cmd = { - .name = "info", - .altname = "i", - .cfunc = info_f, - .oneline = "prints information about the current file", + .name = "info", + .altname = "i", + .cfunc = info_f, + .oneline = "prints information about the current file", }; -static void -discard_help(void) +static void discard_help(void) { - printf( + printf( "\n" " discards a range of bytes from the given offset\n" "\n" @@ -1415,148 +1426,147 @@ discard_help(void) static int discard_f(int argc, char **argv); static const cmdinfo_t discard_cmd = { - .name = "discard", - .altname = "d", - .cfunc = discard_f, - .argmin = 2, - .argmax = -1, - .args = "[-Cq] off len", - .oneline = "discards a number of bytes at a specified offset", - .help = discard_help, + .name = "discard", + .altname = "d", + .cfunc = discard_f, + .argmin = 2, + .argmax = -1, + .args = "[-Cq] off len", + .oneline = "discards a number of bytes at a specified offset", + .help = discard_help, }; -static int -discard_f(int argc, char **argv) +static int discard_f(int argc, char **argv) { - struct timeval t1, t2; - int Cflag = 0, qflag = 0; - int c, ret; - int64_t offset; - int count; + struct timeval t1, t2; + int Cflag = 0, qflag = 0; + int c, ret; + int64_t offset; + int count; - while ((c = getopt(argc, argv, "Cq")) != EOF) { - switch (c) { - case 'C': - Cflag = 1; - break; - case 'q': - qflag = 1; - break; - default: - return command_usage(&discard_cmd); - } - } + while ((c = getopt(argc, argv, "Cq")) != EOF) { + switch (c) { + case 'C': + Cflag = 1; + break; + case 'q': + qflag = 1; + break; + default: + return command_usage(&discard_cmd); + } + } - if (optind != argc - 2) { - return command_usage(&discard_cmd); - } + if (optind != argc - 2) { + return command_usage(&discard_cmd); + } - offset = cvtnum(argv[optind]); - if (offset < 0) { - printf("non-numeric length argument -- %s\n", argv[optind]); - return 0; - } + offset = cvtnum(argv[optind]); + if (offset < 0) { + printf("non-numeric length argument -- %s\n", argv[optind]); + return 0; + } - optind++; - count = cvtnum(argv[optind]); - if (count < 0) { - printf("non-numeric length argument -- %s\n", argv[optind]); - return 0; - } + optind++; + count = cvtnum(argv[optind]); + if (count < 0) { + printf("non-numeric length argument -- %s\n", argv[optind]); + return 0; + } - gettimeofday(&t1, NULL); - ret = bdrv_discard(bs, offset >> BDRV_SECTOR_BITS, count >> BDRV_SECTOR_BITS); - gettimeofday(&t2, NULL); + gettimeofday(&t1, NULL); + ret = bdrv_discard(bs, offset >> BDRV_SECTOR_BITS, + count >> BDRV_SECTOR_BITS); + gettimeofday(&t2, NULL); - if (ret < 0) { - printf("discard failed: %s\n", strerror(-ret)); - goto out; - } + if (ret < 0) { + printf("discard failed: %s\n", strerror(-ret)); + goto out; + } - /* Finally, report back -- -C gives a parsable format */ - if (!qflag) { - t2 = tsub(t2, t1); - print_report("discard", &t2, offset, count, count, 1, Cflag); - } + /* Finally, report back -- -C gives a parsable format */ + if (!qflag) { + t2 = tsub(t2, t1); + print_report("discard", &t2, offset, count, count, 1, Cflag); + } out: - return 0; + return 0; } -static int -alloc_f(int argc, char **argv) +static int alloc_f(int argc, char **argv) { - int64_t offset; - int nb_sectors, remaining; - char s1[64]; - int num, sum_alloc; - int ret; + int64_t offset; + int nb_sectors, remaining; + char s1[64]; + int num, sum_alloc; + int ret; - offset = cvtnum(argv[1]); - if (offset & 0x1ff) { - printf("offset %" PRId64 " is not sector aligned\n", - offset); - return 0; - } + offset = cvtnum(argv[1]); + if (offset & 0x1ff) { + printf("offset %" PRId64 " is not sector aligned\n", + offset); + return 0; + } - if (argc == 3) - nb_sectors = cvtnum(argv[2]); - else - nb_sectors = 1; + if (argc == 3) { + nb_sectors = cvtnum(argv[2]); + } else { + nb_sectors = 1; + } - remaining = nb_sectors; - sum_alloc = 0; - while (remaining) { - ret = bdrv_is_allocated(bs, offset >> 9, nb_sectors, &num); - remaining -= num; - if (ret) { - sum_alloc += num; - } - } + remaining = nb_sectors; + sum_alloc = 0; + while (remaining) { + ret = bdrv_is_allocated(bs, offset >> 9, nb_sectors, &num); + remaining -= num; + if (ret) { + sum_alloc += num; + } + } - cvtstr(offset, s1, sizeof(s1)); + cvtstr(offset, s1, sizeof(s1)); - printf("%d/%d sectors allocated at offset %s\n", - sum_alloc, nb_sectors, s1); - return 0; + printf("%d/%d sectors allocated at offset %s\n", + sum_alloc, nb_sectors, s1); + return 0; } static const cmdinfo_t alloc_cmd = { - .name = "alloc", - .altname = "a", - .argmin = 1, - .argmax = 2, - .cfunc = alloc_f, - .args = "off [sectors]", - .oneline = "checks if a sector is present in the file", + .name = "alloc", + .altname = "a", + .argmin = 1, + .argmax = 2, + .cfunc = alloc_f, + .args = "off [sectors]", + .oneline = "checks if a sector is present in the file", }; -static int -map_f(int argc, char **argv) +static int map_f(int argc, char **argv) { - int64_t offset; - int64_t nb_sectors; - char s1[64]; - int num, num_checked; - int ret; - const char *retstr; + int64_t offset; + int64_t nb_sectors; + char s1[64]; + int num, num_checked; + int ret; + const char *retstr; - offset = 0; - nb_sectors = bs->total_sectors; + offset = 0; + nb_sectors = bs->total_sectors; - do { - num_checked = MIN(nb_sectors, INT_MAX); - ret = bdrv_is_allocated(bs, offset, num_checked, &num); - retstr = ret ? " allocated" : "not allocated"; - cvtstr(offset << 9ULL, s1, sizeof(s1)); - printf("[% 24" PRId64 "] % 8d/% 8d sectors %s at offset %s (%d)\n", - offset << 9ULL, num, num_checked, retstr, s1, ret); + do { + num_checked = MIN(nb_sectors, INT_MAX); + ret = bdrv_is_allocated(bs, offset, num_checked, &num); + retstr = ret ? " allocated" : "not allocated"; + cvtstr(offset << 9ULL, s1, sizeof(s1)); + printf("[% 24" PRId64 "] % 8d/% 8d sectors %s at offset %s (%d)\n", + offset << 9ULL, num, num_checked, retstr, s1, ret); - offset += num; - nb_sectors -= num; - } while(offset < bs->total_sectors); + offset += num; + nb_sectors -= num; + } while (offset < bs->total_sectors); - return 0; + return 0; } static const cmdinfo_t map_cmd = { @@ -1569,50 +1579,48 @@ static const cmdinfo_t map_cmd = { }; -static int -close_f(int argc, char **argv) +static int close_f(int argc, char **argv) { - bdrv_close(bs); - bs = NULL; - return 0; + bdrv_close(bs); + bs = NULL; + return 0; } static const cmdinfo_t close_cmd = { - .name = "close", - .altname = "c", - .cfunc = close_f, - .oneline = "close the current open file", + .name = "close", + .altname = "c", + .cfunc = close_f, + .oneline = "close the current open file", }; static int openfile(char *name, int flags, int growable) { - if (bs) { - fprintf(stderr, "file open already, try 'help close'\n"); - return 1; - } + if (bs) { + fprintf(stderr, "file open already, try 'help close'\n"); + return 1; + } - if (growable) { - if (bdrv_file_open(&bs, name, flags)) { - fprintf(stderr, "%s: can't open device %s\n", progname, name); - return 1; - } - } else { - bs = bdrv_new("hda"); + if (growable) { + if (bdrv_file_open(&bs, name, flags)) { + fprintf(stderr, "%s: can't open device %s\n", progname, name); + return 1; + } + } else { + bs = bdrv_new("hda"); - if (bdrv_open(bs, name, flags, NULL) < 0) { - fprintf(stderr, "%s: can't open device %s\n", progname, name); - bs = NULL; - return 1; - } - } + if (bdrv_open(bs, name, flags, NULL) < 0) { + fprintf(stderr, "%s: can't open device %s\n", progname, name); + bs = NULL; + return 1; + } + } - return 0; + return 0; } -static void -open_help(void) +static void open_help(void) { - printf( + printf( "\n" " opens a new file in the requested mode\n" "\n" @@ -1630,80 +1638,78 @@ open_help(void) static int open_f(int argc, char **argv); static const cmdinfo_t open_cmd = { - .name = "open", - .altname = "o", - .cfunc = open_f, - .argmin = 1, - .argmax = -1, - .flags = CMD_NOFILE_OK, - .args = "[-Crsn] [path]", - .oneline = "open the file specified by path", - .help = open_help, + .name = "open", + .altname = "o", + .cfunc = open_f, + .argmin = 1, + .argmax = -1, + .flags = CMD_NOFILE_OK, + .args = "[-Crsn] [path]", + .oneline = "open the file specified by path", + .help = open_help, }; -static int -open_f(int argc, char **argv) +static int open_f(int argc, char **argv) { - int flags = 0; - int readonly = 0; - int growable = 0; - int c; + int flags = 0; + int readonly = 0; + int growable = 0; + int c; - while ((c = getopt(argc, argv, "snrg")) != EOF) { - switch (c) { - case 's': - flags |= BDRV_O_SNAPSHOT; - break; - case 'n': - flags |= BDRV_O_NOCACHE | BDRV_O_CACHE_WB; - break; - case 'r': - readonly = 1; - break; - case 'g': - growable = 1; - break; - default: - return command_usage(&open_cmd); - } - } - - if (!readonly) { - flags |= BDRV_O_RDWR; + while ((c = getopt(argc, argv, "snrg")) != EOF) { + switch (c) { + case 's': + flags |= BDRV_O_SNAPSHOT; + break; + case 'n': + flags |= BDRV_O_NOCACHE | BDRV_O_CACHE_WB; + break; + case 'r': + readonly = 1; + break; + case 'g': + growable = 1; + break; + default: + return command_usage(&open_cmd); } + } - if (optind != argc - 1) - return command_usage(&open_cmd); + if (!readonly) { + flags |= BDRV_O_RDWR; + } - return openfile(argv[optind], flags, growable); + if (optind != argc - 1) { + return command_usage(&open_cmd); + } + + return openfile(argv[optind], flags, growable); } -static int -init_args_command( - int index) +static int init_args_command(int index) { - /* only one device allowed so far */ - if (index >= 1) - return 0; - return ++index; + /* only one device allowed so far */ + if (index >= 1) { + return 0; + } + return ++index; } -static int -init_check_command( - const cmdinfo_t *ct) +static int init_check_command(const cmdinfo_t *ct) { - if (ct->flags & CMD_FLAG_GLOBAL) - return 1; - if (!(ct->flags & CMD_NOFILE_OK) && !bs) { - fprintf(stderr, "no file open, try 'help open'\n"); - return 0; - } - return 1; + if (ct->flags & CMD_FLAG_GLOBAL) { + return 1; + } + if (!(ct->flags & CMD_NOFILE_OK) && !bs) { + fprintf(stderr, "no file open, try 'help open'\n"); + return 0; + } + return 1; } static void usage(const char *name) { - printf( + printf( "Usage: %s [-h] [-V] [-rsnm] [-c cmd] ... [file]\n" "QEMU Disk exerciser\n" "\n" @@ -1717,115 +1723,117 @@ static void usage(const char *name) " -h, --help display this help and exit\n" " -V, --version output version information and exit\n" "\n", - name); + name); } int main(int argc, char **argv) { - int readonly = 0; - int growable = 0; - const char *sopt = "hVc:rsnmgk"; - const struct option lopt[] = { - { "help", 0, NULL, 'h' }, - { "version", 0, NULL, 'V' }, - { "offset", 1, NULL, 'o' }, - { "cmd", 1, NULL, 'c' }, - { "read-only", 0, NULL, 'r' }, - { "snapshot", 0, NULL, 's' }, - { "nocache", 0, NULL, 'n' }, - { "misalign", 0, NULL, 'm' }, - { "growable", 0, NULL, 'g' }, - { "native-aio", 0, NULL, 'k' }, - { NULL, 0, NULL, 0 } - }; - int c; - int opt_index = 0; - int flags = 0; + int readonly = 0; + int growable = 0; + const char *sopt = "hVc:rsnmgk"; + const struct option lopt[] = { + { "help", 0, NULL, 'h' }, + { "version", 0, NULL, 'V' }, + { "offset", 1, NULL, 'o' }, + { "cmd", 1, NULL, 'c' }, + { "read-only", 0, NULL, 'r' }, + { "snapshot", 0, NULL, 's' }, + { "nocache", 0, NULL, 'n' }, + { "misalign", 0, NULL, 'm' }, + { "growable", 0, NULL, 'g' }, + { "native-aio", 0, NULL, 'k' }, + { NULL, 0, NULL, 0 } + }; + int c; + int opt_index = 0; + int flags = 0; - progname = basename(argv[0]); + progname = basename(argv[0]); - while ((c = getopt_long(argc, argv, sopt, lopt, &opt_index)) != -1) { - switch (c) { - case 's': - flags |= BDRV_O_SNAPSHOT; - break; - case 'n': - flags |= BDRV_O_NOCACHE | BDRV_O_CACHE_WB; - break; - case 'c': - add_user_command(optarg); - break; - case 'r': - readonly = 1; - break; - case 'm': - misalign = 1; - break; - case 'g': - growable = 1; - break; - case 'k': - flags |= BDRV_O_NATIVE_AIO; - break; - case 'V': - printf("%s version %s\n", progname, VERSION); - exit(0); - case 'h': - usage(progname); - exit(0); - default: - usage(progname); - exit(1); - } - } - - if ((argc - optind) > 1) { - usage(progname); - exit(1); - } - - bdrv_init(); - - /* initialize commands */ - quit_init(); - help_init(); - add_command(&open_cmd); - add_command(&close_cmd); - add_command(&read_cmd); - add_command(&readv_cmd); - add_command(&write_cmd); - add_command(&writev_cmd); - add_command(&multiwrite_cmd); - add_command(&aio_read_cmd); - add_command(&aio_write_cmd); - add_command(&aio_flush_cmd); - add_command(&flush_cmd); - add_command(&truncate_cmd); - add_command(&length_cmd); - add_command(&info_cmd); - add_command(&discard_cmd); - add_command(&alloc_cmd); - add_command(&map_cmd); - - add_args_command(init_args_command); - add_check_command(init_check_command); - - /* open the device */ - if (!readonly) { - flags |= BDRV_O_RDWR; + while ((c = getopt_long(argc, argv, sopt, lopt, &opt_index)) != -1) { + switch (c) { + case 's': + flags |= BDRV_O_SNAPSHOT; + break; + case 'n': + flags |= BDRV_O_NOCACHE | BDRV_O_CACHE_WB; + break; + case 'c': + add_user_command(optarg); + break; + case 'r': + readonly = 1; + break; + case 'm': + misalign = 1; + break; + case 'g': + growable = 1; + break; + case 'k': + flags |= BDRV_O_NATIVE_AIO; + break; + case 'V': + printf("%s version %s\n", progname, VERSION); + exit(0); + case 'h': + usage(progname); + exit(0); + default: + usage(progname); + exit(1); } + } - if ((argc - optind) == 1) - openfile(argv[optind], flags, growable); - command_loop(); + if ((argc - optind) > 1) { + usage(progname); + exit(1); + } - /* - * Make sure all outstanding requests get flushed the program exits. - */ - qemu_aio_flush(); + bdrv_init(); - if (bs) - bdrv_close(bs); - return 0; + /* initialize commands */ + quit_init(); + help_init(); + add_command(&open_cmd); + add_command(&close_cmd); + add_command(&read_cmd); + add_command(&readv_cmd); + add_command(&write_cmd); + add_command(&writev_cmd); + add_command(&multiwrite_cmd); + add_command(&aio_read_cmd); + add_command(&aio_write_cmd); + add_command(&aio_flush_cmd); + add_command(&flush_cmd); + add_command(&truncate_cmd); + add_command(&length_cmd); + add_command(&info_cmd); + add_command(&discard_cmd); + add_command(&alloc_cmd); + add_command(&map_cmd); + + add_args_command(init_args_command); + add_check_command(init_check_command); + + /* open the device */ + if (!readonly) { + flags |= BDRV_O_RDWR; + } + + if ((argc - optind) == 1) { + openfile(argv[optind], flags, growable); + } + command_loop(); + + /* + * Make sure all outstanding requests get flushed the program exits. + */ + qemu_aio_flush(); + + if (bs) { + bdrv_close(bs); + } + return 0; } From 5afc8b3de9a8a472a45d2b980632a15cb7f1e1c3 Mon Sep 17 00:00:00 2001 From: Devin Nakamura Date: Mon, 11 Jul 2011 11:20:25 -0400 Subject: [PATCH 137/209] qemu-io: Fix if scoping bug Fix a bug caused by lack of braces in if statement Lack of braces means that if(count & 0x1ff) is never reached Signed-off-by: Devin Nakamura Signed-off-by: Kevin Wolf --- qemu-io.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/qemu-io.c b/qemu-io.c index e3c825f7e9..a553d0c98d 100644 --- a/qemu-io.c +++ b/qemu-io.c @@ -449,7 +449,7 @@ static int read_f(int argc, char **argv) return 0; } - if (!pflag) + if (!pflag) { if (offset & 0x1ff) { printf("offset %" PRId64 " is not sector aligned\n", offset); @@ -460,6 +460,7 @@ static int read_f(int argc, char **argv) count); return 0; } + } buf = qemu_io_alloc(count, 0xab); From 348e7b8dcd1460df4d681105f63dd90bba70496d Mon Sep 17 00:00:00 2001 From: Hannes Reinecke Date: Mon, 11 Jul 2011 15:02:23 +0200 Subject: [PATCH 138/209] iov: Update parameter usage in iov_(to|from)_buf() iov_to_buf() has an 'offset' parameter, iov_from_buf() hasn't. This patch adds the missing parameter to iov_from_buf(). It also renames the 'offset' parameter to 'iov_off' to emphasize it's the offset into the iovec and not the buffer. Signed-off-by: Hannes Reinecke Acked-by: Alexander Graf Signed-off-by: Kevin Wolf --- hw/virtio-net.c | 2 +- hw/virtio-serial-bus.c | 2 +- iov.c | 69 ++++++++++++++++++++++-------------------- iov.h | 10 +++--- 4 files changed, 44 insertions(+), 39 deletions(-) diff --git a/hw/virtio-net.c b/hw/virtio-net.c index 6997e02dcf..a32cc019b0 100644 --- a/hw/virtio-net.c +++ b/hw/virtio-net.c @@ -657,7 +657,7 @@ static ssize_t virtio_net_receive(VLANClientState *nc, const uint8_t *buf, size_ /* copy in packet. ugh */ len = iov_from_buf(sg, elem.in_num, - buf + offset, size - offset); + buf + offset, 0, size - offset); total += len; offset += len; /* If buffers can't be merged, at this point we diff --git a/hw/virtio-serial-bus.c b/hw/virtio-serial-bus.c index 7f6db7bffe..53c58d0f3f 100644 --- a/hw/virtio-serial-bus.c +++ b/hw/virtio-serial-bus.c @@ -103,7 +103,7 @@ static size_t write_to_port(VirtIOSerialPort *port, } len = iov_from_buf(elem.in_sg, elem.in_num, - buf + offset, size - offset); + buf + offset, 0, size - offset); offset += len; virtqueue_push(vq, &elem, len); diff --git a/iov.c b/iov.c index 588cd04288..1e027914d4 100644 --- a/iov.c +++ b/iov.c @@ -14,56 +14,61 @@ #include "iov.h" -size_t iov_from_buf(struct iovec *iov, unsigned int iovcnt, - const void *buf, size_t size) +size_t iov_from_buf(struct iovec *iov, unsigned int iov_cnt, + const void *buf, size_t iov_off, size_t size) { - size_t offset; + size_t iovec_off, buf_off; unsigned int i; - offset = 0; - for (i = 0; offset < size && i < iovcnt; i++) { - size_t len; - - len = MIN(iov[i].iov_len, size - offset); - - memcpy(iov[i].iov_base, buf + offset, len); - offset += len; - } - return offset; -} - -size_t iov_to_buf(const struct iovec *iov, const unsigned int iovcnt, - void *buf, size_t offset, size_t size) -{ - uint8_t *ptr; - size_t iov_off, buf_off; - unsigned int i; - - ptr = buf; - iov_off = 0; + iovec_off = 0; buf_off = 0; - for (i = 0; i < iovcnt && size; i++) { - if (offset < (iov_off + iov[i].iov_len)) { - size_t len = MIN((iov_off + iov[i].iov_len) - offset , size); + for (i = 0; i < iov_cnt && size; i++) { + if (iov_off < (iovec_off + iov[i].iov_len)) { + size_t len = MIN((iovec_off + iov[i].iov_len) - iov_off, size); - memcpy(ptr + buf_off, iov[i].iov_base + (offset - iov_off), len); + memcpy(iov[i].iov_base + (iov_off - iovec_off), buf + buf_off, len); buf_off += len; - offset += len; + iov_off += len; size -= len; } - iov_off += iov[i].iov_len; + iovec_off += iov[i].iov_len; } return buf_off; } -size_t iov_size(const struct iovec *iov, const unsigned int iovcnt) +size_t iov_to_buf(const struct iovec *iov, const unsigned int iov_cnt, + void *buf, size_t iov_off, size_t size) +{ + uint8_t *ptr; + size_t iovec_off, buf_off; + unsigned int i; + + ptr = buf; + iovec_off = 0; + buf_off = 0; + for (i = 0; i < iov_cnt && size; i++) { + if (iov_off < (iovec_off + iov[i].iov_len)) { + size_t len = MIN((iovec_off + iov[i].iov_len) - iov_off , size); + + memcpy(ptr + buf_off, iov[i].iov_base + (iov_off - iovec_off), len); + + buf_off += len; + iov_off += len; + size -= len; + } + iovec_off += iov[i].iov_len; + } + return buf_off; +} + +size_t iov_size(const struct iovec *iov, const unsigned int iov_cnt) { size_t len; unsigned int i; len = 0; - for (i = 0; i < iovcnt; i++) { + for (i = 0; i < iov_cnt; i++) { len += iov[i].iov_len; } return len; diff --git a/iov.h b/iov.h index 60a85470bd..110f67ab53 100644 --- a/iov.h +++ b/iov.h @@ -12,8 +12,8 @@ #include "qemu-common.h" -size_t iov_from_buf(struct iovec *iov, unsigned int iovcnt, - const void *buf, size_t size); -size_t iov_to_buf(const struct iovec *iov, const unsigned int iovcnt, - void *buf, size_t offset, size_t size); -size_t iov_size(const struct iovec *iov, const unsigned int iovcnt); +size_t iov_from_buf(struct iovec *iov, unsigned int iov_cnt, + const void *buf, size_t iov_off, size_t size); +size_t iov_to_buf(const struct iovec *iov, const unsigned int iov_cnt, + void *buf, size_t iov_off, size_t size); +size_t iov_size(const struct iovec *iov, const unsigned int iov_cnt); From c5bf71a9a3b10c0cce877b7b3add4484322d3e2c Mon Sep 17 00:00:00 2001 From: Hannes Reinecke Date: Mon, 11 Jul 2011 15:02:24 +0200 Subject: [PATCH 139/209] scsi: Add 'hba_private' to SCSIRequest 'tag' is just an abstraction to identify the command from the driver. So we should make that explicit by replacing 'tag' with a driver-defined pointer 'hba_private'. This saves the lookup for driver handling several commands in parallel. 'tag' is still being kept for tracing purposes. Signed-off-by: Hannes Reinecke Acked-by: Paolo Bonzini Signed-off-by: Kevin Wolf --- hw/esp.c | 2 +- hw/lsi53c895a.c | 22 ++++++++-------------- hw/scsi-bus.c | 9 ++++++--- hw/scsi-disk.c | 4 ++-- hw/scsi-generic.c | 5 +++-- hw/scsi.h | 10 +++++++--- hw/spapr_vscsi.c | 28 +++++++++------------------- hw/usb-msd.c | 9 +-------- 8 files changed, 37 insertions(+), 52 deletions(-) diff --git a/hw/esp.c b/hw/esp.c index aa50800a56..9ddd6373c8 100644 --- a/hw/esp.c +++ b/hw/esp.c @@ -244,7 +244,7 @@ static void do_busid_cmd(ESPState *s, uint8_t *buf, uint8_t busid) DPRINTF("do_busid_cmd: busid 0x%x\n", busid); lun = busid & 7; - s->current_req = scsi_req_new(s->current_dev, 0, lun); + s->current_req = scsi_req_new(s->current_dev, 0, lun, NULL); datalen = scsi_req_enqueue(s->current_req, buf); s->ti_size = datalen; if (datalen != 0) { diff --git a/hw/lsi53c895a.c b/hw/lsi53c895a.c index 940b43abfd..69eec1d2fe 100644 --- a/hw/lsi53c895a.c +++ b/hw/lsi53c895a.c @@ -661,7 +661,7 @@ static lsi_request *lsi_find_by_tag(LSIState *s, uint32_t tag) static void lsi_request_cancelled(SCSIRequest *req) { LSIState *s = DO_UPCAST(LSIState, dev.qdev, req->bus->qbus.parent); - lsi_request *p; + lsi_request *p = req->hba_private; if (s->current && req == s->current->req) { scsi_req_unref(req); @@ -670,7 +670,6 @@ static void lsi_request_cancelled(SCSIRequest *req) return; } - p = lsi_find_by_tag(s, req->tag); if (p) { QTAILQ_REMOVE(&s->queue, p, next); scsi_req_unref(req); @@ -680,18 +679,12 @@ static void lsi_request_cancelled(SCSIRequest *req) /* Record that data is available for a queued command. Returns zero if the device was reselected, nonzero if the IO is deferred. */ -static int lsi_queue_tag(LSIState *s, uint32_t tag, uint32_t len) +static int lsi_queue_req(LSIState *s, SCSIRequest *req, uint32_t len) { - lsi_request *p; - - p = lsi_find_by_tag(s, tag); - if (!p) { - BADF("IO with unknown tag %d\n", tag); - return 1; - } + lsi_request *p = req->hba_private; if (p->pending) { - BADF("Multiple IO pending for tag %d\n", tag); + BADF("Multiple IO pending for request %p\n", p); } p->pending = len; /* Reselect if waiting for it, or if reselection triggers an IRQ @@ -743,9 +736,9 @@ static void lsi_transfer_data(SCSIRequest *req, uint32_t len) LSIState *s = DO_UPCAST(LSIState, dev.qdev, req->bus->qbus.parent); int out; - if (s->waiting == 1 || !s->current || req->tag != s->current->tag || + if (s->waiting == 1 || !s->current || req->hba_private != s->current || (lsi_irq_on_rsl(s) && !(s->scntl1 & LSI_SCNTL1_CON))) { - if (lsi_queue_tag(s, req->tag, len)) { + if (lsi_queue_req(s, req, len)) { return; } } @@ -789,7 +782,8 @@ static void lsi_do_command(LSIState *s) assert(s->current == NULL); s->current = qemu_mallocz(sizeof(lsi_request)); s->current->tag = s->select_tag; - s->current->req = scsi_req_new(dev, s->current->tag, s->current_lun); + s->current->req = scsi_req_new(dev, s->current->tag, s->current_lun, + s->current); n = scsi_req_enqueue(s->current->req, buf); if (n) { diff --git a/hw/scsi-bus.c b/hw/scsi-bus.c index ad6a730be0..8b1a412210 100644 --- a/hw/scsi-bus.c +++ b/hw/scsi-bus.c @@ -131,7 +131,8 @@ int scsi_bus_legacy_handle_cmdline(SCSIBus *bus) return res; } -SCSIRequest *scsi_req_alloc(size_t size, SCSIDevice *d, uint32_t tag, uint32_t lun) +SCSIRequest *scsi_req_alloc(size_t size, SCSIDevice *d, uint32_t tag, + uint32_t lun, void *hba_private) { SCSIRequest *req; @@ -141,14 +142,16 @@ SCSIRequest *scsi_req_alloc(size_t size, SCSIDevice *d, uint32_t tag, uint32_t l req->dev = d; req->tag = tag; req->lun = lun; + req->hba_private = hba_private; req->status = -1; trace_scsi_req_alloc(req->dev->id, req->lun, req->tag); return req; } -SCSIRequest *scsi_req_new(SCSIDevice *d, uint32_t tag, uint32_t lun) +SCSIRequest *scsi_req_new(SCSIDevice *d, uint32_t tag, uint32_t lun, + void *hba_private) { - return d->info->alloc_req(d, tag, lun); + return d->info->alloc_req(d, tag, lun, hba_private); } uint8_t *scsi_req_get_buf(SCSIRequest *req) diff --git a/hw/scsi-disk.c b/hw/scsi-disk.c index a8c7372d3e..c2a99fe487 100644 --- a/hw/scsi-disk.c +++ b/hw/scsi-disk.c @@ -81,13 +81,13 @@ static int scsi_handle_rw_error(SCSIDiskReq *r, int error, int type); static int scsi_disk_emulate_command(SCSIDiskReq *r, uint8_t *outbuf); static SCSIRequest *scsi_new_request(SCSIDevice *d, uint32_t tag, - uint32_t lun) + uint32_t lun, void *hba_private) { SCSIDiskState *s = DO_UPCAST(SCSIDiskState, qdev, d); SCSIRequest *req; SCSIDiskReq *r; - req = scsi_req_alloc(sizeof(SCSIDiskReq), &s->qdev, tag, lun); + req = scsi_req_alloc(sizeof(SCSIDiskReq), &s->qdev, tag, lun, hba_private); r = DO_UPCAST(SCSIDiskReq, req, req); r->iov.iov_base = qemu_blockalign(s->bs, SCSI_DMA_BUF_SIZE); return req; diff --git a/hw/scsi-generic.c b/hw/scsi-generic.c index 8e59c7ee89..90345a714f 100644 --- a/hw/scsi-generic.c +++ b/hw/scsi-generic.c @@ -96,11 +96,12 @@ static int scsi_get_sense(SCSIRequest *req, uint8_t *outbuf, int len) return size; } -static SCSIRequest *scsi_new_request(SCSIDevice *d, uint32_t tag, uint32_t lun) +static SCSIRequest *scsi_new_request(SCSIDevice *d, uint32_t tag, uint32_t lun, + void *hba_private) { SCSIRequest *req; - req = scsi_req_alloc(sizeof(SCSIGenericReq), d, tag, lun); + req = scsi_req_alloc(sizeof(SCSIGenericReq), d, tag, lun, hba_private); return req; } diff --git a/hw/scsi.h b/hw/scsi.h index c1dca35b86..6b15bbc2cd 100644 --- a/hw/scsi.h +++ b/hw/scsi.h @@ -43,6 +43,7 @@ struct SCSIRequest { } cmd; BlockDriverAIOCB *aiocb; bool enqueued; + void *hba_private; QTAILQ_ENTRY(SCSIRequest) next; }; @@ -67,7 +68,8 @@ struct SCSIDeviceInfo { DeviceInfo qdev; scsi_qdev_initfn init; void (*destroy)(SCSIDevice *s); - SCSIRequest *(*alloc_req)(SCSIDevice *s, uint32_t tag, uint32_t lun); + SCSIRequest *(*alloc_req)(SCSIDevice *s, uint32_t tag, uint32_t lun, + void *hba_private); void (*free_req)(SCSIRequest *req); int32_t (*send_command)(SCSIRequest *req, uint8_t *buf); void (*read_data)(SCSIRequest *req); @@ -138,8 +140,10 @@ extern const struct SCSISense sense_code_LUN_FAILURE; int scsi_build_sense(SCSISense sense, uint8_t *buf, int len, int fixed); int scsi_sense_valid(SCSISense sense); -SCSIRequest *scsi_req_alloc(size_t size, SCSIDevice *d, uint32_t tag, uint32_t lun); -SCSIRequest *scsi_req_new(SCSIDevice *d, uint32_t tag, uint32_t lun); +SCSIRequest *scsi_req_alloc(size_t size, SCSIDevice *d, uint32_t tag, + uint32_t lun, void *hba_private); +SCSIRequest *scsi_req_new(SCSIDevice *d, uint32_t tag, uint32_t lun, + void *hba_private); int32_t scsi_req_enqueue(SCSIRequest *req, uint8_t *buf); void scsi_req_free(SCSIRequest *req); SCSIRequest *scsi_req_ref(SCSIRequest *req); diff --git a/hw/spapr_vscsi.c b/hw/spapr_vscsi.c index 1c901ef6eb..646b1e3caf 100644 --- a/hw/spapr_vscsi.c +++ b/hw/spapr_vscsi.c @@ -121,7 +121,7 @@ static struct vscsi_req *vscsi_get_req(VSCSIState *s) return NULL; } -static void vscsi_put_req(VSCSIState *s, vscsi_req *req) +static void vscsi_put_req(vscsi_req *req) { if (req->sreq != NULL) { scsi_req_unref(req->sreq); @@ -130,15 +130,6 @@ static void vscsi_put_req(VSCSIState *s, vscsi_req *req) req->active = 0; } -static vscsi_req *vscsi_find_req(VSCSIState *s, SCSIRequest *req) -{ - uint32_t tag = req->tag; - if (tag >= VSCSI_REQ_LIMIT || !s->reqs[tag].active) { - return NULL; - } - return &s->reqs[tag]; -} - static void vscsi_decode_id_lun(uint64_t srp_lun, int *id, int *lun) { /* XXX Figure that one out properly ! This is crackpot */ @@ -454,7 +445,7 @@ static void vscsi_send_request_sense(VSCSIState *s, vscsi_req *req) if (n) { req->senselen = n; vscsi_send_rsp(s, req, CHECK_CONDITION, 0, 0); - vscsi_put_req(s, req); + vscsi_put_req(req); return; } @@ -483,7 +474,7 @@ static void vscsi_send_request_sense(VSCSIState *s, vscsi_req *req) static void vscsi_transfer_data(SCSIRequest *sreq, uint32_t len) { VSCSIState *s = DO_UPCAST(VSCSIState, vdev.qdev, sreq->bus->qbus.parent); - vscsi_req *req = vscsi_find_req(s, sreq); + vscsi_req *req = sreq->hba_private; uint8_t *buf; int rc = 0; @@ -531,7 +522,7 @@ static void vscsi_transfer_data(SCSIRequest *sreq, uint32_t len) static void vscsi_command_complete(SCSIRequest *sreq, uint32_t status) { VSCSIState *s = DO_UPCAST(VSCSIState, vdev.qdev, sreq->bus->qbus.parent); - vscsi_req *req = vscsi_find_req(s, sreq); + vscsi_req *req = sreq->hba_private; int32_t res_in = 0, res_out = 0; dprintf("VSCSI: SCSI cmd complete, r=0x%x tag=0x%x status=0x%x, req=%p\n", @@ -563,15 +554,14 @@ static void vscsi_command_complete(SCSIRequest *sreq, uint32_t status) } } vscsi_send_rsp(s, req, 0, res_in, res_out); - vscsi_put_req(s, req); + vscsi_put_req(req); } static void vscsi_request_cancelled(SCSIRequest *sreq) { - VSCSIState *s = DO_UPCAST(VSCSIState, vdev.qdev, sreq->bus->qbus.parent); - vscsi_req *req = vscsi_find_req(s, sreq); + vscsi_req *req = sreq->hba_private; - vscsi_put_req(s, req); + vscsi_put_req(req); } static void vscsi_process_login(VSCSIState *s, vscsi_req *req) @@ -659,7 +649,7 @@ static int vscsi_queue_cmd(VSCSIState *s, vscsi_req *req) } req->lun = lun; - req->sreq = scsi_req_new(sdev, req->qtag, lun); + req->sreq = scsi_req_new(sdev, req->qtag, lun, req); n = scsi_req_enqueue(req->sreq, srp->cmd.cdb); dprintf("VSCSI: Queued command tag 0x%x CMD 0x%x ID %d LUN %d ret: %d\n", @@ -858,7 +848,7 @@ static void vscsi_got_payload(VSCSIState *s, vscsi_crq *crq) } if (done) { - vscsi_put_req(s, req); + vscsi_put_req(req); } } diff --git a/hw/usb-msd.c b/hw/usb-msd.c index 86582cc723..bfea096893 100644 --- a/hw/usb-msd.c +++ b/hw/usb-msd.c @@ -216,10 +216,6 @@ static void usb_msd_transfer_data(SCSIRequest *req, uint32_t len) MSDState *s = DO_UPCAST(MSDState, dev.qdev, req->bus->qbus.parent); USBPacket *p = s->packet; - if (req->tag != s->tag) { - fprintf(stderr, "usb-msd: Unexpected SCSI Tag 0x%x\n", req->tag); - } - assert((s->mode == USB_MSDM_DATAOUT) == (req->cmd.mode == SCSI_XFER_TO_DEV)); s->scsi_len = len; s->scsi_buf = scsi_req_get_buf(req); @@ -241,9 +237,6 @@ static void usb_msd_command_complete(SCSIRequest *req, uint32_t status) MSDState *s = DO_UPCAST(MSDState, dev.qdev, req->bus->qbus.parent); USBPacket *p = s->packet; - if (req->tag != s->tag) { - fprintf(stderr, "usb-msd: Unexpected SCSI Tag 0x%x\n", req->tag); - } DPRINTF("Command complete %d\n", status); s->residue = s->data_len; s->result = status != 0; @@ -387,7 +380,7 @@ static int usb_msd_handle_data(USBDevice *dev, USBPacket *p) s->tag, cbw.flags, cbw.cmd_len, s->data_len); s->residue = 0; s->scsi_len = 0; - s->req = scsi_req_new(s->scsi_dev, s->tag, 0); + s->req = scsi_req_new(s->scsi_dev, s->tag, 0, NULL); scsi_req_enqueue(s->req, cbw.cmd); /* ??? Should check that USB and SCSI data transfer directions match. */ From 653c1c3fb63ca56b9ea33bfd77065915bc02184f Mon Sep 17 00:00:00 2001 From: Hannes Reinecke Date: Mon, 11 Jul 2011 15:02:25 +0200 Subject: [PATCH 140/209] scsi-disk: Fixup debugging statement A debugging statement wasn't converted to the new interface. Signed-off-by: Hannes Reinecke Acked-by: Paolo Bonzini Signed-off-by: Kevin Wolf --- hw/scsi-disk.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hw/scsi-disk.c b/hw/scsi-disk.c index c2a99fe487..5804662793 100644 --- a/hw/scsi-disk.c +++ b/hw/scsi-disk.c @@ -1007,7 +1007,7 @@ static int32_t scsi_send_command(SCSIRequest *req, uint8_t *buf) command = buf[0]; outbuf = (uint8_t *)r->iov.iov_base; - DPRINTF("Command: lun=%d tag=0x%x data=0x%02x", lun, tag, buf[0]); + DPRINTF("Command: lun=%d tag=0x%x data=0x%02x", req->lun, req->tag, buf[0]); if (scsi_req_parse(&r->req, buf) != 0) { BADF("Unsupported command length, command %x\n", command); From 3e1c0c9a4bbaf90a96b14efb771d766fdd091b38 Mon Sep 17 00:00:00 2001 From: Hannes Reinecke Date: Mon, 11 Jul 2011 15:02:26 +0200 Subject: [PATCH 141/209] scsi-disk: Mask out serial number EVPD If the serial number is not set we should mask it out in the list of supported VPD pages and mark it as not supported. Signed-off-by: Hannes Reinecke Acked-by: Paolo Bonzini Signed-off-by: Kevin Wolf --- hw/scsi-disk.c | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/hw/scsi-disk.c b/hw/scsi-disk.c index 5804662793..05d14ab2fd 100644 --- a/hw/scsi-disk.c +++ b/hw/scsi-disk.c @@ -398,7 +398,8 @@ static int scsi_disk_emulate_inquiry(SCSIRequest *req, uint8_t *outbuf) "buffer size %zd\n", req->cmd.xfer); pages = buflen++; outbuf[buflen++] = 0x00; // list of supported pages (this page) - outbuf[buflen++] = 0x80; // unit serial number + if (s->serial) + outbuf[buflen++] = 0x80; // unit serial number outbuf[buflen++] = 0x83; // device identification if (s->drive_kind == SCSI_HD) { outbuf[buflen++] = 0xb0; // block limits @@ -409,8 +410,14 @@ static int scsi_disk_emulate_inquiry(SCSIRequest *req, uint8_t *outbuf) } case 0x80: /* Device serial number, optional */ { - int l = strlen(s->serial); + int l; + if (!s->serial) { + DPRINTF("Inquiry (EVPD[Serial number] not supported\n"); + return -1; + } + + l = strlen(s->serial); if (l > req->cmd.xfer) l = req->cmd.xfer; if (l > 20) @@ -1203,7 +1210,9 @@ static int scsi_initfn(SCSIDevice *dev, SCSIDriveKind kind) if (!s->serial) { /* try to fall back to value set with legacy -drive serial=... */ dinfo = drive_get_by_blockdev(s->bs); - s->serial = qemu_strdup(*dinfo->serial ? dinfo->serial : "0"); + if (*dinfo->serial) { + s->serial = qemu_strdup(dinfo->serial); + } } if (!s->version) { From ae73e5919a75622d2c029d5a0e240115990fb735 Mon Sep 17 00:00:00 2001 From: Luiz Capitulino Date: Tue, 12 Jul 2011 17:35:08 -0300 Subject: [PATCH 142/209] qemu-options.hx: Document missing -drive options They are 'werror', 'rerror' and 'readonly'. Signed-off-by: Luiz Capitulino Signed-off-by: Kevin Wolf --- qemu-options.hx | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/qemu-options.hx b/qemu-options.hx index e6d7adc3af..64114dd448 100644 --- a/qemu-options.hx +++ b/qemu-options.hx @@ -160,6 +160,14 @@ an untrusted format header. This option specifies the serial number to assign to the device. @item addr=@var{addr} Specify the controller's PCI address (if=virtio only). +@item werror=@var{action},rerror=@var{action} +Specify which @var{action} to take on write and read errors. Valid actions are: +"ignore" (ignore the error and try to continue), "stop" (pause QEMU), +"report" (report the error to the guest), "enospc" (pause QEMU only if the +host disk is full; report the error to the guest otherwise). +The default setting is @option{werror=enospc} and @option{rerror=report}. +@item readonly +Open drive @option{file} as read-only. Guest write attempts will fail. @end table By default, writethrough caching is used for all block device. This means that From 69d7e218fd9846b7f5946fe8e17ca609c7fd7ef1 Mon Sep 17 00:00:00 2001 From: Luiz Capitulino Date: Tue, 12 Jul 2011 17:35:09 -0300 Subject: [PATCH 143/209] qemu-config: Document -drive options Signed-off-by: Luiz Capitulino Signed-off-by: Kevin Wolf --- qemu-config.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/qemu-config.c b/qemu-config.c index c63741c6b1..93d20c6cf8 100644 --- a/qemu-config.c +++ b/qemu-config.c @@ -23,6 +23,7 @@ static QemuOptsList qemu_drive_opts = { },{ .name = "index", .type = QEMU_OPT_NUMBER, + .help = "index number", },{ .name = "cyls", .type = QEMU_OPT_NUMBER, @@ -46,6 +47,7 @@ static QemuOptsList qemu_drive_opts = { },{ .name = "snapshot", .type = QEMU_OPT_BOOL, + .help = "enable/disable snapshot mode", },{ .name = "file", .type = QEMU_OPT_STRING, @@ -65,12 +67,15 @@ static QemuOptsList qemu_drive_opts = { },{ .name = "serial", .type = QEMU_OPT_STRING, + .help = "disk serial number", },{ .name = "rerror", .type = QEMU_OPT_STRING, + .help = "read error action", },{ .name = "werror", .type = QEMU_OPT_STRING, + .help = "write error action", },{ .name = "addr", .type = QEMU_OPT_STRING, @@ -78,6 +83,7 @@ static QemuOptsList qemu_drive_opts = { },{ .name = "readonly", .type = QEMU_OPT_BOOL, + .help = "open drive file as read-only", }, { /* end of list */ } }, From b3976d3c8b895e8929d133b80ef5e373b60cf357 Mon Sep 17 00:00:00 2001 From: Fam Zheng Date: Tue, 12 Jul 2011 19:56:28 +0800 Subject: [PATCH 144/209] VMDK: introduce VmdkExtent Introduced VmdkExtent array into BDRVVmdkState, enable holding multiple image extents for multiple file image support. Signed-off-by: Fam Zheng Reviewed-by: Stefan Hajnoczi Signed-off-by: Kevin Wolf --- block/vmdk.c | 348 ++++++++++++++++++++++++++++++++++++--------------- 1 file changed, 246 insertions(+), 102 deletions(-) diff --git a/block/vmdk.c b/block/vmdk.c index 922b23d8f5..3b78583a8a 100644 --- a/block/vmdk.c +++ b/block/vmdk.c @@ -60,7 +60,11 @@ typedef struct { #define L2_CACHE_SIZE 16 -typedef struct BDRVVmdkState { +typedef struct VmdkExtent { + BlockDriverState *file; + bool flat; + int64_t sectors; + int64_t end_sector; int64_t l1_table_offset; int64_t l1_backup_table_offset; uint32_t *l1_table; @@ -74,7 +78,13 @@ typedef struct BDRVVmdkState { uint32_t l2_cache_counts[L2_CACHE_SIZE]; unsigned int cluster_sectors; +} VmdkExtent; + +typedef struct BDRVVmdkState { uint32_t parent_cid; + int num_extents; + /* Extent array with num_extents entries, ascend ordered by address */ + VmdkExtent *extents; } BDRVVmdkState; typedef struct VmdkMetaData { @@ -105,6 +115,19 @@ static int vmdk_probe(const uint8_t *buf, int buf_size, const char *filename) #define DESC_SIZE 20*SECTOR_SIZE // 20 sectors of 512 bytes each #define HEADER_SIZE 512 // first sector of 512 bytes +static void vmdk_free_extents(BlockDriverState *bs) +{ + int i; + BDRVVmdkState *s = bs->opaque; + + for (i = 0; i < s->num_extents; i++) { + qemu_free(s->extents[i].l1_table); + qemu_free(s->extents[i].l2_cache); + qemu_free(s->extents[i].l1_backup_table); + } + qemu_free(s->extents); +} + static uint32_t vmdk_read_cid(BlockDriverState *bs, int parent) { char desc[DESC_SIZE]; @@ -358,11 +381,50 @@ static int vmdk_parent_open(BlockDriverState *bs) return 0; } +/* Create and append extent to the extent array. Return the added VmdkExtent + * address. return NULL if allocation failed. */ +static VmdkExtent *vmdk_add_extent(BlockDriverState *bs, + BlockDriverState *file, bool flat, int64_t sectors, + int64_t l1_offset, int64_t l1_backup_offset, + uint32_t l1_size, + int l2_size, unsigned int cluster_sectors) +{ + VmdkExtent *extent; + BDRVVmdkState *s = bs->opaque; + + s->extents = qemu_realloc(s->extents, + (s->num_extents + 1) * sizeof(VmdkExtent)); + extent = &s->extents[s->num_extents]; + s->num_extents++; + + memset(extent, 0, sizeof(VmdkExtent)); + extent->file = file; + extent->flat = flat; + extent->sectors = sectors; + extent->l1_table_offset = l1_offset; + extent->l1_backup_table_offset = l1_backup_offset; + extent->l1_size = l1_size; + extent->l1_entry_sectors = l2_size * cluster_sectors; + extent->l2_size = l2_size; + extent->cluster_sectors = cluster_sectors; + + if (s->num_extents > 1) { + extent->end_sector = (*(extent - 1)).end_sector + extent->sectors; + } else { + extent->end_sector = extent->sectors; + } + bs->total_sectors = extent->end_sector; + return extent; +} + + static int vmdk_open(BlockDriverState *bs, int flags) { BDRVVmdkState *s = bs->opaque; uint32_t magic; - int l1_size, i; + int i; + uint32_t l1_size, l1_entry_sectors; + VmdkExtent *extent = NULL; if (bdrv_pread(bs->file, 0, &magic, sizeof(magic)) != sizeof(magic)) goto fail; @@ -370,32 +432,34 @@ static int vmdk_open(BlockDriverState *bs, int flags) magic = be32_to_cpu(magic); if (magic == VMDK3_MAGIC) { VMDK3Header header; - - if (bdrv_pread(bs->file, sizeof(magic), &header, sizeof(header)) != sizeof(header)) + if (bdrv_pread(bs->file, sizeof(magic), &header, sizeof(header)) + != sizeof(header)) { goto fail; - s->cluster_sectors = le32_to_cpu(header.granularity); - s->l2_size = 1 << 9; - s->l1_size = 1 << 6; - bs->total_sectors = le32_to_cpu(header.disk_sectors); - s->l1_table_offset = le32_to_cpu(header.l1dir_offset) << 9; - s->l1_backup_table_offset = 0; - s->l1_entry_sectors = s->l2_size * s->cluster_sectors; + } + extent = vmdk_add_extent(bs, bs->file, false, + le32_to_cpu(header.disk_sectors), + le32_to_cpu(header.l1dir_offset) << 9, 0, + 1 << 6, 1 << 9, le32_to_cpu(header.granularity)); } else if (magic == VMDK4_MAGIC) { VMDK4Header header; - - if (bdrv_pread(bs->file, sizeof(magic), &header, sizeof(header)) != sizeof(header)) + if (bdrv_pread(bs->file, sizeof(magic), &header, sizeof(header)) + != sizeof(header)) { goto fail; - bs->total_sectors = le64_to_cpu(header.capacity); - s->cluster_sectors = le64_to_cpu(header.granularity); - s->l2_size = le32_to_cpu(header.num_gtes_per_gte); - s->l1_entry_sectors = s->l2_size * s->cluster_sectors; - if (s->l1_entry_sectors <= 0) + } + l1_entry_sectors = le32_to_cpu(header.num_gtes_per_gte) + * le64_to_cpu(header.granularity); + l1_size = (le64_to_cpu(header.capacity) + l1_entry_sectors - 1) + / l1_entry_sectors; + extent = vmdk_add_extent(bs, bs->file, false, + le64_to_cpu(header.capacity), + le64_to_cpu(header.gd_offset) << 9, + le64_to_cpu(header.rgd_offset) << 9, + l1_size, + le32_to_cpu(header.num_gtes_per_gte), + le64_to_cpu(header.granularity)); + if (extent->l1_entry_sectors <= 0) { goto fail; - s->l1_size = (bs->total_sectors + s->l1_entry_sectors - 1) - / s->l1_entry_sectors; - s->l1_table_offset = le64_to_cpu(header.rgd_offset) << 9; - s->l1_backup_table_offset = le64_to_cpu(header.gd_offset) << 9; - + } // try to open parent images, if exist if (vmdk_parent_open(bs) != 0) goto fail; @@ -406,40 +470,49 @@ static int vmdk_open(BlockDriverState *bs, int flags) } /* read the L1 table */ - l1_size = s->l1_size * sizeof(uint32_t); - s->l1_table = qemu_malloc(l1_size); - if (bdrv_pread(bs->file, s->l1_table_offset, s->l1_table, l1_size) != l1_size) + l1_size = extent->l1_size * sizeof(uint32_t); + extent->l1_table = qemu_malloc(l1_size); + if (bdrv_pread(bs->file, + extent->l1_table_offset, + extent->l1_table, + l1_size) + != l1_size) { goto fail; - for(i = 0; i < s->l1_size; i++) { - le32_to_cpus(&s->l1_table[i]); + } + for (i = 0; i < extent->l1_size; i++) { + le32_to_cpus(&extent->l1_table[i]); } - if (s->l1_backup_table_offset) { - s->l1_backup_table = qemu_malloc(l1_size); - if (bdrv_pread(bs->file, s->l1_backup_table_offset, s->l1_backup_table, l1_size) != l1_size) + if (extent->l1_backup_table_offset) { + extent->l1_backup_table = qemu_malloc(l1_size); + if (bdrv_pread(bs->file, + extent->l1_backup_table_offset, + extent->l1_backup_table, + l1_size) + != l1_size) { goto fail; - for(i = 0; i < s->l1_size; i++) { - le32_to_cpus(&s->l1_backup_table[i]); + } + for (i = 0; i < extent->l1_size; i++) { + le32_to_cpus(&extent->l1_backup_table[i]); } } - s->l2_cache = qemu_malloc(s->l2_size * L2_CACHE_SIZE * sizeof(uint32_t)); + extent->l2_cache = + qemu_malloc(extent->l2_size * L2_CACHE_SIZE * sizeof(uint32_t)); return 0; fail: - qemu_free(s->l1_backup_table); - qemu_free(s->l1_table); - qemu_free(s->l2_cache); + vmdk_free_extents(bs); return -1; } -static uint64_t get_cluster_offset(BlockDriverState *bs, VmdkMetaData *m_data, - uint64_t offset, int allocate); - -static int get_whole_cluster(BlockDriverState *bs, uint64_t cluster_offset, - uint64_t offset, int allocate) +static int get_whole_cluster(BlockDriverState *bs, + VmdkExtent *extent, + uint64_t cluster_offset, + uint64_t offset, + bool allocate) { - BDRVVmdkState *s = bs->opaque; - uint8_t whole_grain[s->cluster_sectors*512]; // 128 sectors * 512 bytes each = grain size 64KB + /* 128 sectors * 512 bytes each = grain size 64KB */ + uint8_t whole_grain[extent->cluster_sectors * 512]; // we will be here if it's first write on non-exist grain(cluster). // try to read from parent image, if exist @@ -450,14 +523,14 @@ static int get_whole_cluster(BlockDriverState *bs, uint64_t cluster_offset, return -1; ret = bdrv_read(bs->backing_hd, offset >> 9, whole_grain, - s->cluster_sectors); + extent->cluster_sectors); if (ret < 0) { return -1; } //Write grain only into the active image - ret = bdrv_write(bs->file, cluster_offset, whole_grain, - s->cluster_sectors); + ret = bdrv_write(extent->file, cluster_offset, whole_grain, + extent->cluster_sectors); if (ret < 0) { return -1; } @@ -465,29 +538,39 @@ static int get_whole_cluster(BlockDriverState *bs, uint64_t cluster_offset, return 0; } -static int vmdk_L2update(BlockDriverState *bs, VmdkMetaData *m_data) +static int vmdk_L2update(VmdkExtent *extent, VmdkMetaData *m_data) { - BDRVVmdkState *s = bs->opaque; - /* update L2 table */ - if (bdrv_pwrite_sync(bs->file, ((int64_t)m_data->l2_offset * 512) + (m_data->l2_index * sizeof(m_data->offset)), - &(m_data->offset), sizeof(m_data->offset)) < 0) + if (bdrv_pwrite_sync( + extent->file, + ((int64_t)m_data->l2_offset * 512) + + (m_data->l2_index * sizeof(m_data->offset)), + &(m_data->offset), + sizeof(m_data->offset) + ) < 0) { return -1; + } /* update backup L2 table */ - if (s->l1_backup_table_offset != 0) { - m_data->l2_offset = s->l1_backup_table[m_data->l1_index]; - if (bdrv_pwrite_sync(bs->file, ((int64_t)m_data->l2_offset * 512) + (m_data->l2_index * sizeof(m_data->offset)), - &(m_data->offset), sizeof(m_data->offset)) < 0) + if (extent->l1_backup_table_offset != 0) { + m_data->l2_offset = extent->l1_backup_table[m_data->l1_index]; + if (bdrv_pwrite_sync( + extent->file, + ((int64_t)m_data->l2_offset * 512) + + (m_data->l2_index * sizeof(m_data->offset)), + &(m_data->offset), sizeof(m_data->offset) + ) < 0) { return -1; + } } return 0; } -static uint64_t get_cluster_offset(BlockDriverState *bs, VmdkMetaData *m_data, - uint64_t offset, int allocate) +static uint64_t get_cluster_offset(BlockDriverState *bs, + VmdkExtent *extent, + VmdkMetaData *m_data, + uint64_t offset, int allocate) { - BDRVVmdkState *s = bs->opaque; unsigned int l1_index, l2_offset, l2_index; int min_index, i, j; uint32_t min_count, *l2_table, tmp = 0; @@ -496,21 +579,23 @@ static uint64_t get_cluster_offset(BlockDriverState *bs, VmdkMetaData *m_data, if (m_data) m_data->valid = 0; - l1_index = (offset >> 9) / s->l1_entry_sectors; - if (l1_index >= s->l1_size) + l1_index = (offset >> 9) / extent->l1_entry_sectors; + if (l1_index >= extent->l1_size) { return 0; - l2_offset = s->l1_table[l1_index]; - if (!l2_offset) + } + l2_offset = extent->l1_table[l1_index]; + if (!l2_offset) { return 0; + } for(i = 0; i < L2_CACHE_SIZE; i++) { - if (l2_offset == s->l2_cache_offsets[i]) { + if (l2_offset == extent->l2_cache_offsets[i]) { /* increment the hit count */ - if (++s->l2_cache_counts[i] == 0xffffffff) { + if (++extent->l2_cache_counts[i] == 0xffffffff) { for(j = 0; j < L2_CACHE_SIZE; j++) { - s->l2_cache_counts[j] >>= 1; + extent->l2_cache_counts[j] >>= 1; } } - l2_table = s->l2_cache + (i * s->l2_size); + l2_table = extent->l2_cache + (i * extent->l2_size); goto found; } } @@ -518,20 +603,25 @@ static uint64_t get_cluster_offset(BlockDriverState *bs, VmdkMetaData *m_data, min_index = 0; min_count = 0xffffffff; for(i = 0; i < L2_CACHE_SIZE; i++) { - if (s->l2_cache_counts[i] < min_count) { - min_count = s->l2_cache_counts[i]; + if (extent->l2_cache_counts[i] < min_count) { + min_count = extent->l2_cache_counts[i]; min_index = i; } } - l2_table = s->l2_cache + (min_index * s->l2_size); - if (bdrv_pread(bs->file, (int64_t)l2_offset * 512, l2_table, s->l2_size * sizeof(uint32_t)) != - s->l2_size * sizeof(uint32_t)) + l2_table = extent->l2_cache + (min_index * extent->l2_size); + if (bdrv_pread( + extent->file, + (int64_t)l2_offset * 512, + l2_table, + extent->l2_size * sizeof(uint32_t) + ) != extent->l2_size * sizeof(uint32_t)) { return 0; + } - s->l2_cache_offsets[min_index] = l2_offset; - s->l2_cache_counts[min_index] = 1; + extent->l2_cache_offsets[min_index] = l2_offset; + extent->l2_cache_counts[min_index] = 1; found: - l2_index = ((offset >> 9) / s->cluster_sectors) % s->l2_size; + l2_index = ((offset >> 9) / extent->cluster_sectors) % extent->l2_size; cluster_offset = le32_to_cpu(l2_table[l2_index]); if (!cluster_offset) { @@ -539,8 +629,11 @@ static uint64_t get_cluster_offset(BlockDriverState *bs, VmdkMetaData *m_data, return 0; // Avoid the L2 tables update for the images that have snapshots. - cluster_offset = bdrv_getlength(bs->file); - bdrv_truncate(bs->file, cluster_offset + (s->cluster_sectors << 9)); + cluster_offset = bdrv_getlength(extent->file); + bdrv_truncate( + extent->file, + cluster_offset + (extent->cluster_sectors << 9) + ); cluster_offset >>= 9; tmp = cpu_to_le32(cluster_offset); @@ -551,7 +644,8 @@ static uint64_t get_cluster_offset(BlockDriverState *bs, VmdkMetaData *m_data, * This problem may occur because of insufficient space on host disk * or inappropriate VM shutdown. */ - if (get_whole_cluster(bs, cluster_offset, offset, allocate) == -1) + if (get_whole_cluster( + bs, extent, cluster_offset, offset, allocate) == -1) return 0; if (m_data) { @@ -566,33 +660,69 @@ static uint64_t get_cluster_offset(BlockDriverState *bs, VmdkMetaData *m_data, return cluster_offset; } +static VmdkExtent *find_extent(BDRVVmdkState *s, + int64_t sector_num, VmdkExtent *start_hint) +{ + VmdkExtent *extent = start_hint; + + if (!extent) { + extent = &s->extents[0]; + } + while (extent < &s->extents[s->num_extents]) { + if (sector_num < extent->end_sector) { + return extent; + } + extent++; + } + return NULL; +} + static int vmdk_is_allocated(BlockDriverState *bs, int64_t sector_num, int nb_sectors, int *pnum) { BDRVVmdkState *s = bs->opaque; - int index_in_cluster, n; - uint64_t cluster_offset; - cluster_offset = get_cluster_offset(bs, NULL, sector_num << 9, 0); - index_in_cluster = sector_num % s->cluster_sectors; - n = s->cluster_sectors - index_in_cluster; + int64_t index_in_cluster, n, ret; + uint64_t offset; + VmdkExtent *extent; + + extent = find_extent(s, sector_num, NULL); + if (!extent) { + return 0; + } + if (extent->flat) { + n = extent->end_sector - sector_num; + ret = 1; + } else { + offset = get_cluster_offset(bs, extent, NULL, sector_num * 512, 0); + index_in_cluster = sector_num % extent->cluster_sectors; + n = extent->cluster_sectors - index_in_cluster; + ret = offset ? 1 : 0; + } if (n > nb_sectors) n = nb_sectors; *pnum = n; - return (cluster_offset != 0); + return ret; } static int vmdk_read(BlockDriverState *bs, int64_t sector_num, uint8_t *buf, int nb_sectors) { BDRVVmdkState *s = bs->opaque; - int index_in_cluster, n, ret; + int ret; + uint64_t n, index_in_cluster; + VmdkExtent *extent = NULL; uint64_t cluster_offset; while (nb_sectors > 0) { - cluster_offset = get_cluster_offset(bs, NULL, sector_num << 9, 0); - index_in_cluster = sector_num % s->cluster_sectors; - n = s->cluster_sectors - index_in_cluster; + extent = find_extent(s, sector_num, extent); + if (!extent) { + return -EIO; + } + cluster_offset = get_cluster_offset( + bs, extent, NULL, sector_num << 9, 0); + index_in_cluster = sector_num % extent->cluster_sectors; + n = extent->cluster_sectors - index_in_cluster; if (n > nb_sectors) n = nb_sectors; if (!cluster_offset) { @@ -621,10 +751,12 @@ static int vmdk_write(BlockDriverState *bs, int64_t sector_num, const uint8_t *buf, int nb_sectors) { BDRVVmdkState *s = bs->opaque; - VmdkMetaData m_data; - int index_in_cluster, n; + VmdkExtent *extent = NULL; + int n; + int64_t index_in_cluster; uint64_t cluster_offset; static int cid_update = 0; + VmdkMetaData m_data; if (sector_num > bs->total_sectors) { fprintf(stderr, @@ -635,20 +767,35 @@ static int vmdk_write(BlockDriverState *bs, int64_t sector_num, } while (nb_sectors > 0) { - index_in_cluster = sector_num & (s->cluster_sectors - 1); - n = s->cluster_sectors - index_in_cluster; - if (n > nb_sectors) + extent = find_extent(s, sector_num, extent); + if (!extent) { + return -EIO; + } + cluster_offset = get_cluster_offset( + bs, + extent, + &m_data, + sector_num << 9, 1); + if (!cluster_offset) { + return -1; + } + index_in_cluster = sector_num % extent->cluster_sectors; + n = extent->cluster_sectors - index_in_cluster; + if (n > nb_sectors) { n = nb_sectors; - cluster_offset = get_cluster_offset(bs, &m_data, sector_num << 9, 1); - if (!cluster_offset) - return -1; + } - if (bdrv_pwrite(bs->file, cluster_offset + index_in_cluster * 512, buf, n * 512) != n * 512) + if (bdrv_pwrite(bs->file, + cluster_offset + index_in_cluster * 512, + buf, n * 512) + != n * 512) { return -1; + } if (m_data.valid) { /* update L2 tables */ - if (vmdk_L2update(bs, &m_data) == -1) + if (vmdk_L2update(extent, &m_data) == -1) { return -1; + } } nb_sectors -= n; sector_num += n; @@ -822,10 +969,7 @@ exit: static void vmdk_close(BlockDriverState *bs) { - BDRVVmdkState *s = bs->opaque; - - qemu_free(s->l1_table); - qemu_free(s->l2_cache); + vmdk_free_extents(bs); } static int vmdk_flush(BlockDriverState *bs) From 0e69c543946957fe8450ebb13874bb1ef280d55b Mon Sep 17 00:00:00 2001 From: Fam Zheng Date: Tue, 12 Jul 2011 19:56:29 +0800 Subject: [PATCH 145/209] VMDK: bugfix, align offset to cluster in get_whole_cluster In get_whole_cluster, the offset is not aligned to cluster when reading from backing_hd. When the first write to child is not at the cluster boundary, wrong address data from parent is copied to child. Signed-off-by: Fam Zheng Reviewed-by: Stefan Hajnoczi Signed-off-by: Kevin Wolf --- block/vmdk.c | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/block/vmdk.c b/block/vmdk.c index 3b78583a8a..03a4619201 100644 --- a/block/vmdk.c +++ b/block/vmdk.c @@ -514,21 +514,23 @@ static int get_whole_cluster(BlockDriverState *bs, /* 128 sectors * 512 bytes each = grain size 64KB */ uint8_t whole_grain[extent->cluster_sectors * 512]; - // we will be here if it's first write on non-exist grain(cluster). - // try to read from parent image, if exist + /* we will be here if it's first write on non-exist grain(cluster). + * try to read from parent image, if exist */ if (bs->backing_hd) { int ret; if (!vmdk_is_cid_valid(bs)) return -1; + /* floor offset to cluster */ + offset -= offset % (extent->cluster_sectors * 512); ret = bdrv_read(bs->backing_hd, offset >> 9, whole_grain, extent->cluster_sectors); if (ret < 0) { return -1; } - //Write grain only into the active image + /* Write grain only into the active image */ ret = bdrv_write(extent->file, cluster_offset, whole_grain, extent->cluster_sectors); if (ret < 0) { From 01fc99d6a8f56c8dc4a7466476d6dcb0ad91f589 Mon Sep 17 00:00:00 2001 From: Fam Zheng Date: Tue, 12 Jul 2011 19:56:30 +0800 Subject: [PATCH 146/209] VMDK: probe for monolithicFlat images Probe as the same behavior as VMware does. Recognize image as monolithicFlat descriptor file when the file is text and the first effective line (not '#' leaded comment or space line) is either 'version=1' or 'version=2'. No space or upper case charactors accepted. Signed-off-by: Fam Zheng Reviewed-by: Stefan Hajnoczi Signed-off-by: Kevin Wolf --- block/vmdk.c | 45 +++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 43 insertions(+), 2 deletions(-) diff --git a/block/vmdk.c b/block/vmdk.c index 03a4619201..f8a815c749 100644 --- a/block/vmdk.c +++ b/block/vmdk.c @@ -103,10 +103,51 @@ static int vmdk_probe(const uint8_t *buf, int buf_size, const char *filename) return 0; magic = be32_to_cpu(*(uint32_t *)buf); if (magic == VMDK3_MAGIC || - magic == VMDK4_MAGIC) + magic == VMDK4_MAGIC) { return 100; - else + } else { + const char *p = (const char *)buf; + const char *end = p + buf_size; + while (p < end) { + if (*p == '#') { + /* skip comment line */ + while (p < end && *p != '\n') { + p++; + } + p++; + continue; + } + if (*p == ' ') { + while (p < end && *p == ' ') { + p++; + } + /* skip '\r' if windows line endings used. */ + if (p < end && *p == '\r') { + p++; + } + /* only accept blank lines before 'version=' line */ + if (p == end || *p != '\n') { + return 0; + } + p++; + continue; + } + if (end - p >= strlen("version=X\n")) { + if (strncmp("version=1\n", p, strlen("version=1\n")) == 0 || + strncmp("version=2\n", p, strlen("version=2\n")) == 0) { + return 100; + } + } + if (end - p >= strlen("version=X\r\n")) { + if (strncmp("version=1\r\n", p, strlen("version=1\r\n")) == 0 || + strncmp("version=2\r\n", p, strlen("version=2\r\n")) == 0) { + return 100; + } + } + return 0; + } return 0; + } } #define CHECK_CID 1 From b4b3ab146c6d8a7ababc5760c9a0ef7cee78707e Mon Sep 17 00:00:00 2001 From: Fam Zheng Date: Tue, 12 Jul 2011 19:56:31 +0800 Subject: [PATCH 147/209] VMDK: separate vmdk_open by format version Separate vmdk_open by subformats to: * vmdk_open_vmdk3 * vmdk_open_vmdk4 Signed-off-by: Fam Zheng Reviewed-by: Stefan Hajnoczi Signed-off-by: Kevin Wolf --- block/vmdk.c | 178 ++++++++++++++++++++++++++++++++------------------- 1 file changed, 112 insertions(+), 66 deletions(-) diff --git a/block/vmdk.c b/block/vmdk.c index f8a815c749..6d7b497202 100644 --- a/block/vmdk.c +++ b/block/vmdk.c @@ -458,67 +458,20 @@ static VmdkExtent *vmdk_add_extent(BlockDriverState *bs, return extent; } - -static int vmdk_open(BlockDriverState *bs, int flags) +static int vmdk_init_tables(BlockDriverState *bs, VmdkExtent *extent) { - BDRVVmdkState *s = bs->opaque; - uint32_t magic; - int i; - uint32_t l1_size, l1_entry_sectors; - VmdkExtent *extent = NULL; - - if (bdrv_pread(bs->file, 0, &magic, sizeof(magic)) != sizeof(magic)) - goto fail; - - magic = be32_to_cpu(magic); - if (magic == VMDK3_MAGIC) { - VMDK3Header header; - if (bdrv_pread(bs->file, sizeof(magic), &header, sizeof(header)) - != sizeof(header)) { - goto fail; - } - extent = vmdk_add_extent(bs, bs->file, false, - le32_to_cpu(header.disk_sectors), - le32_to_cpu(header.l1dir_offset) << 9, 0, - 1 << 6, 1 << 9, le32_to_cpu(header.granularity)); - } else if (magic == VMDK4_MAGIC) { - VMDK4Header header; - if (bdrv_pread(bs->file, sizeof(magic), &header, sizeof(header)) - != sizeof(header)) { - goto fail; - } - l1_entry_sectors = le32_to_cpu(header.num_gtes_per_gte) - * le64_to_cpu(header.granularity); - l1_size = (le64_to_cpu(header.capacity) + l1_entry_sectors - 1) - / l1_entry_sectors; - extent = vmdk_add_extent(bs, bs->file, false, - le64_to_cpu(header.capacity), - le64_to_cpu(header.gd_offset) << 9, - le64_to_cpu(header.rgd_offset) << 9, - l1_size, - le32_to_cpu(header.num_gtes_per_gte), - le64_to_cpu(header.granularity)); - if (extent->l1_entry_sectors <= 0) { - goto fail; - } - // try to open parent images, if exist - if (vmdk_parent_open(bs) != 0) - goto fail; - // write the CID once after the image creation - s->parent_cid = vmdk_read_cid(bs,1); - } else { - goto fail; - } + int ret; + int l1_size, i; /* read the L1 table */ l1_size = extent->l1_size * sizeof(uint32_t); extent->l1_table = qemu_malloc(l1_size); - if (bdrv_pread(bs->file, - extent->l1_table_offset, - extent->l1_table, - l1_size) - != l1_size) { - goto fail; + ret = bdrv_pread(extent->file, + extent->l1_table_offset, + extent->l1_table, + l1_size); + if (ret < 0) { + goto fail_l1; } for (i = 0; i < extent->l1_size; i++) { le32_to_cpus(&extent->l1_table[i]); @@ -526,12 +479,12 @@ static int vmdk_open(BlockDriverState *bs, int flags) if (extent->l1_backup_table_offset) { extent->l1_backup_table = qemu_malloc(l1_size); - if (bdrv_pread(bs->file, - extent->l1_backup_table_offset, - extent->l1_backup_table, - l1_size) - != l1_size) { - goto fail; + ret = bdrv_pread(extent->file, + extent->l1_backup_table_offset, + extent->l1_backup_table, + l1_size); + if (ret < 0) { + goto fail_l1b; } for (i = 0; i < extent->l1_size; i++) { le32_to_cpus(&extent->l1_backup_table[i]); @@ -541,9 +494,102 @@ static int vmdk_open(BlockDriverState *bs, int flags) extent->l2_cache = qemu_malloc(extent->l2_size * L2_CACHE_SIZE * sizeof(uint32_t)); return 0; + fail_l1b: + qemu_free(extent->l1_backup_table); + fail_l1: + qemu_free(extent->l1_table); + return ret; +} + +static int vmdk_open_vmdk3(BlockDriverState *bs, int flags) +{ + int ret; + uint32_t magic; + VMDK3Header header; + VmdkExtent *extent; + + ret = bdrv_pread(bs->file, sizeof(magic), &header, sizeof(header)); + if (ret < 0) { + goto fail; + } + extent = vmdk_add_extent(bs, + bs->file, false, + le32_to_cpu(header.disk_sectors), + le32_to_cpu(header.l1dir_offset) << 9, + 0, 1 << 6, 1 << 9, + le32_to_cpu(header.granularity)); + ret = vmdk_init_tables(bs, extent); + if (ret) { + /* vmdk_init_tables cleans up on fail, so only free allocation of + * vmdk_add_extent here. */ + goto fail; + } + return 0; fail: vmdk_free_extents(bs); - return -1; + return ret; +} + +static int vmdk_open_vmdk4(BlockDriverState *bs, int flags) +{ + int ret; + uint32_t magic; + uint32_t l1_size, l1_entry_sectors; + VMDK4Header header; + BDRVVmdkState *s = bs->opaque; + VmdkExtent *extent; + + ret = bdrv_pread(bs->file, sizeof(magic), &header, sizeof(header)); + if (ret < 0) { + goto fail; + } + l1_entry_sectors = le32_to_cpu(header.num_gtes_per_gte) + * le64_to_cpu(header.granularity); + l1_size = (le64_to_cpu(header.capacity) + l1_entry_sectors - 1) + / l1_entry_sectors; + extent = vmdk_add_extent(bs, bs->file, false, + le64_to_cpu(header.capacity), + le64_to_cpu(header.gd_offset) << 9, + le64_to_cpu(header.rgd_offset) << 9, + l1_size, + le32_to_cpu(header.num_gtes_per_gte), + le64_to_cpu(header.granularity)); + if (extent->l1_entry_sectors <= 0) { + ret = -EINVAL; + goto fail; + } + /* try to open parent images, if exist */ + ret = vmdk_parent_open(bs); + if (ret) { + goto fail; + } + s->parent_cid = vmdk_read_cid(bs, 1); + ret = vmdk_init_tables(bs, extent); + if (ret) { + goto fail; + } + return 0; + fail: + vmdk_free_extents(bs); + return ret; +} + +static int vmdk_open(BlockDriverState *bs, int flags) +{ + uint32_t magic; + + if (bdrv_pread(bs->file, 0, &magic, sizeof(magic)) != sizeof(magic)) { + return -EIO; + } + + magic = be32_to_cpu(magic); + if (magic == VMDK3_MAGIC) { + return vmdk_open_vmdk3(bs, flags); + } else if (magic == VMDK4_MAGIC) { + return vmdk_open_vmdk4(bs, flags); + } else { + return -EINVAL; + } } static int get_whole_cluster(BlockDriverState *bs, @@ -630,11 +676,11 @@ static uint64_t get_cluster_offset(BlockDriverState *bs, if (!l2_offset) { return 0; } - for(i = 0; i < L2_CACHE_SIZE; i++) { + for (i = 0; i < L2_CACHE_SIZE; i++) { if (l2_offset == extent->l2_cache_offsets[i]) { /* increment the hit count */ if (++extent->l2_cache_counts[i] == 0xffffffff) { - for(j = 0; j < L2_CACHE_SIZE; j++) { + for (j = 0; j < L2_CACHE_SIZE; j++) { extent->l2_cache_counts[j] >>= 1; } } @@ -645,7 +691,7 @@ static uint64_t get_cluster_offset(BlockDriverState *bs, /* not found: load a new entry in the least used one */ min_index = 0; min_count = 0xffffffff; - for(i = 0; i < L2_CACHE_SIZE; i++) { + for (i = 0; i < L2_CACHE_SIZE; i++) { if (extent->l2_cache_counts[i] < min_count) { min_count = extent->l2_cache_counts[i]; min_index = i; From e1da9b2433f78c4de3bac8c2a97173c648d212c0 Mon Sep 17 00:00:00 2001 From: Fam Zheng Date: Tue, 12 Jul 2011 19:56:32 +0800 Subject: [PATCH 148/209] VMDK: add field BDRVVmdkState.desc_offset There are several occurrence of magic number 0x200 as the descriptor offset within mono sparse image file. This is not the case for images with separate descriptor file. So a field is added to BDRVVmdkState to hold the correct value. Signed-off-by: Fam Zheng Reviewed-by: Stefan Hajnoczi Signed-off-by: Kevin Wolf --- block/vmdk.c | 27 ++++++++++++++++++--------- 1 file changed, 18 insertions(+), 9 deletions(-) diff --git a/block/vmdk.c b/block/vmdk.c index 6d7b497202..529ae90fdb 100644 --- a/block/vmdk.c +++ b/block/vmdk.c @@ -81,6 +81,7 @@ typedef struct VmdkExtent { } VmdkExtent; typedef struct BDRVVmdkState { + int desc_offset; uint32_t parent_cid; int num_extents; /* Extent array with num_extents entries, ascend ordered by address */ @@ -175,10 +176,11 @@ static uint32_t vmdk_read_cid(BlockDriverState *bs, int parent) uint32_t cid; const char *p_name, *cid_str; size_t cid_str_size; + BDRVVmdkState *s = bs->opaque; - /* the descriptor offset = 0x200 */ - if (bdrv_pread(bs->file, 0x200, desc, DESC_SIZE) != DESC_SIZE) + if (bdrv_pread(bs->file, s->desc_offset, desc, DESC_SIZE) != DESC_SIZE) { return 0; + } if (parent) { cid_str = "parentCID"; @@ -200,10 +202,12 @@ static int vmdk_write_cid(BlockDriverState *bs, uint32_t cid) { char desc[DESC_SIZE], tmp_desc[DESC_SIZE]; char *p_name, *tmp_str; + BDRVVmdkState *s = bs->opaque; - /* the descriptor offset = 0x200 */ - if (bdrv_pread(bs->file, 0x200, desc, DESC_SIZE) != DESC_SIZE) - return -1; + memset(desc, 0, sizeof(desc)); + if (bdrv_pread(bs->file, s->desc_offset, desc, DESC_SIZE) != DESC_SIZE) { + return -EIO; + } tmp_str = strstr(desc,"parentCID"); pstrcpy(tmp_desc, sizeof(tmp_desc), tmp_str); @@ -213,8 +217,9 @@ static int vmdk_write_cid(BlockDriverState *bs, uint32_t cid) pstrcat(desc, sizeof(desc), tmp_desc); } - if (bdrv_pwrite_sync(bs->file, 0x200, desc, DESC_SIZE) < 0) - return -1; + if (bdrv_pwrite_sync(bs->file, s->desc_offset, desc, DESC_SIZE) < 0) { + return -EIO; + } return 0; } @@ -402,10 +407,11 @@ static int vmdk_parent_open(BlockDriverState *bs) { char *p_name; char desc[DESC_SIZE]; + BDRVVmdkState *s = bs->opaque; - /* the descriptor offset = 0x200 */ - if (bdrv_pread(bs->file, 0x200, desc, DESC_SIZE) != DESC_SIZE) + if (bdrv_pread(bs->file, s->desc_offset, desc, DESC_SIZE) != DESC_SIZE) { return -1; + } if ((p_name = strstr(desc,"parentFileNameHint")) != NULL) { char *end_name; @@ -506,8 +512,10 @@ static int vmdk_open_vmdk3(BlockDriverState *bs, int flags) int ret; uint32_t magic; VMDK3Header header; + BDRVVmdkState *s = bs->opaque; VmdkExtent *extent; + s->desc_offset = 0x200; ret = bdrv_pread(bs->file, sizeof(magic), &header, sizeof(header)); if (ret < 0) { goto fail; @@ -539,6 +547,7 @@ static int vmdk_open_vmdk4(BlockDriverState *bs, int flags) BDRVVmdkState *s = bs->opaque; VmdkExtent *extent; + s->desc_offset = 0x200; ret = bdrv_pread(bs->file, sizeof(magic), &header, sizeof(header)); if (ret < 0) { goto fail; From 333c574d054f95912beef4f020f4128868463fd4 Mon Sep 17 00:00:00 2001 From: Fam Zheng Date: Tue, 12 Jul 2011 19:56:33 +0800 Subject: [PATCH 149/209] VMDK: flush multiple extents Flush all the file that referenced by the image. Signed-off-by: Fam Zheng Reviewed-by: Stefan Hajnoczi Signed-off-by: Kevin Wolf --- block/vmdk.c | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/block/vmdk.c b/block/vmdk.c index 529ae90fdb..f6d298635c 100644 --- a/block/vmdk.c +++ b/block/vmdk.c @@ -1072,7 +1072,17 @@ static void vmdk_close(BlockDriverState *bs) static int vmdk_flush(BlockDriverState *bs) { - return bdrv_flush(bs->file); + int i, ret, err; + BDRVVmdkState *s = bs->opaque; + + ret = bdrv_flush(bs->file); + for (i = 0; i < s->num_extents; i++) { + err = bdrv_flush(s->extents[i].file); + if (err < 0) { + ret = err; + } + } + return ret; } From 69b4d86d9f7feb6083cedb0c5fb65fdc30daf623 Mon Sep 17 00:00:00 2001 From: Fam Zheng Date: Tue, 12 Jul 2011 19:56:34 +0800 Subject: [PATCH 150/209] VMDK: move 'static' cid_update flag to bs field Cid_update is the flag for updating CID on first write after opening the image. This should be per image open rather than per program life cycle, so change it from static var of vmdk_write to a field in BDRVVmdkState. Signed-off-by: Fam Zheng Reviewed-by: Stefan Hajnoczi Signed-off-by: Kevin Wolf --- block/vmdk.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/block/vmdk.c b/block/vmdk.c index f6d298635c..8dc58a8cf0 100644 --- a/block/vmdk.c +++ b/block/vmdk.c @@ -82,6 +82,7 @@ typedef struct VmdkExtent { typedef struct BDRVVmdkState { int desc_offset; + bool cid_updated; uint32_t parent_cid; int num_extents; /* Extent array with num_extents entries, ascend ordered by address */ @@ -853,7 +854,6 @@ static int vmdk_write(BlockDriverState *bs, int64_t sector_num, int n; int64_t index_in_cluster; uint64_t cluster_offset; - static int cid_update = 0; VmdkMetaData m_data; if (sector_num > bs->total_sectors) { @@ -900,9 +900,9 @@ static int vmdk_write(BlockDriverState *bs, int64_t sector_num, buf += n * 512; // update CID on the first write every time the virtual disk is opened - if (!cid_update) { + if (!s->cid_updated) { vmdk_write_cid(bs, time(NULL)); - cid_update++; + s->cid_updated = true; } } return 0; From 91b85bd388c3767e6b63aaf33851dbfe87ea24d1 Mon Sep 17 00:00:00 2001 From: Fam Zheng Date: Tue, 12 Jul 2011 19:56:35 +0800 Subject: [PATCH 151/209] VMDK: change get_cluster_offset return type The return type of get_cluster_offset was an offset that use 0 to denote 'not allocated', this will be no longer true for flat extents, as we see flat extent file as a single huge cluster whose offset is 0 and length is the whole file length. So now we use int return value, 0 means success and otherwise offset invalid. Signed-off-by: Fam Zheng Reviewed-by: Stefan Hajnoczi Signed-off-by: Kevin Wolf --- block/vmdk.c | 79 ++++++++++++++++++++++++++++------------------------ 1 file changed, 42 insertions(+), 37 deletions(-) diff --git a/block/vmdk.c b/block/vmdk.c index 8dc58a8cf0..f637d98d47 100644 --- a/block/vmdk.c +++ b/block/vmdk.c @@ -665,26 +665,31 @@ static int vmdk_L2update(VmdkExtent *extent, VmdkMetaData *m_data) return 0; } -static uint64_t get_cluster_offset(BlockDriverState *bs, +static int get_cluster_offset(BlockDriverState *bs, VmdkExtent *extent, VmdkMetaData *m_data, - uint64_t offset, int allocate) + uint64_t offset, + int allocate, + uint64_t *cluster_offset) { unsigned int l1_index, l2_offset, l2_index; int min_index, i, j; uint32_t min_count, *l2_table, tmp = 0; - uint64_t cluster_offset; if (m_data) m_data->valid = 0; + if (extent->flat) { + *cluster_offset = 0; + return 0; + } l1_index = (offset >> 9) / extent->l1_entry_sectors; if (l1_index >= extent->l1_size) { - return 0; + return -1; } l2_offset = extent->l1_table[l1_index]; if (!l2_offset) { - return 0; + return -1; } for (i = 0; i < L2_CACHE_SIZE; i++) { if (l2_offset == extent->l2_cache_offsets[i]) { @@ -714,28 +719,29 @@ static uint64_t get_cluster_offset(BlockDriverState *bs, l2_table, extent->l2_size * sizeof(uint32_t) ) != extent->l2_size * sizeof(uint32_t)) { - return 0; + return -1; } extent->l2_cache_offsets[min_index] = l2_offset; extent->l2_cache_counts[min_index] = 1; found: l2_index = ((offset >> 9) / extent->cluster_sectors) % extent->l2_size; - cluster_offset = le32_to_cpu(l2_table[l2_index]); + *cluster_offset = le32_to_cpu(l2_table[l2_index]); - if (!cluster_offset) { - if (!allocate) - return 0; + if (!*cluster_offset) { + if (!allocate) { + return -1; + } // Avoid the L2 tables update for the images that have snapshots. - cluster_offset = bdrv_getlength(extent->file); + *cluster_offset = bdrv_getlength(extent->file); bdrv_truncate( extent->file, - cluster_offset + (extent->cluster_sectors << 9) + *cluster_offset + (extent->cluster_sectors << 9) ); - cluster_offset >>= 9; - tmp = cpu_to_le32(cluster_offset); + *cluster_offset >>= 9; + tmp = cpu_to_le32(*cluster_offset); l2_table[l2_index] = tmp; /* First of all we write grain itself, to avoid race condition @@ -744,8 +750,8 @@ static uint64_t get_cluster_offset(BlockDriverState *bs, * or inappropriate VM shutdown. */ if (get_whole_cluster( - bs, extent, cluster_offset, offset, allocate) == -1) - return 0; + bs, extent, *cluster_offset, offset, allocate) == -1) + return -1; if (m_data) { m_data->offset = tmp; @@ -755,8 +761,8 @@ static uint64_t get_cluster_offset(BlockDriverState *bs, m_data->valid = 1; } } - cluster_offset <<= 9; - return cluster_offset; + *cluster_offset <<= 9; + return 0; } static VmdkExtent *find_extent(BDRVVmdkState *s, @@ -780,7 +786,6 @@ static int vmdk_is_allocated(BlockDriverState *bs, int64_t sector_num, int nb_sectors, int *pnum) { BDRVVmdkState *s = bs->opaque; - int64_t index_in_cluster, n, ret; uint64_t offset; VmdkExtent *extent; @@ -789,15 +794,13 @@ static int vmdk_is_allocated(BlockDriverState *bs, int64_t sector_num, if (!extent) { return 0; } - if (extent->flat) { - n = extent->end_sector - sector_num; - ret = 1; - } else { - offset = get_cluster_offset(bs, extent, NULL, sector_num * 512, 0); - index_in_cluster = sector_num % extent->cluster_sectors; - n = extent->cluster_sectors - index_in_cluster; - ret = offset ? 1 : 0; - } + ret = get_cluster_offset(bs, extent, NULL, + sector_num * 512, 0, &offset); + /* get_cluster_offset returning 0 means success */ + ret = !ret; + + index_in_cluster = sector_num % extent->cluster_sectors; + n = extent->cluster_sectors - index_in_cluster; if (n > nb_sectors) n = nb_sectors; *pnum = n; @@ -818,14 +821,15 @@ static int vmdk_read(BlockDriverState *bs, int64_t sector_num, if (!extent) { return -EIO; } - cluster_offset = get_cluster_offset( - bs, extent, NULL, sector_num << 9, 0); + ret = get_cluster_offset( + bs, extent, NULL, + sector_num << 9, 0, &cluster_offset); index_in_cluster = sector_num % extent->cluster_sectors; n = extent->cluster_sectors - index_in_cluster; if (n > nb_sectors) n = nb_sectors; - if (!cluster_offset) { - // try to read from parent image, if exist + if (ret) { + /* if not allocated, try to read from parent image, if exist */ if (bs->backing_hd) { if (!vmdk_is_cid_valid(bs)) return -1; @@ -851,7 +855,7 @@ static int vmdk_write(BlockDriverState *bs, int64_t sector_num, { BDRVVmdkState *s = bs->opaque; VmdkExtent *extent = NULL; - int n; + int n, ret; int64_t index_in_cluster; uint64_t cluster_offset; VmdkMetaData m_data; @@ -869,13 +873,14 @@ static int vmdk_write(BlockDriverState *bs, int64_t sector_num, if (!extent) { return -EIO; } - cluster_offset = get_cluster_offset( + ret = get_cluster_offset( bs, extent, &m_data, - sector_num << 9, 1); - if (!cluster_offset) { - return -1; + sector_num << 9, 1, + &cluster_offset); + if (ret) { + return -EINVAL; } index_in_cluster = sector_num % extent->cluster_sectors; n = extent->cluster_sectors - index_in_cluster; From 7fa60fa3778f1c7336d4e91d4055773538609539 Mon Sep 17 00:00:00 2001 From: Fam Zheng Date: Tue, 19 Jul 2011 08:38:22 +0800 Subject: [PATCH 152/209] VMDK: open/read/write for monolithicFlat image Parse vmdk decriptor file and open mono flat image. Read/write the flat extent. Signed-off-by: Fam Zheng Reviewed-by: Stefan Hajnoczi Signed-off-by: Kevin Wolf --- block/vmdk.c | 171 +++++++++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 158 insertions(+), 13 deletions(-) diff --git a/block/vmdk.c b/block/vmdk.c index f637d98d47..e1fb962b53 100644 --- a/block/vmdk.c +++ b/block/vmdk.c @@ -65,6 +65,7 @@ typedef struct VmdkExtent { bool flat; int64_t sectors; int64_t end_sector; + int64_t flat_start_offset; int64_t l1_table_offset; int64_t l1_backup_table_offset; uint32_t *l1_table; @@ -407,9 +408,10 @@ fail: static int vmdk_parent_open(BlockDriverState *bs) { char *p_name; - char desc[DESC_SIZE]; + char desc[DESC_SIZE + 1]; BDRVVmdkState *s = bs->opaque; + desc[DESC_SIZE] = '\0'; if (bdrv_pread(bs->file, s->desc_offset, desc, DESC_SIZE) != DESC_SIZE) { return -1; } @@ -584,6 +586,144 @@ static int vmdk_open_vmdk4(BlockDriverState *bs, int flags) return ret; } +/* find an option value out of descriptor file */ +static int vmdk_parse_description(const char *desc, const char *opt_name, + char *buf, int buf_size) +{ + char *opt_pos, *opt_end; + const char *end = desc + strlen(desc); + + opt_pos = strstr(desc, opt_name); + if (!opt_pos) { + return -1; + } + /* Skip "=\"" following opt_name */ + opt_pos += strlen(opt_name) + 2; + if (opt_pos >= end) { + return -1; + } + opt_end = opt_pos; + while (opt_end < end && *opt_end != '"') { + opt_end++; + } + if (opt_end == end || buf_size < opt_end - opt_pos + 1) { + return -1; + } + pstrcpy(buf, opt_end - opt_pos + 1, opt_pos); + return 0; +} + +static int vmdk_parse_extents(const char *desc, BlockDriverState *bs, + const char *desc_file_path) +{ + int ret; + char access[11]; + char type[11]; + char fname[512]; + const char *p = desc; + int64_t sectors = 0; + int64_t flat_offset; + + while (*p) { + /* parse extent line: + * RW [size in sectors] FLAT "file-name.vmdk" OFFSET + * or + * RW [size in sectors] SPARSE "file-name.vmdk" + */ + flat_offset = -1; + ret = sscanf(p, "%10s %" SCNd64 " %10s %511s %" SCNd64, + access, §ors, type, fname, &flat_offset); + if (ret < 4 || strcmp(access, "RW")) { + goto next_line; + } else if (!strcmp(type, "FLAT")) { + if (ret != 5 || flat_offset < 0) { + return -EINVAL; + } + } else if (ret != 4) { + return -EINVAL; + } + + /* trim the quotation marks around */ + if (fname[0] == '"') { + memmove(fname, fname + 1, strlen(fname)); + if (strlen(fname) <= 1 || fname[strlen(fname) - 1] != '"') { + return -EINVAL; + } + fname[strlen(fname) - 1] = '\0'; + } + if (sectors <= 0 || + (strcmp(type, "FLAT") && strcmp(type, "SPARSE")) || + (strcmp(access, "RW"))) { + goto next_line; + } + + /* save to extents array */ + if (!strcmp(type, "FLAT")) { + /* FLAT extent */ + char extent_path[PATH_MAX]; + BlockDriverState *extent_file; + VmdkExtent *extent; + + path_combine(extent_path, sizeof(extent_path), + desc_file_path, fname); + ret = bdrv_file_open(&extent_file, extent_path, bs->open_flags); + if (ret) { + return ret; + } + extent = vmdk_add_extent(bs, extent_file, true, sectors, + 0, 0, 0, 0, sectors); + extent->flat_start_offset = flat_offset; + } else { + /* SPARSE extent, not supported for now */ + fprintf(stderr, + "VMDK: Not supported extent type \"%s\""".\n", type); + return -ENOTSUP; + } +next_line: + /* move to next line */ + while (*p && *p != '\n') { + p++; + } + p++; + } + return 0; +} + +static int vmdk_open_desc_file(BlockDriverState *bs, int flags) +{ + int ret; + char buf[2048]; + char ct[128]; + BDRVVmdkState *s = bs->opaque; + + ret = bdrv_pread(bs->file, 0, buf, sizeof(buf)); + if (ret < 0) { + return ret; + } + buf[2047] = '\0'; + if (vmdk_parse_description(buf, "createType", ct, sizeof(ct))) { + return -EINVAL; + } + if (strcmp(ct, "monolithicFlat")) { + fprintf(stderr, + "VMDK: Not supported image type \"%s\""".\n", ct); + return -ENOTSUP; + } + s->desc_offset = 0; + ret = vmdk_parse_extents(buf, bs, bs->file->filename); + if (ret) { + return ret; + } + + /* try to open parent images, if exist */ + if (vmdk_parent_open(bs)) { + qemu_free(s->extents); + return -EINVAL; + } + s->parent_cid = vmdk_read_cid(bs, 1); + return 0; +} + static int vmdk_open(BlockDriverState *bs, int flags) { uint32_t magic; @@ -598,7 +738,7 @@ static int vmdk_open(BlockDriverState *bs, int flags) } else if (magic == VMDK4_MAGIC) { return vmdk_open_vmdk4(bs, flags); } else { - return -EINVAL; + return vmdk_open_desc_file(bs, flags); } } @@ -679,7 +819,7 @@ static int get_cluster_offset(BlockDriverState *bs, if (m_data) m_data->valid = 0; if (extent->flat) { - *cluster_offset = 0; + *cluster_offset = extent->flat_start_offset; return 0; } @@ -832,16 +972,20 @@ static int vmdk_read(BlockDriverState *bs, int64_t sector_num, /* if not allocated, try to read from parent image, if exist */ if (bs->backing_hd) { if (!vmdk_is_cid_valid(bs)) - return -1; + return -EINVAL; ret = bdrv_read(bs->backing_hd, sector_num, buf, n); if (ret < 0) - return -1; + return ret; } else { memset(buf, 0, 512 * n); } } else { - if(bdrv_pread(bs->file, cluster_offset + index_in_cluster * 512, buf, n * 512) != n * 512) - return -1; + ret = bdrv_pread(extent->file, + cluster_offset + index_in_cluster * 512, + buf, n * 512); + if (ret < 0) { + return ret; + } } nb_sectors -= n; sector_num += n; @@ -865,7 +1009,7 @@ static int vmdk_write(BlockDriverState *bs, int64_t sector_num, "(VMDK) Wrong offset: sector_num=0x%" PRIx64 " total_sectors=0x%" PRIx64 "\n", sector_num, bs->total_sectors); - return -1; + return -EIO; } while (nb_sectors > 0) { @@ -888,16 +1032,17 @@ static int vmdk_write(BlockDriverState *bs, int64_t sector_num, n = nb_sectors; } - if (bdrv_pwrite(bs->file, + ret = bdrv_pwrite(extent->file, cluster_offset + index_in_cluster * 512, - buf, n * 512) - != n * 512) { - return -1; + buf, + n * 512); + if (ret < 0) { + return ret; } if (m_data.valid) { /* update L2 tables */ if (vmdk_L2update(extent, &m_data) == -1) { - return -1; + return -EIO; } } nb_sectors -= n; From f66fd6c383edc137fa618c250ea40c1936a52f1c Mon Sep 17 00:00:00 2001 From: Fam Zheng Date: Tue, 19 Jul 2011 08:45:23 +0800 Subject: [PATCH 153/209] VMDK: create different subformats Add create option 'format', with enums: monolithicSparse monolithicFlat twoGbMaxExtentSparse twoGbMaxExtentFlat Each creates a subformat image file. The default is monolithicSparse. Signed-off-by: Fam Zheng Reviewed-by: Stefan Hajnoczi Signed-off-by: Kevin Wolf --- block/vmdk.c | 503 ++++++++++++++++++++++++++++----------------------- block_int.h | 1 + 2 files changed, 275 insertions(+), 229 deletions(-) diff --git a/block/vmdk.c b/block/vmdk.c index e1fb962b53..b53c5f5eb3 100644 --- a/block/vmdk.c +++ b/block/vmdk.c @@ -156,8 +156,9 @@ static int vmdk_probe(const uint8_t *buf, int buf_size, const char *filename) #define CHECK_CID 1 #define SECTOR_SIZE 512 -#define DESC_SIZE 20*SECTOR_SIZE // 20 sectors of 512 bytes each -#define HEADER_SIZE 512 // first sector of 512 bytes +#define DESC_SIZE (20 * SECTOR_SIZE) /* 20 sectors of 512 bytes each */ +#define BUF_SIZE 4096 +#define HEADER_SIZE 512 /* first sector of 512 bytes */ static void vmdk_free_extents(BlockDriverState *bs) { @@ -243,168 +244,6 @@ static int vmdk_is_cid_valid(BlockDriverState *bs) return 1; } -static int vmdk_snapshot_create(const char *filename, const char *backing_file) -{ - int snp_fd, p_fd; - int ret; - uint32_t p_cid; - char *p_name, *gd_buf, *rgd_buf; - const char *real_filename, *temp_str; - VMDK4Header header; - uint32_t gde_entries, gd_size; - int64_t gd_offset, rgd_offset, capacity, gt_size; - char p_desc[DESC_SIZE], s_desc[DESC_SIZE], hdr[HEADER_SIZE]; - static const char desc_template[] = - "# Disk DescriptorFile\n" - "version=1\n" - "CID=%x\n" - "parentCID=%x\n" - "createType=\"monolithicSparse\"\n" - "parentFileNameHint=\"%s\"\n" - "\n" - "# Extent description\n" - "RW %u SPARSE \"%s\"\n" - "\n" - "# The Disk Data Base \n" - "#DDB\n" - "\n"; - - snp_fd = open(filename, O_RDWR | O_CREAT | O_TRUNC | O_BINARY | O_LARGEFILE, 0644); - if (snp_fd < 0) - return -errno; - p_fd = open(backing_file, O_RDONLY | O_BINARY | O_LARGEFILE); - if (p_fd < 0) { - close(snp_fd); - return -errno; - } - - /* read the header */ - if (lseek(p_fd, 0x0, SEEK_SET) == -1) { - ret = -errno; - goto fail; - } - if (read(p_fd, hdr, HEADER_SIZE) != HEADER_SIZE) { - ret = -errno; - goto fail; - } - - /* write the header */ - if (lseek(snp_fd, 0x0, SEEK_SET) == -1) { - ret = -errno; - goto fail; - } - if (write(snp_fd, hdr, HEADER_SIZE) == -1) { - ret = -errno; - goto fail; - } - - memset(&header, 0, sizeof(header)); - memcpy(&header,&hdr[4], sizeof(header)); // skip the VMDK4_MAGIC - - if (ftruncate(snp_fd, header.grain_offset << 9)) { - ret = -errno; - goto fail; - } - /* the descriptor offset = 0x200 */ - if (lseek(p_fd, 0x200, SEEK_SET) == -1) { - ret = -errno; - goto fail; - } - if (read(p_fd, p_desc, DESC_SIZE) != DESC_SIZE) { - ret = -errno; - goto fail; - } - - if ((p_name = strstr(p_desc,"CID")) != NULL) { - p_name += sizeof("CID"); - sscanf(p_name,"%x",&p_cid); - } - - real_filename = filename; - if ((temp_str = strrchr(real_filename, '\\')) != NULL) - real_filename = temp_str + 1; - if ((temp_str = strrchr(real_filename, '/')) != NULL) - real_filename = temp_str + 1; - if ((temp_str = strrchr(real_filename, ':')) != NULL) - real_filename = temp_str + 1; - - snprintf(s_desc, sizeof(s_desc), desc_template, p_cid, p_cid, backing_file, - (uint32_t)header.capacity, real_filename); - - /* write the descriptor */ - if (lseek(snp_fd, 0x200, SEEK_SET) == -1) { - ret = -errno; - goto fail; - } - if (write(snp_fd, s_desc, strlen(s_desc)) == -1) { - ret = -errno; - goto fail; - } - - gd_offset = header.gd_offset * SECTOR_SIZE; // offset of GD table - rgd_offset = header.rgd_offset * SECTOR_SIZE; // offset of RGD table - capacity = header.capacity * SECTOR_SIZE; // Extent size - /* - * Each GDE span 32M disk, means: - * 512 GTE per GT, each GTE points to grain - */ - gt_size = (int64_t)header.num_gtes_per_gte * header.granularity * SECTOR_SIZE; - if (!gt_size) { - ret = -EINVAL; - goto fail; - } - gde_entries = (uint32_t)(capacity / gt_size); // number of gde/rgde - gd_size = gde_entries * sizeof(uint32_t); - - /* write RGD */ - rgd_buf = qemu_malloc(gd_size); - if (lseek(p_fd, rgd_offset, SEEK_SET) == -1) { - ret = -errno; - goto fail_rgd; - } - if (read(p_fd, rgd_buf, gd_size) != gd_size) { - ret = -errno; - goto fail_rgd; - } - if (lseek(snp_fd, rgd_offset, SEEK_SET) == -1) { - ret = -errno; - goto fail_rgd; - } - if (write(snp_fd, rgd_buf, gd_size) == -1) { - ret = -errno; - goto fail_rgd; - } - - /* write GD */ - gd_buf = qemu_malloc(gd_size); - if (lseek(p_fd, gd_offset, SEEK_SET) == -1) { - ret = -errno; - goto fail_gd; - } - if (read(p_fd, gd_buf, gd_size) != gd_size) { - ret = -errno; - goto fail_gd; - } - if (lseek(snp_fd, gd_offset, SEEK_SET) == -1) { - ret = -errno; - goto fail_gd; - } - if (write(snp_fd, gd_buf, gd_size) == -1) { - ret = -errno; - goto fail_gd; - } - ret = 0; - -fail_gd: - qemu_free(gd_buf); -fail_rgd: - qemu_free(rgd_buf); -fail: - close(p_fd); - close(snp_fd); - return ret; -} - static int vmdk_parent_open(BlockDriverState *bs) { char *p_name; @@ -1058,68 +897,40 @@ static int vmdk_write(BlockDriverState *bs, int64_t sector_num, return 0; } -static int vmdk_create(const char *filename, QEMUOptionParameter *options) + +static int vmdk_create_extent(const char *filename, int64_t filesize, bool flat) { - int fd, i; + int ret, i; + int fd = 0; VMDK4Header header; uint32_t tmp, magic, grains, gd_size, gt_size, gt_count; - static const char desc_template[] = - "# Disk DescriptorFile\n" - "version=1\n" - "CID=%x\n" - "parentCID=ffffffff\n" - "createType=\"monolithicSparse\"\n" - "\n" - "# Extent description\n" - "RW %" PRId64 " SPARSE \"%s\"\n" - "\n" - "# The Disk Data Base \n" - "#DDB\n" - "\n" - "ddb.virtualHWVersion = \"%d\"\n" - "ddb.geometry.cylinders = \"%" PRId64 "\"\n" - "ddb.geometry.heads = \"16\"\n" - "ddb.geometry.sectors = \"63\"\n" - "ddb.adapterType = \"ide\"\n"; - char desc[1024]; - const char *real_filename, *temp_str; - int64_t total_size = 0; - const char *backing_file = NULL; - int flags = 0; - int ret; - // Read out options - while (options && options->name) { - if (!strcmp(options->name, BLOCK_OPT_SIZE)) { - total_size = options->value.n / 512; - } else if (!strcmp(options->name, BLOCK_OPT_BACKING_FILE)) { - backing_file = options->value.s; - } else if (!strcmp(options->name, BLOCK_OPT_COMPAT6)) { - flags |= options->value.n ? BLOCK_FLAG_COMPAT6: 0; - } - options++; - } - - /* XXX: add support for backing file */ - if (backing_file) { - return vmdk_snapshot_create(filename, backing_file); - } - - fd = open(filename, O_WRONLY | O_CREAT | O_TRUNC | O_BINARY | O_LARGEFILE, - 0644); - if (fd < 0) + fd = open( + filename, + O_WRONLY | O_CREAT | O_TRUNC | O_BINARY | O_LARGEFILE, + 0644); + if (fd < 0) { return -errno; + } + if (flat) { + ret = ftruncate(fd, filesize); + if (ret < 0) { + ret = -errno; + } + goto exit; + } magic = cpu_to_be32(VMDK4_MAGIC); memset(&header, 0, sizeof(header)); header.version = 1; header.flags = 3; /* ?? */ - header.capacity = total_size; + header.capacity = filesize / 512; header.granularity = 128; header.num_gtes_per_gte = 512; - grains = (total_size + header.granularity - 1) / header.granularity; + grains = (filesize / 512 + header.granularity - 1) / header.granularity; gt_size = ((header.num_gtes_per_gte * sizeof(uint32_t)) + 511) >> 9; - gt_count = (grains + header.num_gtes_per_gte - 1) / header.num_gtes_per_gte; + gt_count = + (grains + header.num_gtes_per_gte - 1) / header.num_gtes_per_gte; gd_size = (gt_count * sizeof(uint32_t) + 511) >> 9; header.desc_offset = 1; @@ -1130,7 +941,6 @@ static int vmdk_create(const char *filename, QEMUOptionParameter *options) ((header.gd_offset + gd_size + (gt_size * gt_count) + header.granularity - 1) / header.granularity) * header.granularity; - /* swap endianness for all header fields */ header.version = cpu_to_le32(header.version); header.flags = cpu_to_le32(header.flags); @@ -1188,27 +998,255 @@ static int vmdk_create(const char *filename, QEMUOptionParameter *options) } } - /* compose the descriptor */ - real_filename = filename; - if ((temp_str = strrchr(real_filename, '\\')) != NULL) - real_filename = temp_str + 1; - if ((temp_str = strrchr(real_filename, '/')) != NULL) - real_filename = temp_str + 1; - if ((temp_str = strrchr(real_filename, ':')) != NULL) - real_filename = temp_str + 1; - snprintf(desc, sizeof(desc), desc_template, (unsigned int)time(NULL), - total_size, real_filename, - (flags & BLOCK_FLAG_COMPAT6 ? 6 : 4), - total_size / (int64_t)(63 * 16)); + ret = 0; + exit: + close(fd); + return ret; +} - /* write the descriptor */ - lseek(fd, le64_to_cpu(header.desc_offset) << 9, SEEK_SET); +static int filename_decompose(const char *filename, char *path, char *prefix, + char *postfix, size_t buf_len) +{ + const char *p, *q; + + if (filename == NULL || !strlen(filename)) { + fprintf(stderr, "Vmdk: no filename provided.\n"); + return -1; + } + p = strrchr(filename, '/'); + if (p == NULL) { + p = strrchr(filename, '\\'); + } + if (p == NULL) { + p = strrchr(filename, ':'); + } + if (p != NULL) { + p++; + if (p - filename >= buf_len) { + return -1; + } + pstrcpy(path, p - filename + 1, filename); + } else { + p = filename; + path[0] = '\0'; + } + q = strrchr(p, '.'); + if (q == NULL) { + pstrcpy(prefix, buf_len, p); + postfix[0] = '\0'; + } else { + if (q - p >= buf_len) { + return -1; + } + pstrcpy(prefix, q - p + 1, p); + pstrcpy(postfix, buf_len, q); + } + return 0; +} + +static int relative_path(char *dest, int dest_size, + const char *base, const char *target) +{ + int i = 0; + int n = 0; + const char *p, *q; +#ifdef _WIN32 + const char *sep = "\\"; +#else + const char *sep = "/"; +#endif + + if (!(dest && base && target)) { + return -1; + } + if (path_is_absolute(target)) { + dest[dest_size - 1] = '\0'; + strncpy(dest, target, dest_size - 1); + return 0; + } + while (base[i] == target[i]) { + i++; + } + p = &base[i]; + q = &target[i]; + while (*p) { + if (*p == *sep) { + n++; + } + p++; + } + dest[0] = '\0'; + for (; n; n--) { + pstrcat(dest, dest_size, ".."); + pstrcat(dest, dest_size, sep); + } + pstrcat(dest, dest_size, q); + return 0; +} + +static int vmdk_create(const char *filename, QEMUOptionParameter *options) +{ + int fd, idx = 0; + char desc[BUF_SIZE]; + int64_t total_size = 0, filesize; + const char *backing_file = NULL; + const char *fmt = NULL; + int flags = 0; + int ret = 0; + bool flat, split; + char ext_desc_lines[BUF_SIZE] = ""; + char path[PATH_MAX], prefix[PATH_MAX], postfix[PATH_MAX]; + const int64_t split_size = 0x80000000; /* VMDK has constant split size */ + const char *desc_extent_line; + char parent_desc_line[BUF_SIZE] = ""; + uint32_t parent_cid = 0xffffffff; + const char desc_template[] = + "# Disk DescriptorFile\n" + "version=1\n" + "CID=%x\n" + "parentCID=%x\n" + "createType=\"%s\"\n" + "%s" + "\n" + "# Extent description\n" + "%s" + "\n" + "# The Disk Data Base\n" + "#DDB\n" + "\n" + "ddb.virtualHWVersion = \"%d\"\n" + "ddb.geometry.cylinders = \"%" PRId64 "\"\n" + "ddb.geometry.heads = \"16\"\n" + "ddb.geometry.sectors = \"63\"\n" + "ddb.adapterType = \"ide\"\n"; + + if (filename_decompose(filename, path, prefix, postfix, PATH_MAX)) { + return -EINVAL; + } + /* Read out options */ + while (options && options->name) { + if (!strcmp(options->name, BLOCK_OPT_SIZE)) { + total_size = options->value.n; + } else if (!strcmp(options->name, BLOCK_OPT_BACKING_FILE)) { + backing_file = options->value.s; + } else if (!strcmp(options->name, BLOCK_OPT_COMPAT6)) { + flags |= options->value.n ? BLOCK_FLAG_COMPAT6 : 0; + } else if (!strcmp(options->name, BLOCK_OPT_SUBFMT)) { + fmt = options->value.s; + } + options++; + } + if (!fmt) { + /* Default format to monolithicSparse */ + fmt = "monolithicSparse"; + } else if (strcmp(fmt, "monolithicFlat") && + strcmp(fmt, "monolithicSparse") && + strcmp(fmt, "twoGbMaxExtentSparse") && + strcmp(fmt, "twoGbMaxExtentFlat")) { + fprintf(stderr, "VMDK: Unknown subformat: %s\n", fmt); + return -EINVAL; + } + split = !(strcmp(fmt, "twoGbMaxExtentFlat") && + strcmp(fmt, "twoGbMaxExtentSparse")); + flat = !(strcmp(fmt, "monolithicFlat") && + strcmp(fmt, "twoGbMaxExtentFlat")); + if (flat) { + desc_extent_line = "RW %lld FLAT \"%s\" 0\n"; + } else { + desc_extent_line = "RW %lld SPARSE \"%s\"\n"; + } + if (flat && backing_file) { + /* not supporting backing file for flat image */ + return -ENOTSUP; + } + if (backing_file) { + char parent_filename[PATH_MAX]; + BlockDriverState *bs = bdrv_new(""); + ret = bdrv_open(bs, backing_file, 0, NULL); + if (ret != 0) { + bdrv_delete(bs); + return ret; + } + if (strcmp(bs->drv->format_name, "vmdk")) { + bdrv_delete(bs); + return -EINVAL; + } + filesize = bdrv_getlength(bs); + parent_cid = vmdk_read_cid(bs, 0); + bdrv_delete(bs); + relative_path(parent_filename, sizeof(parent_filename), + filename, backing_file); + snprintf(parent_desc_line, sizeof(parent_desc_line), + "parentFileNameHint=\"%s\"", parent_filename); + } + + /* Create extents */ + filesize = total_size; + while (filesize > 0) { + char desc_line[BUF_SIZE]; + char ext_filename[PATH_MAX]; + char desc_filename[PATH_MAX]; + int64_t size = filesize; + + if (split && size > split_size) { + size = split_size; + } + if (split) { + snprintf(desc_filename, sizeof(desc_filename), "%s-%c%03d%s", + prefix, flat ? 'f' : 's', ++idx, postfix); + } else if (flat) { + snprintf(desc_filename, sizeof(desc_filename), "%s-flat%s", + prefix, postfix); + } else { + snprintf(desc_filename, sizeof(desc_filename), "%s%s", + prefix, postfix); + } + snprintf(ext_filename, sizeof(ext_filename), "%s%s", + path, desc_filename); + + if (vmdk_create_extent(ext_filename, size, flat)) { + return -EINVAL; + } + filesize -= size; + + /* Format description line */ + snprintf(desc_line, sizeof(desc_line), + desc_extent_line, size / 512, desc_filename); + pstrcat(ext_desc_lines, sizeof(ext_desc_lines), desc_line); + } + /* generate descriptor file */ + snprintf(desc, sizeof(desc), desc_template, + (unsigned int)time(NULL), + parent_cid, + fmt, + parent_desc_line, + ext_desc_lines, + (flags & BLOCK_FLAG_COMPAT6 ? 6 : 4), + total_size / (int64_t)(63 * 16 * 512)); + if (split || flat) { + fd = open( + filename, + O_WRONLY | O_CREAT | O_TRUNC | O_BINARY | O_LARGEFILE, + 0644); + } else { + fd = open( + filename, + O_WRONLY | O_BINARY | O_LARGEFILE, + 0644); + } + if (fd < 0) { + return -errno; + } + /* the descriptor offset = 0x200 */ + if (!split && !flat && 0x200 != lseek(fd, 0x200, SEEK_SET)) { + ret = -errno; + goto exit; + } ret = qemu_write_full(fd, desc, strlen(desc)); if (ret != strlen(desc)) { ret = -errno; goto exit; } - ret = 0; exit: close(fd); @@ -1252,6 +1290,13 @@ static QEMUOptionParameter vmdk_create_options[] = { .type = OPT_FLAG, .help = "VMDK version 6 image" }, + { + .name = BLOCK_OPT_SUBFMT, + .type = OPT_STRING, + .help = + "VMDK flat extent format, can be one of " + "{monolithicSparse (default) | monolithicFlat | twoGbMaxExtentSparse | twoGbMaxExtentFlat} " + }, { NULL } }; diff --git a/block_int.h b/block_int.h index 1e265d274d..8a7b6cbb42 100644 --- a/block_int.h +++ b/block_int.h @@ -39,6 +39,7 @@ #define BLOCK_OPT_CLUSTER_SIZE "cluster_size" #define BLOCK_OPT_TABLE_SIZE "table_size" #define BLOCK_OPT_PREALLOC "preallocation" +#define BLOCK_OPT_SUBFMT "subformat" typedef struct AIOPool { void (*cancel)(BlockDriverAIOCB *acb); From ae261c86aaed62e7acddafab8262a2bf286d40b7 Mon Sep 17 00:00:00 2001 From: Fam Zheng Date: Tue, 12 Jul 2011 19:56:38 +0800 Subject: [PATCH 154/209] VMDK: fix coding style Conform coding style in vmdk.c to pass scripts/checkpatch.pl checks. Signed-off-by: Fam Zheng Reviewed-by: Stefan Hajnoczi Signed-off-by: Kevin Wolf --- block/vmdk.c | 76 +++++++++++++++++++++++++++++++--------------------- 1 file changed, 46 insertions(+), 30 deletions(-) diff --git a/block/vmdk.c b/block/vmdk.c index b53c5f5eb3..de08d0ce2d 100644 --- a/block/vmdk.c +++ b/block/vmdk.c @@ -102,8 +102,9 @@ static int vmdk_probe(const uint8_t *buf, int buf_size, const char *filename) { uint32_t magic; - if (buf_size < 4) + if (buf_size < 4) { return 0; + } magic = be32_to_cpu(*(uint32_t *)buf); if (magic == VMDK3_MAGIC || magic == VMDK4_MAGIC) { @@ -193,9 +194,10 @@ static uint32_t vmdk_read_cid(BlockDriverState *bs, int parent) cid_str_size = sizeof("CID"); } - if ((p_name = strstr(desc,cid_str)) != NULL) { + p_name = strstr(desc, cid_str); + if (p_name != NULL) { p_name += cid_str_size; - sscanf(p_name,"%x",&cid); + sscanf(p_name, "%x", &cid); } return cid; @@ -212,9 +214,10 @@ static int vmdk_write_cid(BlockDriverState *bs, uint32_t cid) return -EIO; } - tmp_str = strstr(desc,"parentCID"); + tmp_str = strstr(desc, "parentCID"); pstrcpy(tmp_desc, sizeof(tmp_desc), tmp_str); - if ((p_name = strstr(desc,"CID")) != NULL) { + p_name = strstr(desc, "CID"); + if (p_name != NULL) { p_name += sizeof("CID"); snprintf(p_name, sizeof(desc) - (p_name - desc), "%x\n", cid); pstrcat(desc, sizeof(desc), tmp_desc); @@ -234,13 +237,14 @@ static int vmdk_is_cid_valid(BlockDriverState *bs) uint32_t cur_pcid; if (p_bs) { - cur_pcid = vmdk_read_cid(p_bs,0); - if (s->parent_cid != cur_pcid) - // CID not valid + cur_pcid = vmdk_read_cid(p_bs, 0); + if (s->parent_cid != cur_pcid) { + /* CID not valid */ return 0; + } } #endif - // CID valid + /* CID valid */ return 1; } @@ -255,14 +259,18 @@ static int vmdk_parent_open(BlockDriverState *bs) return -1; } - if ((p_name = strstr(desc,"parentFileNameHint")) != NULL) { + p_name = strstr(desc, "parentFileNameHint"); + if (p_name != NULL) { char *end_name; p_name += sizeof("parentFileNameHint") + 1; - if ((end_name = strchr(p_name,'\"')) == NULL) + end_name = strchr(p_name, '\"'); + if (end_name == NULL) { return -1; - if ((end_name - p_name) > sizeof (bs->backing_file) - 1) + } + if ((end_name - p_name) > sizeof(bs->backing_file) - 1) { return -1; + } pstrcpy(bs->backing_file, end_name - p_name + 1, p_name); } @@ -595,8 +603,9 @@ static int get_whole_cluster(BlockDriverState *bs, if (bs->backing_hd) { int ret; - if (!vmdk_is_cid_valid(bs)) + if (!vmdk_is_cid_valid(bs)) { return -1; + } /* floor offset to cluster */ offset -= offset % (extent->cluster_sectors * 512); @@ -655,8 +664,9 @@ static int get_cluster_offset(BlockDriverState *bs, int min_index, i, j; uint32_t min_count, *l2_table, tmp = 0; - if (m_data) + if (m_data) { m_data->valid = 0; + } if (extent->flat) { *cluster_offset = extent->flat_start_offset; return 0; @@ -712,7 +722,7 @@ static int get_cluster_offset(BlockDriverState *bs, return -1; } - // Avoid the L2 tables update for the images that have snapshots. + /* Avoid the L2 tables update for the images that have snapshots. */ *cluster_offset = bdrv_getlength(extent->file); bdrv_truncate( extent->file, @@ -729,8 +739,9 @@ static int get_cluster_offset(BlockDriverState *bs, * or inappropriate VM shutdown. */ if (get_whole_cluster( - bs, extent, *cluster_offset, offset, allocate) == -1) + bs, extent, *cluster_offset, offset, allocate) == -1) { return -1; + } if (m_data) { m_data->offset = tmp; @@ -780,8 +791,9 @@ static int vmdk_is_allocated(BlockDriverState *bs, int64_t sector_num, index_in_cluster = sector_num % extent->cluster_sectors; n = extent->cluster_sectors - index_in_cluster; - if (n > nb_sectors) + if (n > nb_sectors) { n = nb_sectors; + } *pnum = n; return ret; } @@ -805,16 +817,19 @@ static int vmdk_read(BlockDriverState *bs, int64_t sector_num, sector_num << 9, 0, &cluster_offset); index_in_cluster = sector_num % extent->cluster_sectors; n = extent->cluster_sectors - index_in_cluster; - if (n > nb_sectors) + if (n > nb_sectors) { n = nb_sectors; + } if (ret) { /* if not allocated, try to read from parent image, if exist */ if (bs->backing_hd) { - if (!vmdk_is_cid_valid(bs)) + if (!vmdk_is_cid_valid(bs)) { return -EINVAL; + } ret = bdrv_read(bs->backing_hd, sector_num, buf, n); - if (ret < 0) + if (ret < 0) { return ret; + } } else { memset(buf, 0, 512 * n); } @@ -888,7 +903,8 @@ static int vmdk_write(BlockDriverState *bs, int64_t sector_num, sector_num += n; buf += n * 512; - // update CID on the first write every time the virtual disk is opened + /* update CID on the first write every time the virtual disk is + * opened */ if (!s->cid_updated) { vmdk_write_cid(bs, time(NULL)); s->cid_updated = true; @@ -1301,16 +1317,16 @@ static QEMUOptionParameter vmdk_create_options[] = { }; static BlockDriver bdrv_vmdk = { - .format_name = "vmdk", - .instance_size = sizeof(BDRVVmdkState), - .bdrv_probe = vmdk_probe, + .format_name = "vmdk", + .instance_size = sizeof(BDRVVmdkState), + .bdrv_probe = vmdk_probe, .bdrv_open = vmdk_open, - .bdrv_read = vmdk_read, - .bdrv_write = vmdk_write, - .bdrv_close = vmdk_close, - .bdrv_create = vmdk_create, - .bdrv_flush = vmdk_flush, - .bdrv_is_allocated = vmdk_is_allocated, + .bdrv_read = vmdk_read, + .bdrv_write = vmdk_write, + .bdrv_close = vmdk_close, + .bdrv_create = vmdk_create, + .bdrv_flush = vmdk_flush, + .bdrv_is_allocated = vmdk_is_allocated, .create_options = vmdk_create_options, }; From 4a1d5e1fded54358ddc4d8cbd53388ca7c93499b Mon Sep 17 00:00:00 2001 From: Fam Zheng Date: Tue, 12 Jul 2011 19:56:39 +0800 Subject: [PATCH 155/209] block: add bdrv_get_allocated_file_size() operation qemu-img.c wants to count allocated file size of image. Previously it counts a single bs->file by 'stat' or Window API. As VMDK introduces multiple file support, the operation becomes format specific with platform specific meanwhile. The functions are moved to block/raw-{posix,win32}.c and qemu-img.c calls bdrv_get_allocated_file_size to count the bs. And also added VMDK code to count his own extents. Signed-off-by: Fam Zheng Reviewed-by: Stefan Hajnoczi Signed-off-by: Kevin Wolf --- block.c | 19 +++++++++++++++++++ block.h | 1 + block/raw-posix.c | 21 +++++++++++++++++++++ block/raw-win32.c | 29 +++++++++++++++++++++++++++++ block/vmdk.c | 24 ++++++++++++++++++++++++ block_int.h | 1 + qemu-img.c | 31 +------------------------------ 7 files changed, 96 insertions(+), 30 deletions(-) diff --git a/block.c b/block.c index 24a25d569b..9549b9eff9 100644 --- a/block.c +++ b/block.c @@ -1146,6 +1146,25 @@ int bdrv_truncate(BlockDriverState *bs, int64_t offset) return ret; } +/** + * Length of a allocated file in bytes. Sparse files are counted by actual + * allocated space. Return < 0 if error or unknown. + */ +int64_t bdrv_get_allocated_file_size(BlockDriverState *bs) +{ + BlockDriver *drv = bs->drv; + if (!drv) { + return -ENOMEDIUM; + } + if (drv->bdrv_get_allocated_file_size) { + return drv->bdrv_get_allocated_file_size(bs); + } + if (bs->file) { + return bdrv_get_allocated_file_size(bs->file); + } + return -ENOTSUP; +} + /** * Length of a file in bytes. Return < 0 if error or unknown. */ diff --git a/block.h b/block.h index 859d1d9835..59cc410e3b 100644 --- a/block.h +++ b/block.h @@ -89,6 +89,7 @@ int bdrv_write_sync(BlockDriverState *bs, int64_t sector_num, const uint8_t *buf, int nb_sectors); int bdrv_truncate(BlockDriverState *bs, int64_t offset); int64_t bdrv_getlength(BlockDriverState *bs); +int64_t bdrv_get_allocated_file_size(BlockDriverState *bs); void bdrv_get_geometry(BlockDriverState *bs, uint64_t *nb_sectors_ptr); void bdrv_guess_geometry(BlockDriverState *bs, int *pcyls, int *pheads, int *psecs); int bdrv_commit(BlockDriverState *bs); diff --git a/block/raw-posix.c b/block/raw-posix.c index 34b64aa205..cd89c8312a 100644 --- a/block/raw-posix.c +++ b/block/raw-posix.c @@ -793,6 +793,17 @@ static int64_t raw_getlength(BlockDriverState *bs) } #endif +static int64_t raw_get_allocated_file_size(BlockDriverState *bs) +{ + struct stat st; + BDRVRawState *s = bs->opaque; + + if (fstat(s->fd, &st) < 0) { + return -errno; + } + return (int64_t)st.st_blocks * 512; +} + static int raw_create(const char *filename, QEMUOptionParameter *options) { int fd; @@ -888,6 +899,8 @@ static BlockDriver bdrv_file = { .bdrv_truncate = raw_truncate, .bdrv_getlength = raw_getlength, + .bdrv_get_allocated_file_size + = raw_get_allocated_file_size, .create_options = raw_create_options, }; @@ -1156,6 +1169,8 @@ static BlockDriver bdrv_host_device = { .bdrv_read = raw_read, .bdrv_write = raw_write, .bdrv_getlength = raw_getlength, + .bdrv_get_allocated_file_size + = raw_get_allocated_file_size, /* generic scsi device */ #ifdef __linux__ @@ -1277,6 +1292,8 @@ static BlockDriver bdrv_host_floppy = { .bdrv_read = raw_read, .bdrv_write = raw_write, .bdrv_getlength = raw_getlength, + .bdrv_get_allocated_file_size + = raw_get_allocated_file_size, /* removable device support */ .bdrv_is_inserted = floppy_is_inserted, @@ -1380,6 +1397,8 @@ static BlockDriver bdrv_host_cdrom = { .bdrv_read = raw_read, .bdrv_write = raw_write, .bdrv_getlength = raw_getlength, + .bdrv_get_allocated_file_size + = raw_get_allocated_file_size, /* removable device support */ .bdrv_is_inserted = cdrom_is_inserted, @@ -1503,6 +1522,8 @@ static BlockDriver bdrv_host_cdrom = { .bdrv_read = raw_read, .bdrv_write = raw_write, .bdrv_getlength = raw_getlength, + .bdrv_get_allocated_file_size + = raw_get_allocated_file_size, /* removable device support */ .bdrv_is_inserted = cdrom_is_inserted, diff --git a/block/raw-win32.c b/block/raw-win32.c index 56bd7195a1..91067e7595 100644 --- a/block/raw-win32.c +++ b/block/raw-win32.c @@ -213,6 +213,31 @@ static int64_t raw_getlength(BlockDriverState *bs) return l.QuadPart; } +static int64_t raw_get_allocated_file_size(BlockDriverState *bs) +{ + typedef DWORD (WINAPI * get_compressed_t)(const char *filename, + DWORD * high); + get_compressed_t get_compressed; + struct _stati64 st; + const char *filename = bs->filename; + /* WinNT support GetCompressedFileSize to determine allocate size */ + get_compressed = + (get_compressed_t) GetProcAddress(GetModuleHandle("kernel32"), + "GetCompressedFileSizeA"); + if (get_compressed) { + DWORD high, low; + low = get_compressed(filename, &high); + if (low != 0xFFFFFFFFlu || GetLastError() == NO_ERROR) { + return (((int64_t) high) << 32) + low; + } + } + + if (_stati64(filename, &st) < 0) { + return -1; + } + return st.st_size; +} + static int raw_create(const char *filename, QEMUOptionParameter *options) { int fd; @@ -257,6 +282,8 @@ static BlockDriver bdrv_file = { .bdrv_write = raw_write, .bdrv_truncate = raw_truncate, .bdrv_getlength = raw_getlength, + .bdrv_get_allocated_file_size + = raw_get_allocated_file_size, .create_options = raw_create_options, }; @@ -419,6 +446,8 @@ static BlockDriver bdrv_host_device = { .bdrv_read = raw_read, .bdrv_write = raw_write, .bdrv_getlength = raw_getlength, + .bdrv_get_allocated_file_size + = raw_get_allocated_file_size, }; static void bdrv_file_init(void) diff --git a/block/vmdk.c b/block/vmdk.c index de08d0ce2d..37478d2553 100644 --- a/block/vmdk.c +++ b/block/vmdk.c @@ -1289,6 +1289,29 @@ static int vmdk_flush(BlockDriverState *bs) return ret; } +static int64_t vmdk_get_allocated_file_size(BlockDriverState *bs) +{ + int i; + int64_t ret = 0; + int64_t r; + BDRVVmdkState *s = bs->opaque; + + ret = bdrv_get_allocated_file_size(bs->file); + if (ret < 0) { + return ret; + } + for (i = 0; i < s->num_extents; i++) { + if (s->extents[i].file == bs->file) { + continue; + } + r = bdrv_get_allocated_file_size(s->extents[i].file); + if (r < 0) { + return r; + } + ret += r; + } + return ret; +} static QEMUOptionParameter vmdk_create_options[] = { { @@ -1327,6 +1350,7 @@ static BlockDriver bdrv_vmdk = { .bdrv_create = vmdk_create, .bdrv_flush = vmdk_flush, .bdrv_is_allocated = vmdk_is_allocated, + .bdrv_get_allocated_file_size = vmdk_get_allocated_file_size, .create_options = vmdk_create_options, }; diff --git a/block_int.h b/block_int.h index 8a7b6cbb42..efb68038c4 100644 --- a/block_int.h +++ b/block_int.h @@ -86,6 +86,7 @@ struct BlockDriver { const char *protocol_name; int (*bdrv_truncate)(BlockDriverState *bs, int64_t offset); int64_t (*bdrv_getlength)(BlockDriverState *bs); + int64_t (*bdrv_get_allocated_file_size)(BlockDriverState *bs); int (*bdrv_write_compressed)(BlockDriverState *bs, int64_t sector_num, const uint8_t *buf, int nb_sectors); diff --git a/qemu-img.c b/qemu-img.c index 54137a4e92..b205e98dd2 100644 --- a/qemu-img.c +++ b/qemu-img.c @@ -1024,35 +1024,6 @@ out: return 0; } -#ifdef _WIN32 -static int64_t get_allocated_file_size(const char *filename) -{ - typedef DWORD (WINAPI * get_compressed_t)(const char *filename, DWORD *high); - get_compressed_t get_compressed; - struct _stati64 st; - - /* WinNT support GetCompressedFileSize to determine allocate size */ - get_compressed = (get_compressed_t) GetProcAddress(GetModuleHandle("kernel32"), "GetCompressedFileSizeA"); - if (get_compressed) { - DWORD high, low; - low = get_compressed(filename, &high); - if (low != 0xFFFFFFFFlu || GetLastError() == NO_ERROR) - return (((int64_t) high) << 32) + low; - } - - if (_stati64(filename, &st) < 0) - return -1; - return st.st_size; -} -#else -static int64_t get_allocated_file_size(const char *filename) -{ - struct stat st; - if (stat(filename, &st) < 0) - return -1; - return (int64_t)st.st_blocks * 512; -} -#endif static void dump_snapshots(BlockDriverState *bs) { @@ -1112,7 +1083,7 @@ static int img_info(int argc, char **argv) bdrv_get_format(bs, fmt_name, sizeof(fmt_name)); bdrv_get_geometry(bs, &total_sectors); get_human_readable_size(size_buf, sizeof(size_buf), total_sectors * 512); - allocated_size = get_allocated_file_size(filename); + allocated_size = bdrv_get_allocated_file_size(bs); if (allocated_size < 0) { snprintf(dsize_buf, sizeof(dsize_buf), "unavailable"); } else { From 93913dfd8acbaddc8ef3716cd7b8a2830c99cb19 Mon Sep 17 00:00:00 2001 From: Kevin Wolf Date: Tue, 19 Jul 2011 13:01:48 +0200 Subject: [PATCH 156/209] qcow2: Use Qcow2Cache in writeback mode during loadvm/savevm In snapshotting there is no guest involved, so we can safely use a writeback mode and do the flushes in the right place (i.e. at the very end). This improves the time that creating/restoring an internal snapshot takes with an image in writethrough mode. Signed-off-by: Kevin Wolf --- block/qcow2-cache.c | 12 ++++++++++++ block/qcow2-refcount.c | 38 +++++++++++++++++++++++++++----------- block/qcow2.h | 2 ++ 3 files changed, 41 insertions(+), 11 deletions(-) diff --git a/block/qcow2-cache.c b/block/qcow2-cache.c index 382473933c..84088477a4 100644 --- a/block/qcow2-cache.c +++ b/block/qcow2-cache.c @@ -312,3 +312,15 @@ found: c->entries[i].dirty = true; } +bool qcow2_cache_set_writethrough(BlockDriverState *bs, Qcow2Cache *c, + bool enable) +{ + bool old = c->writethrough; + + if (!old && enable) { + qcow2_cache_flush(bs, c); + } + + c->writethrough = enable; + return old; +} diff --git a/block/qcow2-refcount.c b/block/qcow2-refcount.c index ac95b88fe1..14b2f67f14 100644 --- a/block/qcow2-refcount.c +++ b/block/qcow2-refcount.c @@ -705,8 +705,15 @@ int qcow2_update_snapshot_refcount(BlockDriverState *bs, BDRVQcowState *s = bs->opaque; uint64_t *l1_table, *l2_table, l2_offset, offset, l1_size2, l1_allocated; int64_t old_offset, old_l2_offset; - int i, j, l1_modified, nb_csectors, refcount; + int i, j, l1_modified = 0, nb_csectors, refcount; int ret; + bool old_l2_writethrough, old_refcount_writethrough; + + /* Switch caches to writeback mode during update */ + old_l2_writethrough = + qcow2_cache_set_writethrough(bs, s->l2_table_cache, false); + old_refcount_writethrough = + qcow2_cache_set_writethrough(bs, s->refcount_block_cache, false); l2_table = NULL; l1_table = NULL; @@ -720,7 +727,11 @@ int qcow2_update_snapshot_refcount(BlockDriverState *bs, l1_allocated = 1; if (bdrv_pread(bs->file, l1_table_offset, l1_table, l1_size2) != l1_size2) + { + ret = -EIO; goto fail; + } + for(i = 0;i < l1_size; i++) be64_to_cpus(&l1_table[i]); } else { @@ -729,7 +740,6 @@ int qcow2_update_snapshot_refcount(BlockDriverState *bs, l1_allocated = 0; } - l1_modified = 0; for(i = 0; i < l1_size; i++) { l2_offset = l1_table[i]; if (l2_offset) { @@ -773,6 +783,7 @@ int qcow2_update_snapshot_refcount(BlockDriverState *bs, } if (refcount < 0) { + ret = -EIO; goto fail; } } @@ -803,6 +814,7 @@ int qcow2_update_snapshot_refcount(BlockDriverState *bs, refcount = get_refcount(bs, l2_offset >> s->cluster_bits); } if (refcount < 0) { + ret = -EIO; goto fail; } else if (refcount == 1) { l2_offset |= QCOW_OFLAG_COPIED; @@ -813,6 +825,18 @@ int qcow2_update_snapshot_refcount(BlockDriverState *bs, } } } + + ret = 0; +fail: + if (l2_table) { + qcow2_cache_put(bs, s->l2_table_cache, (void**) &l2_table); + } + + /* Enable writethrough cache mode again */ + qcow2_cache_set_writethrough(bs, s->l2_table_cache, old_l2_writethrough); + qcow2_cache_set_writethrough(bs, s->refcount_block_cache, + old_refcount_writethrough); + if (l1_modified) { for(i = 0; i < l1_size; i++) cpu_to_be64s(&l1_table[i]); @@ -824,15 +848,7 @@ int qcow2_update_snapshot_refcount(BlockDriverState *bs, } if (l1_allocated) qemu_free(l1_table); - return 0; - fail: - if (l2_table) { - qcow2_cache_put(bs, s->l2_table_cache, (void**) &l2_table); - } - - if (l1_allocated) - qemu_free(l1_table); - return -EIO; + return ret; } diff --git a/block/qcow2.h b/block/qcow2.h index e1ae3e8c2b..6a0a21b694 100644 --- a/block/qcow2.h +++ b/block/qcow2.h @@ -228,6 +228,8 @@ int qcow2_read_snapshots(BlockDriverState *bs); Qcow2Cache *qcow2_cache_create(BlockDriverState *bs, int num_tables, bool writethrough); int qcow2_cache_destroy(BlockDriverState* bs, Qcow2Cache *c); +bool qcow2_cache_set_writethrough(BlockDriverState *bs, Qcow2Cache *c, + bool enable); void qcow2_cache_entry_mark_dirty(Qcow2Cache *c, void *table); int qcow2_cache_flush(BlockDriverState *bs, Qcow2Cache *c); From df9cb669425051f4f4364cffb19c9b8089e04297 Mon Sep 17 00:00:00 2001 From: Gerd Hoffmann Date: Thu, 7 Jul 2011 17:04:17 +0200 Subject: [PATCH 157/209] spice: add sanity check for spice ports Make sure at least one port (port=.. or tls-port=...) is specified. Also apply range checks to the port numbers. Signed-off-by: Gerd Hoffmann --- ui/spice-core.c | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/ui/spice-core.c b/ui/spice-core.c index e142452bb6..1100417698 100644 --- a/ui/spice-core.c +++ b/ui/spice-core.c @@ -480,7 +480,16 @@ void qemu_spice_init(void) port = qemu_opt_get_number(opts, "port", 0); tls_port = qemu_opt_get_number(opts, "tls-port", 0); if (!port && !tls_port) { - return; + fprintf(stderr, "neither port nor tls-port specified for spice."); + exit(1); + } + if (port < 0 || port > 65535) { + fprintf(stderr, "spice port is out of range"); + exit(1); + } + if (tls_port < 0 || tls_port > 65535) { + fprintf(stderr, "spice tls-port is out of range"); + exit(1); } password = qemu_opt_get(opts, "password"); From 8927cfbba232e28304734f7afd463c1b84134031 Mon Sep 17 00:00:00 2001 From: Yonit Halperin Date: Tue, 12 Jul 2011 11:51:58 +0300 Subject: [PATCH 158/209] qxl: upon reset, if spice worker is stopped, the command rings can be not empty Spice worker does no longer process commands when it is stopped. Otherwise, it might crash during migration when attempting to process commands while the guest is not completely loaded. Cc: Alon Levy Signed-off-by: Gerd Hoffmann --- hw/qxl.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/hw/qxl.c b/hw/qxl.c index 0b9a4c71ec..a6fb7f0acb 100644 --- a/hw/qxl.c +++ b/hw/qxl.c @@ -656,8 +656,8 @@ static void qxl_reset_state(PCIQXLDevice *d) QXLRam *ram = d->ram; QXLRom *rom = d->rom; - assert(SPICE_RING_IS_EMPTY(&ram->cmd_ring)); - assert(SPICE_RING_IS_EMPTY(&ram->cursor_ring)); + assert(!d->ssd.running || SPICE_RING_IS_EMPTY(&ram->cmd_ring)); + assert(!d->ssd.running || SPICE_RING_IS_EMPTY(&ram->cursor_ring)); d->shadow_rom.update_id = cpu_to_le32(0); *rom = d->shadow_rom; qxl_rom_set_dirty(d); From aaf55b4795d95d87353a08710f237f88d81a3c35 Mon Sep 17 00:00:00 2001 From: Jes Sorensen Date: Tue, 19 Jul 2011 15:01:34 +0200 Subject: [PATCH 159/209] Add missing documentation for qemu-img -p Signed-off-by: Jes Sorensen Signed-off-by: Kevin Wolf --- qemu-img-cmds.hx | 4 ++-- qemu-img.texi | 6 ++++-- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/qemu-img-cmds.hx b/qemu-img-cmds.hx index 2b70618c70..1299e83ef2 100644 --- a/qemu-img-cmds.hx +++ b/qemu-img-cmds.hx @@ -30,7 +30,7 @@ ETEXI DEF("convert", img_convert, "convert [-c] [-p] [-f fmt] [-t cache] [-O output_fmt] [-o options] [-s snapshot_name] filename [filename2 [...]] output_filename") STEXI -@item convert [-c] [-f @var{fmt}] [-O @var{output_fmt}] [-o @var{options}] [-s @var{snapshot_name}] @var{filename} [@var{filename2} [...]] @var{output_filename} +@item convert [-c] [-p] [-f @var{fmt}] [-O @var{output_fmt}] [-o @var{options}] [-s @var{snapshot_name}] @var{filename} [@var{filename2} [...]] @var{output_filename} ETEXI DEF("info", img_info, @@ -48,7 +48,7 @@ ETEXI DEF("rebase", img_rebase, "rebase [-f fmt] [-t cache] [-p] [-u] -b backing_file [-F backing_fmt] filename") STEXI -@item rebase [-f @var{fmt}] [-u] -b @var{backing_file} [-F @var{backing_fmt}] @var{filename} +@item rebase [-f @var{fmt}] [-p] [-u] -b @var{backing_file} [-F @var{backing_fmt}] @var{filename} ETEXI DEF("resize", img_resize, diff --git a/qemu-img.texi b/qemu-img.texi index 526474c112..495a1b6695 100644 --- a/qemu-img.texi +++ b/qemu-img.texi @@ -38,6 +38,8 @@ by the used format or see the format descriptions below for details. indicates that target image must be compressed (qcow format only) @item -h with or without a command shows help and lists the supported formats +@item -p +display progress bar (convert and rebase commands only) @end table Parameters to snapshot subcommand: @@ -84,7 +86,7 @@ it doesn't need to be specified separately in this case. Commit the changes recorded in @var{filename} in its base image. -@item convert [-c] [-f @var{fmt}] [-O @var{output_fmt}] [-o @var{options}] [-s @var{snapshot_name}] @var{filename} [@var{filename2} [...]] @var{output_filename} +@item convert [-c] [-p] [-f @var{fmt}] [-O @var{output_fmt}] [-o @var{options}] [-s @var{snapshot_name}] @var{filename} [@var{filename2} [...]] @var{output_filename} Convert the disk image @var{filename} or a snapshot @var{snapshot_name} to disk image @var{output_filename} using format @var{output_fmt}. It can be optionally compressed (@code{-c} @@ -114,7 +116,7 @@ they are displayed too. List, apply, create or delete snapshots in image @var{filename}. -@item rebase [-f @var{fmt}] [-u] -b @var{backing_file} [-F @var{backing_fmt}] @var{filename} +@item rebase [-f @var{fmt}] [-p] [-u] -b @var{backing_file} [-F @var{backing_fmt}] @var{filename} Changes the backing file of an image. Only the formats @code{qcow2} and @code{qed} support changing the backing file. From afcb7375123fcb73649dba56f5393e2f2e173b5e Mon Sep 17 00:00:00 2001 From: Tsuneo Saito Date: Mon, 18 Jul 2011 15:00:00 +0900 Subject: [PATCH 160/209] SPARC64: fix VIS1 SIMD signed compare instructions The destination registers of SIMD signed compare instructions (fcmp*<16|32>) are not FP registers but general purpose r registers. Comparisons should be freg_rs1 CMP freg_rs2, that were reversed. Signed-off-by: Tsuneo Saito Signed-off-by: Blue Swirl --- target-sparc/helper.h | 4 ++-- target-sparc/op_helper.c | 23 +++++++++++++---------- target-sparc/translate.c | 32 ++++++++++++++++---------------- 3 files changed, 31 insertions(+), 28 deletions(-) diff --git a/target-sparc/helper.h b/target-sparc/helper.h index 023f4d6023..2d36af3a31 100644 --- a/target-sparc/helper.h +++ b/target-sparc/helper.h @@ -148,8 +148,8 @@ F_HELPER_0_0(expand); VIS_HELPER(padd); VIS_HELPER(psub); #define VIS_CMPHELPER(name) \ - F_HELPER_0_0(name##16); \ - F_HELPER_0_0(name##32) + DEF_HELPER_0(f##name##16, i64); \ + DEF_HELPER_0(f##name##32, i64) VIS_CMPHELPER(cmpgt); VIS_CMPHELPER(cmpeq); VIS_CMPHELPER(cmple); diff --git a/target-sparc/op_helper.c b/target-sparc/op_helper.c index 15af27ba1f..b99223eddb 100644 --- a/target-sparc/op_helper.c +++ b/target-sparc/op_helper.c @@ -525,6 +525,7 @@ typedef union { uint16_t w[4]; int16_t sw[4]; uint32_t l[2]; + uint64_t ll; float64 d; } vis64; @@ -789,32 +790,34 @@ VIS_HELPER(helper_fpadd, FADD) VIS_HELPER(helper_fpsub, FSUB) #define VIS_CMPHELPER(name, F) \ - void name##16(void) \ + uint64_t name##16(void) \ { \ vis64 s, d; \ \ s.d = DT0; \ d.d = DT1; \ \ - d.VIS_W64(0) = F(d.VIS_W64(0), s.VIS_W64(0))? 1: 0; \ - d.VIS_W64(0) |= F(d.VIS_W64(1), s.VIS_W64(1))? 2: 0; \ - d.VIS_W64(0) |= F(d.VIS_W64(2), s.VIS_W64(2))? 4: 0; \ - d.VIS_W64(0) |= F(d.VIS_W64(3), s.VIS_W64(3))? 8: 0; \ + d.VIS_W64(0) = F(s.VIS_W64(0), d.VIS_W64(0)) ? 1 : 0; \ + d.VIS_W64(0) |= F(s.VIS_W64(1), d.VIS_W64(1)) ? 2 : 0; \ + d.VIS_W64(0) |= F(s.VIS_W64(2), d.VIS_W64(2)) ? 4 : 0; \ + d.VIS_W64(0) |= F(s.VIS_W64(3), d.VIS_W64(3)) ? 8 : 0; \ + d.VIS_W64(1) = d.VIS_W64(2) = d.VIS_W64(3) = 0; \ \ - DT0 = d.d; \ + return d.ll; \ } \ \ - void name##32(void) \ + uint64_t name##32(void) \ { \ vis64 s, d; \ \ s.d = DT0; \ d.d = DT1; \ \ - d.VIS_L64(0) = F(d.VIS_L64(0), s.VIS_L64(0))? 1: 0; \ - d.VIS_L64(0) |= F(d.VIS_L64(1), s.VIS_L64(1))? 2: 0; \ + d.VIS_L64(0) = F(s.VIS_L64(0), d.VIS_L64(0)) ? 1 : 0; \ + d.VIS_L64(0) |= F(s.VIS_L64(1), d.VIS_L64(1)) ? 2 : 0; \ + d.VIS_L64(1) = 0; \ \ - DT0 = d.d; \ + return d.ll; \ } #define FCMPGT(a, b) ((a) > (b)) diff --git a/target-sparc/translate.c b/target-sparc/translate.c index 27c2cf98e8..a1a19c34e6 100644 --- a/target-sparc/translate.c +++ b/target-sparc/translate.c @@ -3789,57 +3789,57 @@ static void disas_sparc_insn(DisasContext * dc) CHECK_FPU_FEATURE(dc, VIS1); gen_op_load_fpr_DT0(DFPREG(rs1)); gen_op_load_fpr_DT1(DFPREG(rs2)); - gen_helper_fcmple16(); - gen_op_store_DT0_fpr(DFPREG(rd)); + gen_helper_fcmple16(cpu_dst); + gen_movl_TN_reg(rd, cpu_dst); break; case 0x022: /* VIS I fcmpne16 */ CHECK_FPU_FEATURE(dc, VIS1); gen_op_load_fpr_DT0(DFPREG(rs1)); gen_op_load_fpr_DT1(DFPREG(rs2)); - gen_helper_fcmpne16(); - gen_op_store_DT0_fpr(DFPREG(rd)); + gen_helper_fcmpne16(cpu_dst); + gen_movl_TN_reg(rd, cpu_dst); break; case 0x024: /* VIS I fcmple32 */ CHECK_FPU_FEATURE(dc, VIS1); gen_op_load_fpr_DT0(DFPREG(rs1)); gen_op_load_fpr_DT1(DFPREG(rs2)); - gen_helper_fcmple32(); - gen_op_store_DT0_fpr(DFPREG(rd)); + gen_helper_fcmple32(cpu_dst); + gen_movl_TN_reg(rd, cpu_dst); break; case 0x026: /* VIS I fcmpne32 */ CHECK_FPU_FEATURE(dc, VIS1); gen_op_load_fpr_DT0(DFPREG(rs1)); gen_op_load_fpr_DT1(DFPREG(rs2)); - gen_helper_fcmpne32(); - gen_op_store_DT0_fpr(DFPREG(rd)); + gen_helper_fcmpne32(cpu_dst); + gen_movl_TN_reg(rd, cpu_dst); break; case 0x028: /* VIS I fcmpgt16 */ CHECK_FPU_FEATURE(dc, VIS1); gen_op_load_fpr_DT0(DFPREG(rs1)); gen_op_load_fpr_DT1(DFPREG(rs2)); - gen_helper_fcmpgt16(); - gen_op_store_DT0_fpr(DFPREG(rd)); + gen_helper_fcmpgt16(cpu_dst); + gen_movl_TN_reg(rd, cpu_dst); break; case 0x02a: /* VIS I fcmpeq16 */ CHECK_FPU_FEATURE(dc, VIS1); gen_op_load_fpr_DT0(DFPREG(rs1)); gen_op_load_fpr_DT1(DFPREG(rs2)); - gen_helper_fcmpeq16(); - gen_op_store_DT0_fpr(DFPREG(rd)); + gen_helper_fcmpeq16(cpu_dst); + gen_movl_TN_reg(rd, cpu_dst); break; case 0x02c: /* VIS I fcmpgt32 */ CHECK_FPU_FEATURE(dc, VIS1); gen_op_load_fpr_DT0(DFPREG(rs1)); gen_op_load_fpr_DT1(DFPREG(rs2)); - gen_helper_fcmpgt32(); - gen_op_store_DT0_fpr(DFPREG(rd)); + gen_helper_fcmpgt32(cpu_dst); + gen_movl_TN_reg(rd, cpu_dst); break; case 0x02e: /* VIS I fcmpeq32 */ CHECK_FPU_FEATURE(dc, VIS1); gen_op_load_fpr_DT0(DFPREG(rs1)); gen_op_load_fpr_DT1(DFPREG(rs2)); - gen_helper_fcmpeq32(); - gen_op_store_DT0_fpr(DFPREG(rd)); + gen_helper_fcmpeq32(cpu_dst); + gen_movl_TN_reg(rd, cpu_dst); break; case 0x031: /* VIS I fmul8x16 */ CHECK_FPU_FEATURE(dc, VIS1); From b7d69dc233cee75cbdd3f9df12c6cda9a24ba37d Mon Sep 17 00:00:00 2001 From: Tsuneo Saito Date: Mon, 18 Jul 2011 15:36:57 +0900 Subject: [PATCH 161/209] SPARC64: add missing break on fmovdcc "break" is missing on V9 fmovdcc (%icc). Signed-off-by: Tsuneo Saito Signed-off-by: Blue Swirl --- target-sparc/translate.c | 1 + 1 file changed, 1 insertion(+) diff --git a/target-sparc/translate.c b/target-sparc/translate.c index a1a19c34e6..15967c551a 100644 --- a/target-sparc/translate.c +++ b/target-sparc/translate.c @@ -2864,6 +2864,7 @@ static void disas_sparc_insn(DisasContext * dc) break; case 0x102: /* V9 fmovdcc %icc */ FMOVDCC(0); + break; case 0x103: /* V9 fmovqcc %icc */ CHECK_FPU_FEATURE(dc, FLOAT128); FMOVQCC(0); From 86f25c7c37c6f6250cfac4d0ee331af3ca73cac1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Herv=C3=A9=20Poussineau?= Date: Mon, 18 Jul 2011 23:34:20 +0200 Subject: [PATCH 162/209] ds1225y: Remove protection stuff, which doesn't belong to this device MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Hervé Poussineau Signed-off-by: Blue Swirl --- hw/ds1225y.c | 42 +----------------------------------------- hw/mips.h | 1 - 2 files changed, 1 insertion(+), 42 deletions(-) diff --git a/hw/ds1225y.c b/hw/ds1225y.c index b1c52321fe..1fd701086e 100644 --- a/hw/ds1225y.c +++ b/hw/ds1225y.c @@ -33,7 +33,6 @@ typedef struct ds1225y_t uint32_t chip_size; QEMUFile *file; uint8_t *contents; - uint8_t protection; } ds1225y_t; @@ -98,34 +97,6 @@ static void nvram_writel (void *opaque, target_phys_addr_t addr, uint32_t val) nvram_writeb(opaque, addr + 3, (val >> 24) & 0xff); } -static void nvram_writeb_protected (void *opaque, target_phys_addr_t addr, uint32_t val) -{ - ds1225y_t *s = opaque; - - if (s->protection != 7) { -#ifdef DEBUG_NVRAM - printf("nvram: prevent write of 0x%x at " TARGET_FMT_lx "\n", val, addr); -#endif - return; - } - - nvram_writeb(opaque, addr, val); -} - -static void nvram_writew_protected (void *opaque, target_phys_addr_t addr, uint32_t val) -{ - nvram_writeb_protected(opaque, addr, val & 0xff); - nvram_writeb_protected(opaque, addr + 1, (val >> 8) & 0xff); -} - -static void nvram_writel_protected (void *opaque, target_phys_addr_t addr, uint32_t val) -{ - nvram_writeb_protected(opaque, addr, val & 0xff); - nvram_writeb_protected(opaque, addr + 1, (val >> 8) & 0xff); - nvram_writeb_protected(opaque, addr + 2, (val >> 16) & 0xff); - nvram_writeb_protected(opaque, addr + 3, (val >> 24) & 0xff); -} - static CPUReadMemoryFunc * const nvram_read[] = { &nvram_readb, &nvram_readw, @@ -138,23 +109,16 @@ static CPUWriteMemoryFunc * const nvram_write[] = { &nvram_writel, }; -static CPUWriteMemoryFunc * const nvram_write_protected[] = { - &nvram_writeb_protected, - &nvram_writew_protected, - &nvram_writel_protected, -}; - /* Initialisation routine */ void *ds1225y_init(target_phys_addr_t mem_base, const char *filename) { ds1225y_t *s; - int mem_indexRW, mem_indexRP; + int mem_indexRW; QEMUFile *file; s = qemu_mallocz(sizeof(ds1225y_t)); s->chip_size = 0x2000; /* Fixed for ds1225y chip: 8 KiB */ s->contents = qemu_mallocz(s->chip_size); - s->protection = 7; /* Read current file */ file = qemu_fopen(filename, "rb"); @@ -174,9 +138,5 @@ void *ds1225y_init(target_phys_addr_t mem_base, const char *filename) mem_indexRW = cpu_register_io_memory(nvram_read, nvram_write, s, DEVICE_NATIVE_ENDIAN); cpu_register_physical_memory(mem_base, s->chip_size, mem_indexRW); - /* Read/write protected memory */ - mem_indexRP = cpu_register_io_memory(nvram_read, nvram_write_protected, s, - DEVICE_NATIVE_ENDIAN); - cpu_register_physical_memory(mem_base + s->chip_size, s->chip_size, mem_indexRP); return s; } diff --git a/hw/mips.h b/hw/mips.h index 73aa8f8b0e..93c8831f91 100644 --- a/hw/mips.h +++ b/hw/mips.h @@ -10,7 +10,6 @@ PCIBus *bonito_init(qemu_irq *pic); /* ds1225y.c */ void *ds1225y_init(target_phys_addr_t mem_base, const char *filename); -void ds1225y_set_protection(void *opaque, int protection); /* g364fb.c */ int g364fb_mm_init(target_phys_addr_t vram_base, From d43ed9ec2566922e76420b4ac914df6b2d473c4b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Herv=C3=A9=20Poussineau?= Date: Mon, 18 Jul 2011 23:34:21 +0200 Subject: [PATCH 163/209] ds1225y: use trace framework MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Hervé Poussineau Signed-off-by: Blue Swirl --- hw/ds1225y.c | 16 +++++----------- trace-events | 4 ++++ 2 files changed, 9 insertions(+), 11 deletions(-) diff --git a/hw/ds1225y.c b/hw/ds1225y.c index 1fd701086e..5105b9bb8f 100644 --- a/hw/ds1225y.c +++ b/hw/ds1225y.c @@ -24,9 +24,7 @@ #include "hw.h" #include "mips.h" -#include "nvram.h" - -//#define DEBUG_NVRAM +#include "trace.h" typedef struct ds1225y_t { @@ -42,10 +40,7 @@ static uint32_t nvram_readb (void *opaque, target_phys_addr_t addr) uint32_t val; val = s->contents[addr]; - -#ifdef DEBUG_NVRAM - printf("nvram: read 0x%x at " TARGET_FMT_lx "\n", val, addr); -#endif + trace_nvram_read(addr, val); return val; } @@ -71,11 +66,10 @@ static void nvram_writeb (void *opaque, target_phys_addr_t addr, uint32_t val) { ds1225y_t *s = opaque; -#ifdef DEBUG_NVRAM - printf("nvram: write 0x%x at " TARGET_FMT_lx "\n", val, addr); -#endif + val &= 0xff; + trace_nvram_write(addr, s->contents[addr], val); - s->contents[addr] = val & 0xff; + s->contents[addr] = val; if (s->file) { qemu_fseek(s->file, addr, SEEK_SET); qemu_put_byte(s->file, (int)val); diff --git a/trace-events b/trace-events index ad11b09c0a..99a4a2b144 100644 --- a/trace-events +++ b/trace-events @@ -103,6 +103,10 @@ disable cs4231_mem_readl_reg(uint32_t reg, uint32_t ret) "read reg %d: 0x%08x" disable cs4231_mem_writel_reg(uint32_t reg, uint32_t old, uint32_t val) "write reg %d: 0x%08x -> 0x%08x" disable cs4231_mem_writel_dreg(uint32_t reg, uint32_t old, uint32_t val) "write dreg %d: 0x%02x -> 0x%02x" +# hw/ds1225y.c +disable nvram_read(uint32_t addr, uint32_t ret) "read addr %d: 0x%02x" +disable nvram_write(uint32_t addr, uint32_t old, uint32_t val) "write addr %d: 0x%02x -> 0x%02x" + # hw/eccmemctl.c disable ecc_mem_writel_mer(uint32_t val) "Write memory enable %08x" disable ecc_mem_writel_mdr(uint32_t val) "Write memory delay %08x" From cd3e2409a3605b89b6bea2e67fc128c6a9f8d921 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Herv=C3=A9=20Poussineau?= Date: Mon, 18 Jul 2011 23:34:22 +0200 Subject: [PATCH 164/209] ds1225y: convert to qdev device, and use it in MIPS Jazz emulation MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Hervé Poussineau Signed-off-by: Blue Swirl --- hw/ds1225y.c | 106 +++++++++++++++++++++++++++++++++++-------------- hw/mips.h | 3 -- hw/mips_jazz.c | 10 ++++- 3 files changed, 85 insertions(+), 34 deletions(-) diff --git a/hw/ds1225y.c b/hw/ds1225y.c index 5105b9bb8f..662d7b5166 100644 --- a/hw/ds1225y.c +++ b/hw/ds1225y.c @@ -22,21 +22,20 @@ * THE SOFTWARE. */ -#include "hw.h" -#include "mips.h" +#include "sysbus.h" #include "trace.h" -typedef struct ds1225y_t -{ +typedef struct { + DeviceState qdev; uint32_t chip_size; + char *filename; QEMUFile *file; uint8_t *contents; -} ds1225y_t; - +} NvRamState; static uint32_t nvram_readb (void *opaque, target_phys_addr_t addr) { - ds1225y_t *s = opaque; + NvRamState *s = opaque; uint32_t val; val = s->contents[addr]; @@ -64,7 +63,7 @@ static uint32_t nvram_readl (void *opaque, target_phys_addr_t addr) static void nvram_writeb (void *opaque, target_phys_addr_t addr, uint32_t val) { - ds1225y_t *s = opaque; + NvRamState *s = opaque; val &= 0xff; trace_nvram_write(addr, s->contents[addr], val); @@ -103,34 +102,83 @@ static CPUWriteMemoryFunc * const nvram_write[] = { &nvram_writel, }; -/* Initialisation routine */ -void *ds1225y_init(target_phys_addr_t mem_base, const char *filename) +static int nvram_post_load(void *opaque, int version_id) { - ds1225y_t *s; - int mem_indexRW; - QEMUFile *file; + NvRamState *s = opaque; - s = qemu_mallocz(sizeof(ds1225y_t)); - s->chip_size = 0x2000; /* Fixed for ds1225y chip: 8 KiB */ - s->contents = qemu_mallocz(s->chip_size); - - /* Read current file */ - file = qemu_fopen(filename, "rb"); - if (file) { - /* Read nvram contents */ - qemu_get_buffer(file, s->contents, s->chip_size); - qemu_fclose(file); + /* Close file, as filename may has changed in load/store process */ + if (s->file) { + qemu_fclose(s->file); } - s->file = qemu_fopen(filename, "wb"); + + /* Write back nvram contents */ + s->file = qemu_fopen(s->filename, "wb"); if (s->file) { /* Write back contents, as 'wb' mode cleaned the file */ qemu_put_buffer(s->file, s->contents, s->chip_size); qemu_fflush(s->file); } - /* Read/write memory */ - mem_indexRW = cpu_register_io_memory(nvram_read, nvram_write, s, - DEVICE_NATIVE_ENDIAN); - cpu_register_physical_memory(mem_base, s->chip_size, mem_indexRW); - return s; + return 0; } + +static const VMStateDescription vmstate_nvram = { + .name = "nvram", + .version_id = 0, + .minimum_version_id = 0, + .minimum_version_id_old = 0, + .post_load = nvram_post_load, + .fields = (VMStateField[]) { + VMSTATE_VARRAY_UINT32(contents, NvRamState, chip_size, 0, + vmstate_info_uint8, uint8_t), + VMSTATE_END_OF_LIST() + } +}; + +typedef struct { + SysBusDevice busdev; + NvRamState nvram; +} SysBusNvRamState; + +static int nvram_sysbus_initfn(SysBusDevice *dev) +{ + NvRamState *s = &FROM_SYSBUS(SysBusNvRamState, dev)->nvram; + QEMUFile *file; + int s_io; + + s->contents = qemu_mallocz(s->chip_size); + + s_io = cpu_register_io_memory(nvram_read, nvram_write, s, + DEVICE_NATIVE_ENDIAN); + sysbus_init_mmio(dev, s->chip_size, s_io); + + /* Read current file */ + file = qemu_fopen(s->filename, "rb"); + if (file) { + /* Read nvram contents */ + qemu_get_buffer(file, s->contents, s->chip_size); + qemu_fclose(file); + } + nvram_post_load(s, 0); + + return 0; +} + +static SysBusDeviceInfo nvram_sysbus_info = { + .qdev.name = "ds1225y", + .qdev.size = sizeof(SysBusNvRamState), + .qdev.vmsd = &vmstate_nvram, + .init = nvram_sysbus_initfn, + .qdev.props = (Property[]) { + DEFINE_PROP_UINT32("size", SysBusNvRamState, nvram.chip_size, 0x2000), + DEFINE_PROP_STRING("filename", SysBusNvRamState, nvram.filename), + DEFINE_PROP_END_OF_LIST(), + }, +}; + +static void nvram_register(void) +{ + sysbus_register_withprop(&nvram_sysbus_info); +} + +device_init(nvram_register) diff --git a/hw/mips.h b/hw/mips.h index 93c8831f91..cae5f4c804 100644 --- a/hw/mips.h +++ b/hw/mips.h @@ -8,9 +8,6 @@ PCIBus *gt64120_register(qemu_irq *pic); /* bonito.c */ PCIBus *bonito_init(qemu_irq *pic); -/* ds1225y.c */ -void *ds1225y_init(target_phys_addr_t mem_base, const char *filename); - /* g364fb.c */ int g364fb_mm_init(target_phys_addr_t vram_base, target_phys_addr_t ctrl_base, int it_shift, diff --git a/hw/mips_jazz.c b/hw/mips_jazz.c index a1003945fd..f6ab6dc455 100644 --- a/hw/mips_jazz.c +++ b/hw/mips_jazz.c @@ -37,6 +37,7 @@ #include "loader.h" #include "mc146818rtc.h" #include "blockdev.h" +#include "sysbus.h" enum jazz_model_e { @@ -115,6 +116,8 @@ void mips_jazz_init (ram_addr_t ram_size, void* rc4030_opaque; int s_rtc, s_dma_dummy; NICInfo *nd; + DeviceState *dev; + SysBusDevice *sysbus; ISADevice *pit; DriveInfo *fds[MAX_FD]; qemu_irq esp_reset, dma_enable; @@ -266,8 +269,11 @@ void mips_jazz_init (ram_addr_t ram_size, /* FIXME: missing Jazz sound at 0x8000c000, rc4030[2] */ audio_init(i8259, NULL); - /* NVRAM: Unprotected at 0x9000, Protected at 0xa000, Read only at 0xb000 */ - ds1225y_init(0x80009000, "nvram"); + /* NVRAM */ + dev = qdev_create(NULL, "ds1225y"); + qdev_init_nofail(dev); + sysbus = sysbus_from_qdev(dev); + sysbus_mmio_map(sysbus, 0, 0x80009000); /* LED indicator */ jazz_led_init(0x8000f000); From ea15fb067212bb79291f9b76c5c76d45b62f58e1 Mon Sep 17 00:00:00 2001 From: Stefan Weil Date: Wed, 20 Jul 2011 20:56:36 +0200 Subject: [PATCH 165/209] tcg/mips: Fix regression caused by typo (copy + paste bug) cppcheck reports an error: qemu/tcg/mips/tcg-target.c:1487: error: Invalid number of character (() The unpatched code won't compile on mips hosts starting with commit cea5f9a28faa528b6b1b117c9ab2d8828f473fef. Cc: Blue Swirl Signed-off-by: Stefan Weil Signed-off-by: Blue Swirl --- tcg/mips/tcg-target.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tcg/mips/tcg-target.c b/tcg/mips/tcg-target.c index 12ff9d5259..a33d21fd07 100644 --- a/tcg/mips/tcg-target.c +++ b/tcg/mips/tcg-target.c @@ -1484,7 +1484,7 @@ static void tcg_target_qemu_prologue(TCGContext *s) } /* Call generated code */ - tcg_out_opc_reg(s, OPC_JR, 0, tcg_target_call_iarg_regs[1]), 0); + tcg_out_opc_reg(s, OPC_JR, 0, tcg_target_call_iarg_regs[1], 0); tcg_out_mov(s, TCG_TYPE_PTR, TCG_AREG0, tcg_target_call_iarg_regs[0]); tb_ret_addr = s->code_ptr; From d0510af26d854f714114a312eb4aed0649447eb2 Mon Sep 17 00:00:00 2001 From: Blue Swirl Date: Fri, 15 Jul 2011 20:09:10 +0000 Subject: [PATCH 166/209] checkpatch: Fix bracing false positives on #if 789f88d0b21fedfd4251d56bb7a9fbfbda7a4ac7 only fixed #else, fix also #if. Reviewed-by: Stefan Hajnoczi Signed-off-by: Blue Swirl --- scripts/checkpatch.pl | 1 + 1 file changed, 1 insertion(+) diff --git a/scripts/checkpatch.pl b/scripts/checkpatch.pl index 70a2111d19..3498425fff 100755 --- a/scripts/checkpatch.pl +++ b/scripts/checkpatch.pl @@ -2539,6 +2539,7 @@ sub process { } if (!defined $suppress_ifbraces{$linenr - 1} && $line =~ /\b(if|while|for|else)\b/ && + $line !~ /\#\s*if/ && $line !~ /\#\s*else/) { my $allowed = 0; From b5176d279a81b3205a6f4e4757b39a4e4f8833e8 Mon Sep 17 00:00:00 2001 From: Tsuneo Saito Date: Mon, 18 Jul 2011 14:32:18 +0900 Subject: [PATCH 167/209] SPARC64: treat UA2007 ASI_BLK_* as translating ASIs. UA2007 ASI_BLK_* should be added in is_translating_asi(). Signed-off-by: Tsuneo Saito Acked-by: Artyom Tarasenko Signed-off-by: Blue Swirl --- target-sparc/op_helper.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/target-sparc/op_helper.c b/target-sparc/op_helper.c index b99223eddb..8b0df68477 100644 --- a/target-sparc/op_helper.c +++ b/target-sparc/op_helper.c @@ -287,7 +287,8 @@ static inline int is_translating_asi(int asi) */ switch (asi) { case 0x04 ... 0x11: - case 0x18 ... 0x19: + case 0x16 ... 0x19: + case 0x1E ... 0x1F: case 0x24 ... 0x2C: case 0x70 ... 0x73: case 0x78 ... 0x79: From 21673cdecb9e9b5a22acaf0a44e47145beb1999e Mon Sep 17 00:00:00 2001 From: Blue Swirl Date: Thu, 14 Jul 2011 15:22:20 +0000 Subject: [PATCH 168/209] Avoid CPU endian memory accesses in devices Don't compile virtio.c in hwlib, it depends on memory accesses performed in CPU endianness. Make loads and stores in CPU endianness unavailable to devices and poison them to avoid further bugs. Acked-by: Alexander Graf Signed-off-by: Blue Swirl --- Makefile.objs | 2 +- Makefile.target | 2 +- cpu-common.h | 19 +++++++++++-------- poison.h | 9 +++++++++ 4 files changed, 22 insertions(+), 10 deletions(-) diff --git a/Makefile.objs b/Makefile.objs index 1635df6e2f..c43ed05c89 100644 --- a/Makefile.objs +++ b/Makefile.objs @@ -171,7 +171,7 @@ user-obj-y += cutils.o cache-utils.o hw-obj-y = hw-obj-y += vl.o loader.o -hw-obj-$(CONFIG_VIRTIO) += virtio.o virtio-console.o +hw-obj-$(CONFIG_VIRTIO) += virtio-console.o hw-obj-$(CONFIG_VIRTIO_PCI) += virtio-pci.o hw-obj-y += fw_cfg.o hw-obj-$(CONFIG_PCI) += pci.o pci_bridge.o diff --git a/Makefile.target b/Makefile.target index fe785161cb..e20a313b9d 100644 --- a/Makefile.target +++ b/Makefile.target @@ -191,7 +191,7 @@ obj-y = arch_init.o cpus.o monitor.o machine.o gdbstub.o balloon.o # virtio has to be here due to weird dependency between PCI and virtio-net. # need to fix this properly obj-$(CONFIG_NO_PCI) += pci-stub.o -obj-$(CONFIG_VIRTIO) += virtio-blk.o virtio-balloon.o virtio-net.o virtio-serial-bus.o +obj-$(CONFIG_VIRTIO) += virtio.o virtio-blk.o virtio-balloon.o virtio-net.o virtio-serial-bus.o obj-y += vhost_net.o obj-$(CONFIG_VHOST_NET) += vhost.o obj-$(CONFIG_REALLY_VIRTFS) += 9pfs/virtio-9p-device.o diff --git a/cpu-common.h b/cpu-common.h index a5b80e1351..44b04b3839 100644 --- a/cpu-common.h +++ b/cpu-common.h @@ -134,28 +134,31 @@ void qemu_unregister_coalesced_mmio(target_phys_addr_t addr, ram_addr_t size); void qemu_flush_coalesced_mmio_buffer(void); uint32_t ldub_phys(target_phys_addr_t addr); -uint32_t lduw_phys(target_phys_addr_t addr); uint32_t lduw_le_phys(target_phys_addr_t addr); uint32_t lduw_be_phys(target_phys_addr_t addr); -uint32_t ldl_phys(target_phys_addr_t addr); uint32_t ldl_le_phys(target_phys_addr_t addr); uint32_t ldl_be_phys(target_phys_addr_t addr); -uint64_t ldq_phys(target_phys_addr_t addr); uint64_t ldq_le_phys(target_phys_addr_t addr); uint64_t ldq_be_phys(target_phys_addr_t addr); -void stl_phys_notdirty(target_phys_addr_t addr, uint32_t val); -void stq_phys_notdirty(target_phys_addr_t addr, uint64_t val); void stb_phys(target_phys_addr_t addr, uint32_t val); -void stw_phys(target_phys_addr_t addr, uint32_t val); void stw_le_phys(target_phys_addr_t addr, uint32_t val); void stw_be_phys(target_phys_addr_t addr, uint32_t val); -void stl_phys(target_phys_addr_t addr, uint32_t val); void stl_le_phys(target_phys_addr_t addr, uint32_t val); void stl_be_phys(target_phys_addr_t addr, uint32_t val); -void stq_phys(target_phys_addr_t addr, uint64_t val); void stq_le_phys(target_phys_addr_t addr, uint64_t val); void stq_be_phys(target_phys_addr_t addr, uint64_t val); +#ifdef NEED_CPU_H +uint32_t lduw_phys(target_phys_addr_t addr); +uint32_t ldl_phys(target_phys_addr_t addr); +uint64_t ldq_phys(target_phys_addr_t addr); +void stl_phys_notdirty(target_phys_addr_t addr, uint32_t val); +void stq_phys_notdirty(target_phys_addr_t addr, uint64_t val); +void stw_phys(target_phys_addr_t addr, uint32_t val); +void stl_phys(target_phys_addr_t addr, uint32_t val); +void stq_phys(target_phys_addr_t addr, uint64_t val); +#endif + void cpu_physical_memory_write_rom(target_phys_addr_t addr, const uint8_t *buf, int len); diff --git a/poison.h b/poison.h index 2b182328a1..5354e7779c 100644 --- a/poison.h +++ b/poison.h @@ -37,6 +37,15 @@ #pragma GCC poison CPUState #pragma GCC poison env +#pragma GCC poison lduw_phys +#pragma GCC poison ldl_phys +#pragma GCC poison ldq_phys +#pragma GCC poison stl_phys_notdirty +#pragma GCC poison stq_phys_notdirty +#pragma GCC poison stw_phys +#pragma GCC poison stl_phys +#pragma GCC poison stq_phys + #pragma GCC poison CPU_INTERRUPT_HARD #pragma GCC poison CPU_INTERRUPT_EXITTB #pragma GCC poison CPU_INTERRUPT_HALT From b14ef7c9ab41ea824c3ccadb070ad95567cca84e Mon Sep 17 00:00:00 2001 From: Blue Swirl Date: Sun, 3 Jul 2011 08:53:46 +0000 Subject: [PATCH 169/209] Fix unassigned memory access handling MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit cea5f9a28faa528b6b1b117c9ab2d8828f473fef exposed bugs in unassigned memory access handling. Fix them by always passing CPUState to the handlers. Reported-by: Hervé Poussineau Signed-off-by: Blue Swirl --- exec-all.h | 2 +- exec.c | 12 ++++++------ target-alpha/cpu.h | 5 +++-- target-alpha/op_helper.c | 6 ++++-- target-microblaze/cpu.h | 4 ++-- target-microblaze/op_helper.c | 14 ++++---------- target-mips/cpu.h | 4 ++-- target-mips/op_helper.c | 6 ++++-- target-sparc/cpu.h | 4 ++-- target-sparc/op_helper.c | 26 ++++++++++++++++++++------ 10 files changed, 48 insertions(+), 35 deletions(-) diff --git a/exec-all.h b/exec-all.h index 69acf3b97d..9b8d62c239 100644 --- a/exec-all.h +++ b/exec-all.h @@ -323,7 +323,7 @@ static inline tb_page_addr_t get_page_addr_code(CPUState *env1, target_ulong add pd = env1->tlb_table[mmu_idx][page_index].addr_code & ~TARGET_PAGE_MASK; if (pd > IO_MEM_ROM && !(pd & IO_MEM_ROMD)) { #if defined(TARGET_ALPHA) || defined(TARGET_MIPS) || defined(TARGET_SPARC) - do_unassigned_access(addr, 0, 1, 0, 4); + cpu_unassigned_access(env1, addr, 0, 1, 0, 4); #else cpu_abort(env1, "Trying to execute code outside RAM or ROM at 0x" TARGET_FMT_lx "\n", addr); #endif diff --git a/exec.c b/exec.c index 827790088b..2160ded401 100644 --- a/exec.c +++ b/exec.c @@ -3238,7 +3238,7 @@ static uint32_t unassigned_mem_readb(void *opaque, target_phys_addr_t addr) printf("Unassigned mem read " TARGET_FMT_plx "\n", addr); #endif #if defined(TARGET_ALPHA) || defined(TARGET_SPARC) || defined(TARGET_MICROBLAZE) - do_unassigned_access(addr, 0, 0, 0, 1); + cpu_unassigned_access(cpu_single_env, addr, 0, 0, 0, 1); #endif return 0; } @@ -3249,7 +3249,7 @@ static uint32_t unassigned_mem_readw(void *opaque, target_phys_addr_t addr) printf("Unassigned mem read " TARGET_FMT_plx "\n", addr); #endif #if defined(TARGET_ALPHA) || defined(TARGET_SPARC) || defined(TARGET_MICROBLAZE) - do_unassigned_access(addr, 0, 0, 0, 2); + cpu_unassigned_access(cpu_single_env, addr, 0, 0, 0, 2); #endif return 0; } @@ -3260,7 +3260,7 @@ static uint32_t unassigned_mem_readl(void *opaque, target_phys_addr_t addr) printf("Unassigned mem read " TARGET_FMT_plx "\n", addr); #endif #if defined(TARGET_ALPHA) || defined(TARGET_SPARC) || defined(TARGET_MICROBLAZE) - do_unassigned_access(addr, 0, 0, 0, 4); + cpu_unassigned_access(cpu_single_env, addr, 0, 0, 0, 4); #endif return 0; } @@ -3271,7 +3271,7 @@ static void unassigned_mem_writeb(void *opaque, target_phys_addr_t addr, uint32_ printf("Unassigned mem write " TARGET_FMT_plx " = 0x%x\n", addr, val); #endif #if defined(TARGET_ALPHA) || defined(TARGET_SPARC) || defined(TARGET_MICROBLAZE) - do_unassigned_access(addr, 1, 0, 0, 1); + cpu_unassigned_access(cpu_single_env, addr, 1, 0, 0, 1); #endif } @@ -3281,7 +3281,7 @@ static void unassigned_mem_writew(void *opaque, target_phys_addr_t addr, uint32_ printf("Unassigned mem write " TARGET_FMT_plx " = 0x%x\n", addr, val); #endif #if defined(TARGET_ALPHA) || defined(TARGET_SPARC) || defined(TARGET_MICROBLAZE) - do_unassigned_access(addr, 1, 0, 0, 2); + cpu_unassigned_access(cpu_single_env, addr, 1, 0, 0, 2); #endif } @@ -3291,7 +3291,7 @@ static void unassigned_mem_writel(void *opaque, target_phys_addr_t addr, uint32_ printf("Unassigned mem write " TARGET_FMT_plx " = 0x%x\n", addr, val); #endif #if defined(TARGET_ALPHA) || defined(TARGET_SPARC) || defined(TARGET_MICROBLAZE) - do_unassigned_access(addr, 1, 0, 0, 4); + cpu_unassigned_access(cpu_single_env, addr, 1, 0, 0, 4); #endif } diff --git a/target-alpha/cpu.h b/target-alpha/cpu.h index 78caa796b6..919be12a38 100644 --- a/target-alpha/cpu.h +++ b/target-alpha/cpu.h @@ -434,8 +434,9 @@ uint64_t cpu_alpha_load_fpcr (CPUState *env); void cpu_alpha_store_fpcr (CPUState *env, uint64_t val); #ifndef CONFIG_USER_ONLY void swap_shadow_regs(CPUState *env); -extern QEMU_NORETURN void do_unassigned_access(target_phys_addr_t addr, - int, int, int, int); +QEMU_NORETURN void cpu_unassigned_access(CPUState *env1, + target_phys_addr_t addr, int is_write, + int is_exec, int unused, int size); #endif /* Bits in TB->FLAGS that control how translation is processed. */ diff --git a/target-alpha/op_helper.c b/target-alpha/op_helper.c index 51d1bd7099..8f39154391 100644 --- a/target-alpha/op_helper.c +++ b/target-alpha/op_helper.c @@ -1301,9 +1301,11 @@ static void QEMU_NORETURN do_unaligned_access(target_ulong addr, int is_write, helper_excp(EXCP_UNALIGN, 0); } -void QEMU_NORETURN do_unassigned_access(target_phys_addr_t addr, int is_write, - int is_exec, int unused, int size) +void QEMU_NORETURN cpu_unassigned_access(CPUState *env1, + target_phys_addr_t addr, int is_write, + int is_exec, int unused, int size) { + env = env1; env->trap_arg0 = addr; env->trap_arg1 = is_write; dynamic_excp(EXCP_MCHK, 0); diff --git a/target-microblaze/cpu.h b/target-microblaze/cpu.h index 51a13e38d1..76f4fc4a7a 100644 --- a/target-microblaze/cpu.h +++ b/target-microblaze/cpu.h @@ -347,8 +347,8 @@ static inline void cpu_get_tb_cpu_state(CPUState *env, target_ulong *pc, } #if !defined(CONFIG_USER_ONLY) -void do_unassigned_access(target_phys_addr_t addr, int is_write, int is_exec, - int is_asi, int size); +void cpu_unassigned_access(CPUState *env1, target_phys_addr_t addr, + int is_write, int is_exec, int is_asi, int size); #endif static inline bool cpu_has_work(CPUState *env) diff --git a/target-microblaze/op_helper.c b/target-microblaze/op_helper.c index 1a0a476a62..664ffe5990 100644 --- a/target-microblaze/op_helper.c +++ b/target-microblaze/op_helper.c @@ -488,20 +488,14 @@ void helper_mmu_write(uint32_t rn, uint32_t v) mmu_write(env, rn, v); } -void do_unassigned_access(target_phys_addr_t addr, int is_write, int is_exec, - int is_asi, int size) +void cpu_unassigned_access(CPUState *env1, target_phys_addr_t addr, + int is_write, int is_exec, int is_asi, int size) { CPUState *saved_env; - if (!cpu_single_env) { - /* XXX: ??? */ - return; - } - - /* XXX: hack to restore env in all cases, even if not called from - generated code */ saved_env = env; - env = cpu_single_env; + env = env1; + qemu_log_mask(CPU_LOG_INT, "Unassigned " TARGET_FMT_plx " wr=%d exe=%d\n", addr, is_write, is_exec); if (!(env->sregs[SR_MSR] & MSR_EE)) { diff --git a/target-mips/cpu.h b/target-mips/cpu.h index b0ac4da5a7..33be2962a2 100644 --- a/target-mips/cpu.h +++ b/target-mips/cpu.h @@ -493,8 +493,8 @@ void r4k_helper_tlbwr (void); void r4k_helper_tlbp (void); void r4k_helper_tlbr (void); -void do_unassigned_access(target_phys_addr_t addr, int is_write, int is_exec, - int unused, int size); +void cpu_unassigned_access(CPUState *env, target_phys_addr_t addr, + int is_write, int is_exec, int unused, int size); #endif void mips_cpu_list (FILE *f, fprintf_function cpu_fprintf); diff --git a/target-mips/op_helper.c b/target-mips/op_helper.c index 6b966b1849..01315ef0dc 100644 --- a/target-mips/op_helper.c +++ b/target-mips/op_helper.c @@ -1980,9 +1980,11 @@ void tlb_fill (target_ulong addr, int is_write, int mmu_idx, void *retaddr) env = saved_env; } -void do_unassigned_access(target_phys_addr_t addr, int is_write, int is_exec, - int unused, int size) +void cpu_unassigned_access(CPUState *env1, target_phys_addr_t addr, + int is_write, int is_exec, int unused, int size) { + env = env1; + if (is_exec) helper_raise_exception(EXCP_IBE); else diff --git a/target-sparc/cpu.h b/target-sparc/cpu.h index 22ee2743d7..0084b67625 100644 --- a/target-sparc/cpu.h +++ b/target-sparc/cpu.h @@ -510,8 +510,8 @@ static inline int tlb_compare_context(const SparcTLBEntry *tlb, /* cpu-exec.c */ #if !defined(CONFIG_USER_ONLY) -void do_unassigned_access(target_phys_addr_t addr, int is_write, int is_exec, - int is_asi, int size); +void cpu_unassigned_access(CPUState *env1, target_phys_addr_t addr, + int is_write, int is_exec, int is_asi, int size); target_phys_addr_t cpu_get_phys_page_nofault(CPUState *env, target_ulong addr, int mmu_idx); diff --git a/target-sparc/op_helper.c b/target-sparc/op_helper.c index 8b0df68477..3b7f9cab5d 100644 --- a/target-sparc/op_helper.c +++ b/target-sparc/op_helper.c @@ -79,9 +79,14 @@ #define CACHE_CTRL_FD (1 << 22) /* Flush Data cache (Write only) */ #define CACHE_CTRL_DS (1 << 23) /* Data cache snoop enable */ -#if defined(CONFIG_USER_ONLY) && defined(TARGET_SPARC64) +#if !defined(CONFIG_USER_ONLY) +static void do_unassigned_access(target_phys_addr_t addr, int is_write, + int is_exec, int is_asi, int size); +#else +#ifdef TARGET_SPARC64 static void do_unassigned_access(target_ulong addr, int is_write, int is_exec, - int is_asi, int size); + int is_asi, int size); +#endif #endif #if defined(TARGET_SPARC64) && !defined(CONFIG_USER_ONLY) @@ -4235,8 +4240,8 @@ void tlb_fill(target_ulong addr, int is_write, int mmu_idx, void *retaddr) #ifndef TARGET_SPARC64 #if !defined(CONFIG_USER_ONLY) -void do_unassigned_access(target_phys_addr_t addr, int is_write, int is_exec, - int is_asi, int size) +static void do_unassigned_access(target_phys_addr_t addr, int is_write, + int is_exec, int is_asi, int size) { CPUState *saved_env; int fault_type; @@ -4301,8 +4306,8 @@ void do_unassigned_access(target_phys_addr_t addr, int is_write, int is_exec, static void do_unassigned_access(target_ulong addr, int is_write, int is_exec, int is_asi, int size) #else -void do_unassigned_access(target_phys_addr_t addr, int is_write, int is_exec, - int is_asi, int size) +static void do_unassigned_access(target_phys_addr_t addr, int is_write, + int is_exec, int is_asi, int size) #endif { CPUState *saved_env; @@ -4351,3 +4356,12 @@ void helper_tick_set_limit(void *opaque, uint64_t limit) #endif } #endif + +#if !defined(CONFIG_USER_ONLY) +void cpu_unassigned_access(CPUState *env1, target_phys_addr_t addr, + int is_write, int is_exec, int is_asi, int size) +{ + env = env1; + do_unassigned_access(addr, is_write, is_exec, is_asi, size); +} +#endif From 6c916eda22e7ef180bb2dd183bbef85f9ff006c6 Mon Sep 17 00:00:00 2001 From: Blue Swirl Date: Wed, 20 Jul 2011 22:06:33 +0000 Subject: [PATCH 170/209] Update OpenBIOS images for Sparc32 and Sparc64 Update Sparc32 and Sparc64 OpenBIOS images to SVN revision 1045. Signed-off-by: Blue Swirl --- pc-bios/README | 7 +++---- pc-bios/openbios-sparc32 | Bin 377388 -> 381484 bytes pc-bios/openbios-sparc64 | Bin 1598328 -> 1598328 bytes 3 files changed, 3 insertions(+), 4 deletions(-) diff --git a/pc-bios/README b/pc-bios/README index 40568f82aa..f74b2463f1 100644 --- a/pc-bios/README +++ b/pc-bios/README @@ -10,10 +10,9 @@ - OpenBIOS (http://www.openbios.org/) is a free (GPL v2) portable firmware implementation. The goal is to implement a 100% IEEE 1275-1994 (referred to as Open Firmware) compliant firmware. - The included image for PowerPC (for 32 and 64 bit PPC CPUs), - - PowerPC is built from OpenBIOS SVN revision 1044 - Sparc32 and Sparc64 are built from OpenBIOS SVN revision 1018. + The included image for PowerPC (for 32 and 64 bit PPC CPUs) + is built from OpenBIOS SVN revision 1044 and Sparc32 and Sparc64 + images are built from OpenBIOS SVN revision 1045. - SLOF (Slimline Open Firmware) is a free IEEE 1275 Open Firmware implementation for certain IBM POWER hardware. The sources are at diff --git a/pc-bios/openbios-sparc32 b/pc-bios/openbios-sparc32 index b2dc5c5e7b645cd5deffe1bdf36e53ba87b17be9..ea9cc3214e1bba99de7ab201e53bcf01b7c43679 100644 GIT binary patch literal 381484 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 H{WJd`2SJUx literal 377388 zcmd444|rA8mG{5z&CR_b1h@$y|ItIBNs1H$qT-*LQ%fziwo+>?ZLPftR;hJ5jAO0S zSIZ$rq}FjlM4Dl&Jp_u>I&EYqrHnPVw;9TG94Et2OPy9PXl=%6ZH7UN7;@jw_uO+~ zgF0`2&oj^OAuT76_-V#5p(=6YNEVHazg=Tfhqp+#o?P{ zBBsT}O|jDXUn-h_t~i}GWm%CW_%G2*(yMw*M1}vPSAD26=|n8}72qIe;YfcOxX4O> z2{=}OH!e2jktL$5X%)_b-yiZn3w@$MkGyAqODuRV@B|Bf8o1Pge+E3!f_DR#S#Sn; zk_A5rJlTSG0Z*~u9l%Ko-UeK5!QTb0u;535r&{n9;7SYb1Dw!<~wotDk6`iFE-N7(CL((4Q9z^j29EVvVRdI1jkM=ky@lYV9)9sb*Z z9Sgn%c!mXk0l3kEZvvia!8ZWUvfvKjcUbUsz)cqXN#JG+z6Q9(g0BL8rv-l)c(w&! z4t$mcUj{tKf-ePrmjzz}JlBFR1U}n>&jSv8RXFT1A9$XXJ{Net1p@Im1BS@3hfms#+$z(Fh!j`;Ho@M0@{FYpH} z_-WwFE%;}^AGF}zz*kss2KYl3{3P&)EqE93l@`1Mc!>pX1OA8we;4>F3w{*%qZYgc z_-YI81OAu=KMZ`01#bZUxCO5VUTVQTz@M<-`++}c!S@1x%7Rw|Uu(ggz@N6@uK-_X z!Cwacj0N8YoU-6sfZHwj3&0%~d=v083%&vPdJFCV{;UOG2YiDCe-ijc3%&+;xdmSZ z{5cE$Fz`(ld^zyvE%-9vn=SZK;6Jh8OMt&%!50GmsRf@0yuyO#1OJ%?&jr54f@cH& zxdk@?-)g~)z+bfBGk|Zi;5y(hS#TBb?G{`C{1+BH8TiWqrd z8wdW11s?@oX~9Q;Jqtbryvl-K0q(Tmmx1rH;Fo~AEchVs-4^^D@M;Tw7Wf_ueg=4r z1@8s^ss%p{e6I!n4ESpnyc_sF3(f%lr3F6;e7^TJU#)do1`- z;0G*t3-CG%?gM_%f*%H6Z^0XYziz?nfqN~u2l%fo_;hoSnxH# zTP^r1;BQ;-hk+lp;LCx(W5JgJKW4#~0{^WAUjqDH3%(Hedlq~iaK8o52Y%dw=K^oD z;Mu@`XTeRt+by^e`1=-o2Jj9Gt^@vo1y=#@wBQQhA6oEa;9VA63jFsL90z{Ff}_Ac zIssRX13ziOM}Y?{_y};?f)4@z*n(dH&RFovz(29zmw*Q?_#p5%0Dj$qCj%d~;8Nf>EI1B4 zX2DV5V<+ILnf5J7>H_ihmo^yn+m~lI}&u*(~-xJVP%~%6n z8r;|Wc}K>tyOWKkAOrkb(g*W-PX9Rkz2NR!HJs1)g0{mqE6?ug87c5rDzA`tD|}CZ zTcv!(zpbicG4N%`*jdOM_%EOv;^|68ffxAC!S4(6y#Su$`jU1tY5RGea|+K5;C~hJ z%zx@UnG$~ni|R6{rZoeE^r zj-dGovX{U=;pFzz@FVb7hk4#bp1lPg@Jr$Qe%{Z<-yr{)d0pG_vxe`Her?G2MfgU+ zJ(}0`Nzxa6TJd2Weg&U#&3pf&C-LN{==JQbXl!v!(Qx|gC&!>|g(k8qX-as%l=pZ~ z(oBDFwv!Y+w2Ptr^@H>Mr1wZF(&(8uc69Q27yW|t<%&D-o^t`^FKRo|<(hX)9BS=& z$TeqAlpe&Vpzp~fq|13DLp84GE6A%zxIvRzJUG+8c4(%b8mdW^?5jyFbWPjq`)g9J z^vUxrsvjk-xb1MNE4k4lzM2Qn@qziN4@~@ z8O2WTMCTxG%AP$Hsjc8X1#as((45OZurn0T=G#V9yV5@j-DCMOH`GGAGha^|*N}Dv zY5mZbczuqk@Jy-7j7_F_eriI+K<<}a#;*+R^)-0+JX&N%|Z1S66FQV+! z&-K!QI`h`EKHQU>h0n}1y*ynr|8Iv(YNQuC*Api)iIh>jiMFTHoz#7)DRa$*)ApIv zFtO}Il0#h;iw=MLn_-h$u??S+UgTX1=M}_5_|x=hhf@!*hE2b=pr_Q0s4jP#<8@el zN*%^UN8KH-L*yizk^2t*XHt$CJhvlbOXWd-FZH&$vX43|YVE5`4f>Tc-J}o+@V#@B4U9s@?OxL4IM`pFXYrDP$}O z?cc+D%1Wm%s~-fnd}VIDR6Y{aclC_r;Gd*U!#=IP3!2;WY3*xB`vGbDSLTi#Q+-=J zkAn~UwE8c>({1rQN?I145qJu5T={GU|6-`$P4M^@&o@XLhUW!%Dy@9J4*qu`&!^$( zw|Kfq8-eGU^5gwSP@a3i9}anzz>{wy)j@ggB5f3&eU&SHzFg|t?9z5vexE1yR23qqb@cv2Qm zJ!uQ!X@_T_#WM~3#UanX!?VKTnM|4s&(*a1sKrwPeo@HtG(2l8o(O4+;dwv#TzrD3 z?hWu)ggifnr~3p?-C@$`JC9vRK4Bfm26goR?N^69+u`vop5KzTl(d_n`&;6l)h64o z1^0XyGpXCUZ^7Gd@$QE=)gAX3`y89TJ?=*=J^z)o>%%<14&NS&?`NbfC%(FhuZoAF z3(_kb;y_)7^qajcUTwv|_-}|QQz=iZCozz7&7~3g>PY6wfgCZ)MCn5mXS={D?hLlH zw+h}>-BGfWI8Jvr0o=lLh!>4w7wO5J)r~da7m_z){KGfWANb^bv-&mRqz`(m0QWFy zTh%A>a_n#PuG;1qfYw*y$?C<)Yp4{qP8GdV-EZ!4jy~F=SScZAzhO9 z3E+0_qHhf8%60_h|L75o)ima#zpL-Ac9QGR)iqbYdnfPWC+|h!c+L|}wEZ znzgf?nza`?HR!&KGEU(61)gO*+vj`1*y2XXRC|EGJA9Ax-U}|q(-)3sKY4sd?ZnC# zTi5=Yv_LPOjIV0@j<*B)2Kpu++`e$1q0RSURR>kxr=Z)cbo^%`X?wu^ zqNie6+QRJzw->rp4>Bkt{`j64Z9cGTW{Oh`Uv}RVC-Te`C%S)%Q}pZ<++0AM4q?uEaQ1Q&q+M*;5nJ6$8(Bn%*(uY$qwkG z@qIpS=F9Te_+bCa+#AoU4tX;F&+8ca=XQ9y;b~Rbc)y1Cyzd8nY3*0x-?{3i*%Hk~ z3hNZ7KDBP4I?j*f{abf4hSV5vkH&x-P0<3+Owt%|kMU!Io2XyG;g~+rmGoI3aT(*k zdMou@oN;{P`Kcntb|Z}KJdJHN4h5(2c#&dpt7nXB62Y3bYob>RH_ey{qFYz7(PQ4x zO8vx`cYKxDX7b}$a~b&MdcP=`*R=f@xWzSQWiZ~Y?jUw0E+_7;pgeaDCQ=Q9PWw~g zyTkh)l@ojm@7WMf`>8q$c#vM=LyVzlZ;VZ8$LLBsPQ~)JH6FHIthA#h#k~Fv=Kr)g z=z0HAcf!@!x;S44&duO%Il0eiyq&anP$tn)S4q|}8oRZg6z0WR(#Bz>^(WitgUt1$ z`-Qqg$BKUHL^`AAJBLjBQ-w6u-!xOC@xGaA9MiMfCC)Tm(|wr!B+?w{^_HoKq(@kI4L!U?5Pe#rj$l3YjWwaS|iWF?6^1^$Ao}O;i3ZC_C_z{!-^8U7($}4O-Rh zx!7TZd6H|cdP(K^W7h9v(+TjdDQ{dcXgbV2Eq=7i4CDr#j-bs9MNH8*UieXO4{LAh z6aHdiGq(BB=+S0>>7izS@h;vEH~Uu{Y4(?lHv3l-x6gQ{d0UE%B~p>0 zE8UDS=|h=E zH>`GRdN1{jw<+-5lJ(5U?@%9Jj zIrPJQ%YQI88yz)nz4up65$&UrKC8ytl2U!1-;LkmGh;o;8Ti4>XV&oyeWO+NhaGkg zkuEv(B@I8EpNj3QFb(u)kx|F3-#MjiDA6 zs>@+^&RMzisjgvn%nZy({R^0saPkI;m&Fk$)E%4IR@XLJ(xPn z{ywsnYRtg%8jTltUcEj4yo&K>iPi+RRl6nq)uxQ{F}II@1R2bSa{K-|)SLJBhxT}% zc%+-g8{pSK+i;ZpPs#HlwLj85XiaCdO=IB*J`^3H9>C95`|6yUHq`aw4=3Hi`@dN4 zJM_L6+4~xunEZBcqm#E|UcXC+;!a6td?2^oVL)qK*5iUaFJ~R1E)#cRY7_WBzDgOR zrq^%7Xuv9BNLL*m`e(PBQwcyh(nAsl^`U*kcl9OzOQCo}m2#Q-=F!BlQLT zQ~O1g4Vt@D2GxCeTP~+{8Rn}wWMJo+k0TR#j*(5VC;FQj@VitG{7;vTt||UK_y)mN z{)`)EU92{GYW{_Gt^RapvTw~fJa<%etV<}q5i91k9bP`_qz)@4Y_IZ7+UZ-PeuO;g z(#=k+zp8QZ5vQGTo5qwK{-D`7XRz5RIqDoJNjn`rYacJBn^VM;Hx?dl%Ho45lWyu* zr+hk{6{aVOzdCN+UTljk%b=kQGZ!ij3^jE;#{0R#4>k4m^FCki%4;y;#D<)X?U{JT zW6Uuou;%($+HuVweJE)q%!!usUc#I##<*@d`zyTn^{4TVH2%?Dbzp?PId+04Rgzy9 ztETTx73P|wuBm4IuY*2zyeIF|^6MkL|^tpnii+TRCF=ErPwBzG1j!8GW!Y4iddNY3@H!hg^WnA{!LgJy>Rh^<826p(s zu4cDEWy6MN5x*wTb{fR=RRzukW&EJW5;vWEyALU*WeSaR$c(MAo z`FYOaEU%!Y}namu-8Ls9B_{K;EavB|~f z)CVO0=B!kWYd$*bZ!SvJbV4&YdDEUuQ@cAjH`_H>l8VT0I+M%X4DpdZ{axViw#r0) zR}PbRTxFWi99U^HC|3}xE4BVW9aJsbgMF5$Z2aGcoC==Zpy^1qFY zr3D${hi}z(>aP>|gOiu-$;_JJDvlR)m#&gUy|GR@Gew$Xkli7zSjFajAul#4}-x+rnW}2LZjJ3-;lN~qnzQ)Q&vEXyarw`B? z(W@)y*H>gr%4C`c#zq{U_G2pL>q;A>eKM9;pLvoF<=Aj4aSt6d9@g6Ll95J7@niT< zqjTR#lrsxI&Cl~?pSSxDY#=+l#s66I!0;1^ffbo%KQ`Fxm+Wr#%l0(;$)7d*l}|PM zHBUGD4MWYov$xrwwXfN4d8XN)v%lGIeYV+O@T+Ej;d9OY#o1=xJ=p9odXah2OU#Le znG?OtoalGVm0oG~uN`UjQ_PvJXTG$Y`O?kImsT*3x^=wSzXN{7>lGT0jNR@s=SPnO zdL+;zfgTC;NT5f;Z`hkahXgt#&>?{i33N#K3$qFGPmn+Jw3ia(pCJDP`6tLfLH-Hy zPmq6t{1fD#ApZpUC&)kHuNg}C_pS1RyuGo6fBPz;(_K{1+u2k2x3XmcNBw+5b&Kx@ zYtS2dT*bg*;!w%jAtyLPf)6oIN>5_$_aN=m6h#AAbHNs374tb)W8g{mQ}@KJSd#f; zZ;=z(aF;s?eB%B{Hln@Nzd0ur*-+$0$SZOGjBEtHiKPR%;M@&uIVwHw-;C=GmUUDS`Pk3!ezN5or&Jg+CM`0!cjqC83la^t;sIT7;s zZ0}6g14u_!3|VIfvM3wx)zphjgY3A_t!(3 zx6^qtG_Qs96M292Eo2sh|L>3{%KLxyZgNyVeX@TO^{qO~uN9cmRh|?0pXxcl-C`!_ zUH;2Bg*BxsZe$N=$u7ocQ`>Sm`mLPCD#DTWKKzn3r7NxzjL&FXG9gU6M6pqQjrtg9 zE)@PmEP6s?5n|;ByV*0SA@<#=^)0WjEr-9%9di67J&7mwz*o60+1Tp!HMW8;E;44e z;wCE0dp#+}R3Bc4ZoxPM8PhXUp8O&G z!xZ|uN%L;J}{PtXl7xZtR+gJVMGhUyszINJP z(&&5c4EmI4pTRc2A4;UK!;_<0zX1MM!Cs$Z8oi)@4Rlg^`s{(%$d^4}XGr-;7Vsy4 zvxPhte$lnD&mZVAHNeei1$W_&X^lgy-NwqnsZ7*EVqJyr^$}aP0cR3V?(FBe8F)+O zfhC!9XBVRwY^fo(f zvMuXnZPZKBPk2e%?`kP$#3>}l_xndNa zX<x(Iyqp1PbXd}RhiGWW4=GkE=B-ZpOYgeS zIg`DSrCzsNf{w*QPF8yh(v!MxT%6b6ttCbzLi{rD%jFC5gTM!P$KDmJ1v95S`YF!) zTuXivlpp)k&fwg%{567|>N?};8tkDMG?BKs2)pIH&4GQ2`ZosSNkYuP7t&g-Y4_!priUL{P^6|VWfknrmF z;4K+y$}S1@WbNhk|3L0i`Kf3qOTAtiv5aqpc}FDF#B$1aIsGtaRWT{ z6X9R`Z-Jk&Pq%c?WMkf16D8JHZEy1GS7SHNPshkR4qu&k)ebx16WoH2NKP^9H=dsj zY)Pufs>Mef3aW{hX;s&%o}YgJuu%Hp>RsL~~Qw$OpefK6H3t2W|KAlz6fC4N8ZO zb7|_ck9Md|3gZv_$IDQ-Rqss)YeLYB6ky)}tMCq8Ij~cJv6r7|Nf-EL3=(f81DcJb zwd@XM@*dkArtu!N@R0)E!sQC@$a$k6Q!#?MU5xg31@pw~72i5pOI4g=jkHU?I?7xx zwBdEwAsfDbiuYl7blze*btxNirpzIZy?!+@;`p49GcK>I|40suzoFQmai8YM@YOJn zZec!pAM@6dqN00#$NVdiX~O^74*x0RElO8?4{3}E4ztz^ecVx8Q5#^s>qNTgqsea^ zno7wWYI17QjV4&rO>2$Uw9la&WraFnE_-J%2CZ;X);r}pV&PA`EB=v(p*$&_R}^ng z)MahqwS}P^Q_fm;;XQalA3me@{sVHtITz!OXeyRLPoA=KjAxB_crOWgp9r59%U*>s zTX%ITMQne1nqgy5>jx@db1GFR-~ZM+qViT7P~OZV|5LVC{Z7SBJr&ZSd@XhDkoJ4U znvm|mDRkFEcMo(e(7jNg+oia*#Zi2!gr4zvd0~G@=SA#%mXk)m<}lXF{hRa&`@Oi{ z!3EzL@q9}DDjTp@HeI@e@)t;c7&EKHLm6&H=2`U3Ddh$4_Atg4%BJ*BlILR5b`3(Bp?>t2&N1R1Z7v+weDGRg z)$x64JGQ8d-Rn~tqpQEZIdge1ZVARbk|~_Z=$hn6n1{{`*tWeD-B|w*%6W%qWLxCL z=fO+azZCL)QRNJ5`^t<|-Y45~`TeQU*@gY7eECO{iXWma)OBc=Pa*Rr-fLNZWj^C* zP5PQ`RVh|llDUw!w}*8T(H~;{hBcbwd)nITUYQ$h+uJ-aX3M6GH(@L4aFq2whxU1- zi`b@hKgA)|x!8YlT>NCTRb^@Q@_wpxaQJ}Zox;-#&sN1o(r3N}&l&La{|`KYj}`Kv zY-?2>@?(uVXg7C&`w8`;c5~(_Hqsf3K=+W=gLWtR?Xk-YpL~NcM=lr(t1Qr|jqC?U zJUpq6yGVP4v@B^wh5YE_U(3rFf0bulD1-Aq4(Bsu&+$L>hxkyJHOP4|FK7I}WS5h5 z8h=4@;8Xk87)Ux@1^pkMT*SI|i`cU|J2`M`O8^@DT$37Q+D^MCYI zxIvky=X&7ugN(a0mhCKQ&vhHOvgP4#pal z_Xc3?&Gkwy&-FZIvvoX)fl1ZpaXZOJz%4|-bq8lPo@8(Q@6JTqVf-c4yE=Jbs5oZc zGXa>hP>J9iiIeK3Z!BW0RB-RbNV46#Qc0b*>V;){skz2m#=)kZk(l z+SSPylHh|gc;K%FcbKzJck+I%XxMYR5t@Ezg0*h&DezM}lkH`zli3mQL7xo%`Ve0N zehK&*`uBG5%k%hU1|7P=H-Jxqzd4UjZC#x_*b3eOud>{V&UYfe75XQ@w}87H+IIA) z0j6ypt_0`h`8z1{!J)w>XBN0SgoAFyo+f7ra4&Er`Xqsu0zVYO{m?%K?C0SFqTL+6 zkMO>ge6}ke_GqNr&hUMR_a~?u_V(H@=6y~0zKHkxI;#%!^X%@dZr{#xozh%0<*S^f z`jy%R-=9(6Q=ZV7Iea4MQ$#b=bf6#nO6WMheVlF^bZO`&t1XiMw4vtq?bOeTOw$b3 z`43M+ha~URUGPrcGpHlIcP!z(vf#t=)wlasTY0;Gc)Sef5C=JfxSR8bdpLXeGtM18 z#hJsWId3Rmrp;;|RL4B1p0$sL4f*yEd|Ta5AD1bDzX<*!_>15#g1-pi z!C9@-SHD+-yBbbo{hm9${qNP)r@4M#IKA~7yYe*GZ{caK->lPIzjvPI`b|B(^?Tzq zkI!%X^z^ph(foHhr|tam=y|8P{ercJ(|3GuG&nnS8tZqO_aCnZXTkqi``w#-N0K#l zGx+%X{Cgv*=-^l1=X<{YkqR&J9{F(yzYqA`7W^UL3oQ6!z~>4^R?)pPkQ2(=gS_4R z-_w#US(8l1!uO>0UegIaVNA(=j$6cDJ>Pt8D(i}P(WkvGllHqLGyXrxT(ydY&5cfq z^NQ#5WCPl~gQru<8A0jftxan$Dme39)Nng{wW-+MEq+;-aoMBh?2q!``%~pLk^Jv1 zUlzMJ;uR0BT4d527ePP6405JFOfx5^HQH$Pa?io#8;cm=APDUjJ3|lZpzRFd@63W z`4;$1V<*%3u`aU_+Kr_7L#L*JpYvv1U(1_un`XTkx7m3!?pqCS#%-y2GwzYfQ*f1& z*pI!h+&8R)n>FQ*xo?ISTkW`U>V#*ySpWAYP0?S@^UJ=9EucALPhbl_w%VkN@7w4{ zI8PF+UERl7gw->$u?cPeom*2rb8I5#@$W59={!J8w$7}II2pcd@-~{{)tq&Rb0&68 z#0h-#{uy3T7yj^9^Rlt0eUEkbMts!M_s!MZT@&nC7wSCp<@BmGKD)X3GrD@RFa782 zt@AC#Yv*$oGB|rAy!N#@uP}l9#=D~q-%#bh-CWh1VhuQ0cb@YUtH|1$W`9LK!S~F* z>25To-7~zBsrUr%6S^Bc)`&%)8XbZ@d>_8sr9A_$>OOH^Q~9pS8gtL${95qplI)q3 zIifv;Ro$tR^!EI0g1yZ;@l50%^?S?P%P4mZ_b$!a6LHNRhl3t@d@{t(IR$@5h+lIG zUiOom?gBp0XHE8`{8z2XI?B87&zXVy0{wrQ-^(t)*yYR!_p9+uGW#kqzNagG=FymW z=F#_Md0$?5$F|X)Xe#>5y{jCrFRR!P>^B$EX@f^~P8WYTp4Z*jr>n(xR;^(}7Q3%b zW;uH$o&6Q7*=D4!YgVPx!~)~}zvQk8_ug4EFGa702hTAR*OmHHygq+IW$1(CU4#y; z{C|8OW!R@O^krjJ1GyujFUTlwSfnz?r(V2BWsZk&Vm*6l&&xJEf6E$uB*gzMaBvqU z&jaAgL;Meb|1G5dckmS<{>R||S@?o2&SnqxmEwb&-vu&VoUU3mh(A3=pbhchOab?` zIobHV6H+U;PI6X0nl#0a5v#tvAR7TsjCCtX%gOgvtxcKiMZS6QBEMw#B0u@Ni~O3A zi+tzsMSjcZMSknpMgBtVQ{2mWJlby6s{5Sd^&;I0`F&?WHm`qnoIW`HSBnL<_w)RL;iQcvwxb~za7}ylGeG!s@2V= z4w@;0ANGonQOi*%>FnrC;2N`Xz}x85*3dR|))YHCq5UQtO}Hk{;|Je4cbb8Lz&3wK z#gyhfk`n#}+cwf!`_`0=J@MQSd$KK=piTna>r@`4pUXdK3!(Wb=UBOyN8bxOG4w3k zUbWE$|6|I+J*0t+rtdXwJ-EAe@GYw9m2yqonc&PZb?!aFclZOj;I4vJ&VtBRhp-Fu zUkdi(RNs78Em>Y)pvS~+@*}VKP}DUS5+8=B<9vPLKaxL?dxLs9^{&uDxi^7^o+gw)h9`Nz<9JfJfg4*T6Fko+@~%--d^K(%i_Q z$xc1_h`tv3x_QWWEBb9!F5hPT;kLD}G`7ly@>$M3HI(qJf_z$evmdK-#*4XcWs|Kw|po+%lelF~hBH%TV>aZajnX(5*I zJ=>l06#u5ArtRSVlYLWCRpeQZJ!*y$GoHd8oTcYH`fR^s9TNiRs@EmmQrdpwzNQ&_ zGVvLE2BF&#Pe*7&>R+W>^qvcx=vOar&RjEre~!J~bG9i0F8=B}o#>u;I_9lO$IvY< zy?3MQYU35{cDNHjFfs*GmLl5KD{CA#x@Ss?bKGsY-BVJv=un588f==yJL;?Y?S&N{4MykK-^@K?F_YM)8Rh>7$!E5~(zu=w|b+rak-y0@Ws zswv$t)z!Tm!THDshRvjW**1=0JMJ9bI0oE#>Yc;Vn>$N7^LLiCkDa))q+)_-p#Yiq=SB3a9EyBm79J}ec0cs?6Mc11(vXhVs7@+Te>C8%Y2D1ZT4!(fyt$Y#67%qKz}h zH`5if)-Vp?oBaq%mN37X&Kx~h@5uzVcKJ3X@Trf7`G2g?cUbwK73Tj=3)d9lW(lYI zkdMf(Iqw(v(GY$V+HOL@c8X`JOWo;|N$_1iR+P_CZoa{ABQ|aXnDaPC|0ntCiy8A2 zOP73JVu$Rp+6-u1c}y`w@qu_79WibZ`aFZbpzrv; z6Sf$Cwvd;~?)h#Bbr9IN({W1*c2PM;unD~XAl`z{hI;>FK@ZY*=WRPaXyx(a5I2zb zx$*5*+OD_AzoMJG3o%UVO<$euM4uy8XOAl4_eb?wWhCQJ*~!%179wEo86{Ar6-&beWk<^<`V z4ALDdJ#5?Kv#j(QEB%a+|2)}Q-#h3$nrzQJ<@4B^vE;bE^U(b{!96*mA?+;DNhay_ z^o(UsewzW_;R4-)6Lim7blFheb1K8*vjcthTKUnOBfWfr-SqhgY?4rSs%VVVft>R_m>9gyDa{6nEqstUJ;~kv(k5l={th-x*$Egw_M|b zWBp3+Ul`bFv&Aq9-( ziFOfWO(o z-xA>Sa&EBjH-|cC-c~ZyOuunb8#{J=nBJlE40GB}&PQk7O!vuTVZS)BH|6{Jwcb(~$cs_Sgx%wT1WAm-@EMyKr|8nd+(Rq)!CFkMC znpcEvr`e)?S4ewSfp*r(v<((*_?^Zvr$F1Ea^&YzEt=Y}4AlV*Z7rlJ6^+_B-vfEf zZH|u*x}zo>9~_IEh<;FVJdWjZF2fn|7iAa50xxR*6s~a;sm+Z5+jT%$ zyiEK6dFTFPlr-5GJ^o?{oYu-1$HqDnPFW`5@O1fhlR2wdImVpvq&Xb<1#>Or{6gz{ z;TSm9pGcLBaNk;gRPlIhPQiC9`hT+NJE6}&-$^@^uJ9cHenw@N?F)8Sxhb>9_n0rp z-l0t|KgFiO_~}%eBDbQT%gWr#g?1G?(XYN7^s7NR26D&pKKSx*{`tny6Le!HO#7!3 zxHp13!l>)l>5s7I8?R|?(>3q5`t3Ikg|bGJ2F)w-A7~CT1_|yj)SLvphqb;z`b(Nu zN+0T--&|-P9>hMKj7i=+*M1|o+X$XtN~UaW&-38t+vpSXTD=#10-mFxUdLk_-$t(B zKH7DI^fj!r*39?J~B>T<+*m)iR5G88!Phm&h67P@V9WDmT%YN z+{4tUZ_(b?F4S3Y7F9IdLlwmOZG5ACD1R66org{Pptr?WTP_Lm*y7g3!};mFASS)s z!Fu9I!4|Y}z7=xV``fsW^_YEK*qD0(SPMx7->mW;>~HejZ7hQ=I-|sg@OxHpWhz%D zuzN~&FP2_f7a2hY{-|{am4z7RnuWCA{wevI z-n*tXuHd~^cZ7id*j}}L$=vKlV(cY&n^WO8kuw*P{-K%3Qy#oujt{Mn?rJxDNBt4@ zc*dCjc&ytJdtSf0yRv;mwCi}H&k}gvy%+khEYU{Kd{Jcs|FCSh6~4RP<<#7@Fs1Lb zKSufm`WB6S%Ua-D_`i%YT&;ct++^UZ_+JFwOz8pc=;1ox0ApX23q&-)8gRcPpMPytQ;&tCkk#wLe zugM;Ll!d!{b?3Zd>-n4Jq-fjY!8n^d&rw;GZ)R4o|2emhI+LyPeiYQ_tW)Z9DdW)J zQ=eSDOMT*l=n>ZEvEQP{Th!;V13`VN??0tJk-={X%uEq)9#FkeU(Lv4Qi5Dwlbpm0i?ExNjv`%MaQ~@eKMZ6QMpP<^5)K`^i2xI!qaK zKS(+H_Th*1*=DE2){EHthauV;$D@bq9JS558HimxDIHC98Hi zPr5Mu%l%r?>u90xRs2b4eO~(I z*WgdAyXXDgt&0&)gSbgQE&qrPjDKJLAzaYkl?)tH-(8@eMZdGX#;rp)?#n0EuJI#{ z1LNyekHUMtsq1&0<9)aKAMI@rf8X}^5Hl4H`@vs%U3+kMw=y)Kk7o}|-xDYAYE1#R zPgI-htVunE{QMndDemlG4VHdfeQ%(z+A+4!7%bQ;;_hP3+XVODU(UPsy7^{z;RntE z7k)SVU))d3-M#tyjOLl(ZdBd#!o6e{R1U>E%DAg1OKj!$#5&60eGOmZP8P>OAKk_C zd2C#xb32S<{-qmQ(o7?Fz3jS&b>1?^a6jXVo!pNR`tHi>u#oY+Ha6Yd+xOYl>^V^ag z+I#$b@a<}a zeStah6R$5lojvdPNZhXkKasw-NqE6)sEbV8uR-UA?Yw)N97A7RvnJwj&lY1&_y+kM zId7BO0KP(a!5i*!i@3il>TU7Lh%GT}sr%_ml`g&anhw_IQsS@9m+kOP;hiD0p)9N zR`t|b(>{?qoQcgQvW|AaK58+3bmzLL$Nk0po?BR#==@I6k#^m_z-NNHSw6p)ch7ex zklupaom27A-2u;mgy`|Z@*eI%_xuABp(!aQo%%gYI85-}G4~~g?oBV^*+9N7`3{qxV*LbsxV&Hk$~b|%hoLzi->fBFcTVa4G~FA3pU$9< z{q3Oa0PS?yJG2#NPJYY%zcbu|?SOXyFD=JciCM3ApPetKYfL-%72r5Wk(UeZT5!GM z<()h0W~9S+ez#=Ci1N#~?}eXZJ>0r4CLag1-VNUewI1-t#39?iPxWbgkCe84Bw~!( z{9(-@zWGx(m^ZHa>4`fuNPCfTu%GX!U*TQ?2VE{y48m5qUl-<=@|jGcBe)+2-%MoU zZqW8t=3e6t+eX$5JGeh5=)(iwAL5;~I>{Moa)UERiaF#lMfO1UZt147(4UlJ7v=Xe z;re?5x?l{i@@;p#y2I8!fyVlZjgmbNo5S}scd}Pv};3J#{Iebp%LwUq-o4i z&~+0sTEXux@bZ3_(&aOXWyG*q;5ZZ1=<>X~KxgX#{x;;d@Z8NjYKipa{m)L3GY6dR zwO-2e&kJ&_e5A(?(wFo6yp32!Lo%Cgeblz`KK0H6hGiY0(-V4F?h4dd+ zU50fY3;pma$&mjs7cJ}$@0ASrUM{RbSboG=6PMgZl}~=oOIRiHPm14 zTljP>>5EAJFzH*v`mI!c)L(e#OUGi;8CT74h(~X{MLwE`{SG--KzH%lSDZbe9PRy} zckuggcKt1ZZa($5leFW$eX`!;`;m7wbS>x=${YWcYz5D=iZk9LDYf&`{2t_Q=XNJj zkwTkRU&5H>-5NWbdWO&P%caQI`KSFnhYRIWj0)Sb&MMLFU-}3-WXbpM^7S(QIOS4X z!=J|g(bk)Jy7c=tx?jul^-yLnXIgX@Oa49y?qVUomkzV$YR5=#iyy{DQyZMwZ}KE7 z^o8J#71c?h&7?>>KtDv=8V~k=)CSNy*aruLM%YdQ{{we5^5nM@cF|s>12&!L=XJeDy3kL)j((aa>TaLK(AP8n9+4dK z`wD$^L)gCWJsS7I7#H}b<^uA;;5!*_OG>iKbT=w8)&JWuU=6b8VN0IJUQ)hl)BN@= z7q2?9UxQ!S7_6%zZ?a@5|HfcU;Kx|+IsEPo^-^V++cd<8~6f9cC-Sth3cZVpCcfX@~ZNV2ds}CvEkIK#5Tz3CpGd5>k zfO~mO3G?%ADi^#Be#{Z?VrU9?wN^+*n9o`B$wxF{9+y~j;rQy9#u(H1uf$&a$YZMb zc?NfUBKt3;gJfWfn728w#lO6aEl7(}-xXnfPcGCGa)NWSR@=A_+>fYFol7amu803N zWK+)ZoOt^KKO6r&G{=4Ve{(E47+_uQY7kdXzM!YfPy%n(v?OM2|9lq^^p$$2lX- zJ)V^NpN3dl-g!@3u3;cIwj-V!8zLWU^ltgLWw&l{e~SDxJi&KQ$SIfJz(0}AlrJ!Q zR3^gQEUNX3&bX0}atCgTxn0nP^*fn&%AbQWKN6fR8WHX&V_IZhpmumd=M=v~_9mSl zmd?BGS$V3?>PE%~mQ zN=$T0B~R^#v%PmdSCnnoP~=8eUuTM*otU1q+A#MwzS4`HJu6+*PiuU*ys>w8QI_=x zuL$2dykV(ZL>r5Afgd!Sf#+N^>kU!TUe%dCoehVd?>rsn;i6nlb+!6a>7s({JuUE8 zAyc{{vxz;y=cwlq{wd3oeCHC((L;a0N4yfs9PC}umL1krYG{L%`SDZkdX1k7XDd}s z-SMhhp^`t2=t(zml>vq?ZCYg97Uqrpg4z(-Ja>AdI4<}R>>=mq` zsC*o?|4yNN;QLvR?p3?nj$h*A4&{u{PKszJQTEHe&U*f=t`eWR^D9{wYh_J)*6PZR z&8+*d|H*mhv=@{+qcu13Z1f`6LsQBAKWjFTRHn(VWPb=arF;D!?<`q43QXCZO5i8t zBfAq$Lzi)*dOy;Vj>1o!yV25t+|jO22j|}CZ*%Y2S{(Rf4F2TSpgx?W_pSUlGV*<7 zvc4C2Z(erfJv?Utw~g?W-jpGVO{>|r#9)TCQ=e}E{K$MS(lmsQ@<;KA27C;>6XMSn zzOy8S9a0}6{Xcnrin6x62%bEeRc8718sjzSn@G>kPN9v&_)U_r^bBWCy2)>OsmaHd zlHp&Zc-LiC4#C#~JQ>>-3(mKzNX1~Y(-PW!0%=o}KXz)74#+Rn+1}L&Q#5F@G4j+o z-=eOFpI~|q53}vX(x1v)S;W-{xE@T}$l>HO&yoG%E;hd0<@455w2)>;m-}m7Q zefYRe0S~c7I(;|P={pu(F@E1FUk5iw@6r`oVjs9Bzm+^&MX$Um&qKTi<-ryklrR6% zm43sEpv!~80Sh0r0oJf%)VtQL?^jyaQod(-oAxq$Aosdx86)0?--@SfnCIKk7upx} zx{tR~?JLkF-=Gioq_)L&b*C*Z3jvnLPV_Ug1UU5{ugdY*$D5Go%T%$OP+)DXkh{ONV5ylZy z6~icVtsiqDl%rvTaVM?D=konNeKNeH2mSsU?4YvChWOH}8z?7t3fPcoI4=>`M@#(`? z2V=_c&iKJNV}0_bE{m9BlxUuz`{V}`F6VagduA1$3C^Kw{NtLUlD+ENXFJ%`F^%eX zIg2zexcmCRX4c_q4w24ph2DGt|Ey2kti3SS4VrgUWtZ?flN81@@mrIve4m6peA8q6 zqO3{jHy+0*Z&8m)Pw>myv_D<%N8MzG{xEN&R5AWnjsGRPsx~groFrqs$f~p(^sBB} zpzjhUAb%3~*$4h8oq-cP8%SRrrWcd0wTAkYxdXv{-I7V&=)1jtA2f`AewyD$_+#dY z#b^HOM|Ve!&N-EE&gwG!Lu2#?>c=o9ixnH!TV0Yfe20&%gS2UCXM@QUdHSL)4`I$$ z_?*Bq@nEcLO22~-znoG$<%E2Vja&nk?wFGRx(t#g}u~s8PA+!IEx(L6w|oY zHS@MIZr0c>MrdrJ*U=gE4;fpLWN6%^In(jq@0GqKpg83u+{Tq2_|F3l;I`BK(g+1!X-XUjMjJ@SO*l4Tv zf`^(12Ic=zFY>csPq=#EFfoydze%VKN}rXvY!+Qw8DC^1i+ybIA0J=B`}Zm%`;UhU zd%5f_r?q}2Un*x`S$n(MyWNIN>WTI1nHm$Rz0TbhXADRi+A%p@a+vY^_ISF^>+|aB zu!q;j`c$=l>;uQ%BrcTVi})>5oZ zZaw%?;Rk__@AdY%+UqU#ws?~$dx_H5L>%UrW=hjQ?)Up^jbsNtAbp964tt|%k9jNU zPphqEn+KlQM!ixm4UDB3U&}`v&O|x7|3Gt}&tVhxG+$kTU9~5_1lxW!+z*z2YE7wr zeSCnuuIyddd^tA1UiC|zFg7(y)nDTq{C0JU-;@q~khA^+gZNi$opJH6*4KxM1OKYg z6ZqGpB{TfvW06M6xGNs)lj3KzZT3XbiTPFb#%yBX33ObnI_7x+cQVv$DW+an|9Z@% zie4^G#n7YVu}K4Ctd|w_#nM%ePFkt>Huuq(JB@FrZ^qx)2XEiWed+qGHulLKLwd9K zEq8h*p4EDi+CK3z6&>RHch{V^uA4qj@rm-6?+b9}oH68VDhuf~)|jaGkbaHm13x@L zUx8oix1ED?JjkD@{9dd~SIK`VW18}vjr@wk*oohgsbPJfTr|YVJ(*(H$gb3DzD~bo zRHvIWaaV1(LH3btlqdE)Fl3g+ye(=o`7?~j*7_{#3OXCGjCI$P%B(f8o3RPsqAz33 zCd=>9%-A2&1m_2+3;kBqa{fO@dO~qY@I2ZJ?ZfA{vTkO*RPE1j_VH%Lx!=*o)P~aR zfAp~z!n$>i-_084ce9eLpSslB<=>6_jB!$GTbIdKv1#zz?C3b{nK-{mR_f6&KmVWm zQF#CWib@m?kT z&#OES%t?j(rtX3HDQs^JiC27#?TD8w0>H-_66H$QQm)@;62@@0FZr!Ge)7QuspJC- zeCo-YfX@ZL7ZTbpI46$|A568;kOP}y6tLl(bl=}ykT$?y4HdcIT2m>{t)=5WTB z@n@r99N^&Vz8P$EV!HDPzT!c?GjYwi{5HMA+DBjq+Ulz-fTQ5QvyZbcAzTFf2ypKp zzm-Z~ApW@e3}|Dd`NYDgVDFKjtx;BgN&0+8wx!=Rz1S!=1T4JDgU3^1&a03X?7diNztWgSbfg6_k+g7+}LLUp$&fHOvdz$#WxX zUzypW{%9Nj)OYz#zQsPGIp~&jg#Iv!&+-jpR^#9aY9q3(`XYR*9)F}x=l!rJ;Rb2) zvrKhr4s9rcul+$jIFv|ftbZf<1!tWYON^lR%FwRu=%se3vV1wTEA96+{bn6?@mla5 zIq8a*vnlst`jCbBb-dT^vG+lt#b^H*Tf916u*C^mzcwkqH+k%`H>c(Llvbz*?oVse zyeOOxmvIl6)>n&mmvSFPOO|oG%RJ7FF;*zH0R)4VwX2zK64|F^NiiB z-Ei)(L~Am{N6vYLXAb$Uaos|GlX@X%mG5;T$d1!qN;%V&ylcK+Li^^Mc(-)zY|brm zc5&@|Cpf>TI9Y^lI(z5!nV{|z>$J|FL@dB>DTnT)~$^B{zJYBUAT|Fj5>dfel7TYrh`fY{*9j1_$vORv3Je-iWg{m z$Ch;S9X;}oc2{c;IQNX`yuM(35bV2)c4h8WKcpK8>7KId<&T-iXe_=m_nWxnsXL3Jtz|JBQ>1{G) zosL_xp)(zP)3CoeRnmK@ul3;?zHwlV?V1cRasP!*Qr~74?i9ig4nlL%oe#{HYgW@o zQs4C+eGOxUcQH;p{{8xX*1OktuFS2wv@NInln*{tMt{h>%A>FFG**pM|MZDzolRss z1s<69a-NKzgZ&l9>vM~Oba3)*?Z+4N+kj00+~wA7f3Jfq)=>s>dxtnb8RnC}n~MCzCtMic*63Th&Ujmn zadNG<#;rM+@KfYdJbWg%DaCI@e=PVoKq!CH4gG@So@L8`j{a;rq3#g8FI8^%4iVKZ`1T zUWxLf9251f{LrJy>q}1{zc_kSL#y-&!V8vc$-*|Yx!h;5555repMfp&d9e1v7#aVo z^YVSzH1%!4@6z&Hv+MCW`7h@d2G(6F-wXP=GSO(BgWpW2uHS$^x}W-{ZX)Y9xl^I< zU$@NgyF@2`15s_!(y0#G8l|J#CC|juLHnPN?J~SeH~EO(!#pR`9&1=Lkk4zZel+N( zct`)>+gsA)`&#e#Z>5QTW$xEU<#UpwHhHwsDLK^WlpSU`I@EL`A9U+pnR{U@h(lX~ zc36f_GQQAU($qW?({Bsr_-*L?c@tvvnZo0L?Aw{zqci=~252c?-RMmJTG@R)Fm+PT z_A7~eEhl?=TY@%H ztbFGM{rj&*>EHWVL;Io1!t)2DY2T4NUR`0IJ>uLO_8I>kXS**}tPR^_Zy$LD?Ghf! zSCr-V>Vp_S-B%3c#`|KaV9u2>%lLgwqd6S;J5lVnTX||u_AS0eI*83TUrQ|7?3K?V zjvS7AhOvLSx7n>|8sOj|??d1y`#Q>7uCrAKo6_dRW@ALFwk(@!jO^v>WYz`rC1E+) z1Dv^^H5vUTXyRa;y5}8T&hR#ya@xdn;Ih8W?j&MH#eU|znZ(S3z9nnf6Yq|D>XXXQ zdjqx(erJA1JYDi^lUJfK^j*Qec{Xye$%}}8-x;ZrL(Bt@$KfqOoXawwW?bqjCQneG ztM~b=Kb_Nd_)pMf5`9gz-j!zAY@&2fzS84ad?eK6OZ)K^>7m&6Y+_*4*2ka4cPZ$W zMJLJB-adZyl?Pr)B|{(npw`G}TP34WM`ioyk@%*D4F838M2skz295e<{f=I(p7^Z% z1RWZ&u~Ah+Z?~E1|qOp>7w^Zc8%s(ek^U)H~zLh|-8L9=%}+S7)Osf%NC0U z8!Za*!k>@wTif{ApTIMV^+IljlA$p-{#%}pJNej6+@TJS9l=k6*o`lrIj=33 zqpk|EU1!T4B~Bbar=Fp`QU}2~gA8#=?MQV?jJj4aN%3%$I)Ts89Bvynmwd{lctd`{ z@6iz(a=|?lg}9@6uHHFI8{XG)upd9GQJs0TPyFGX$c|8dj3bB@8pp;{iIus7-&XmC znzAv${f?>KZrr+^tkGsWXjNgfDY=2%?lTI1@`+j0eNl)D6 zeiq^~>kr@#s65OQna^krYz*_rm}};)Sr;7`6)eBWG&50dc8|wUbh>9-e5J-6!CDe> zer(GI{wK-rjAD1+O%hKgNEhO@&X#Fyc{w!SPyIBke%kzPCm ziu|l~Qc5u*n$t5W!^~^j3}sr1x5LF2m11fsr3yLk=ezda$suZgoq6YvbN1fr@~me) z>silw*0Y|qme*}d_lXV?7M3UZPdz^3^Nub0C)?tSPC`G<=VUHCUGLDAwji(h-VJSb z#TT9OHdtDBV!g$RC|!SpHB1}9-4Xb4ddN8Xz}eWHr6*vgH~r`w^#$QUcs=n`2$!BQ zF;mpSd2uZ#J?4YZj6Q65i?P^Zo<92=Xs3Rt@n}*HVR>~~M_n3K7v@=!b>xGGi{W3% zr958Gop(RHOkSf~!i(lC9`cUG?u|Y4VC4zw3=fw+;O~)VTt8SNxI8BcAD)`y8lI(2 z_*LeO^F?kY{Z02m8^2O$dd4`P&Ku`XfSc(v&;oq%&*(dO0(naQ%k~0i)HpA`VGMpI z3GZ}-^(M?8=lybfI|I6kpM3h7aUR+nFlXcO{DtrpEcIE@Lj8L`brzkd_fc?MCi-Zu z(PlZ3Hp`2&T5iPn2oyQdw4F)}+NtCoJ5?FREAIFVCyL*SnV*c%^}u7@*x=Pq*mLoH z%0S+n`X0}6`sIYzWnbTHd~b?cd1wBVlq_V7PcZI{)q9?vI3zu>UtzXn`7^s2KY;tp zH-IXJi97(caIrLNj}ixt8f~q(`@loj}jy zu4?9Eh8Hdzn06+Aw~a&hA9qX6>+}ldqd#nqznW{UO4oWFw`gvU9AA`ESkJgcxvk?? zTDoI=`?x`TXBXv^Ui1fN=zW0xY8k#dvrW`5ay?3;-<#u};rt-K4~2Rcu$D=lN|wTx z)5NRt%^`2iFR{-6Cn>&!{-5hsI%ER4UMU?~aip6>I^_4MPv`OPWqOy~7hi~10zDOd zcBn~t$UDi$&*nTV@v=?ZoW4luXyb-$#Cx0nB}aQk-^n}-JTD&IPvOH_^;MN69tQSh zf(@RWsdZqw_=mPP<4jnd%AyR~@br{@7Ch6vNVK0sd5P9i=9;M%eIrMi2eIdIeJSmA zuQk@R&)06rweroHYHn-XiD1W%beFEg=h(DgO!U|qzs0m2c$9;e`pU$0z7FE;D7(ds zWowRk$e}amD6LgaO~40*s;A)ejGJqT53qy&f%0n92l1VLiE`k9H-5`gx^h`7HUQS5 z#!}Zw;_bGm_Mdwb7s68t6Ys;bB|iV}|FvkL@Yhv;c%Ql%J{SLc?1wACe#?_GPTxI> zUs5~Z-(L85{ar0N)~;%|k6CGLpFU_!ppB?=9c}RAPU@C|ht(E62IuTC%B=6EF5<6% zHZ{H+F+5v9y!cFfE1oJJiQE5tPy9@OLFn->qLJ#(xKqRmK(5u2>kzc*Sn_Cw#6SCvj2~sp4HON3+k7t_ zC~7`0h@VS)PEf!9Yi(HQ9ig4gZOOfck9C75SZ`+o9>lhnUzfajA3AOB5OcokfipGS zV? zp)0+t?@s0hlpS%bS?}nKI^mdzo-wOjX|eB=P-cQVyodFS(S3+|7vUd5Ub}=cw@3%` zQk+%hFa~Tke95}Mt9kE9_=R;##v{LYYybRp&+QGDUq|AxN#5%wtiXI2Rg#AJRj82>+nlH3@jnD}CIv{*z~d@2x!9za9B>kgmQr z?vO4rlkjoCFXky7wzR>E)QCq2`v-56k-NX>GHI=sm4@&lp29H+ypQ|vBIkLV4F9VR zErc%#%DGRtg4dr#L&g|$M+9k>Q;(^N1D^8S$nPWIc^S`7lKpNoB)_jlX-`J^%Cm%3(^(6m#(xOWZ{c|Vv=DAulY{?C7ARe7@=th+X?^Y7_UBV`WeTM#W zlCTtg$M_Y!-I#)Lk9DrBbkkh{FZ2sPV6S96S{dS}IPjAW#=4cE#SW#TUVZuw+VqpY zyfAaq#|dZM+Te{&Wc1lO;C`YP*~3#Z&t4l(MZmufzko^95jj6IwPOZr4)MNRr=RXW zkR|od^t*ybzFjI;yb<8B!5`y>E)|VPw|+;FlYpiVC|>1~=W+1`acdQ(wmILk*62OJ z`&{8o{5=YDth3XS)1A{j(R?SmtKhaF8z!YVKYtrI<3n+>({k$9k_MdrkvbS%UVE(i z6JA|EX~{cTa2d0N_qtBoGxk4xElwKzfu*s=+w5t-$EQ@U?bOx4SnSn#sg(M|VU07C z-=z2y?<$}2Oxd&t{sJ?vQaRvm^Ie~rV_C8%qL1Xz2X()f%3r1O8RJ!+{6a7HHfJsR z&{6N}ZUOz{6~U&Q52~C_Te|SjXIOijBpD1`XYVro7S(%e$#J=|n4?HKctp_-SJo@bNgW+fg`t@}ti|54&=0#Rm6&-q($va{YSthWPsS z?~U&L*XXZ*Nl(pRhel?ch#;eQn)5FxOTIkTx@xDy+i2EIiq?;(P8(Y^2QPemx{;rS zJ@i|>OYi9+J^7yrbq?P5secZ}4cz|^XdB>dDPE`C2V`UdqaItF}v*~6q0?||R?1*`QP*$8PO-3q_Q>@}dknQyKSII;jeyg0DYYAuPB|;WbiG)jxHaQQs^~xFa9OO zez-Z;9~DK>vQ=wqEOi zwTiHuN8Y3 zv(Xs#PW3(Y_YvQA@aoI`AjFY0@ds$P_3hU9{n*+#Tc~2+4Cjn_Cp;s2sd)3_?DIsA z&&^M7d&DpXxK6kd6 z!y$uWt;q(-%6F*Ul}>XD(ybuU*^dj^c0(Qs108Ui(A>F^?mzVoZNY9qsv2PC|8+aZI&lBN&oiAICy_6SrFX?ETV~4{qXx*qj z1fLnbi?}Y$$!R~%!fBSj4fH#PPtcSt(58{_-SY?|JUC;fnYK7~0eaRd;^d#*!}kKw z&9SC`ihR9<_Xq8g$&-J5l5>0F_B6>F+9es3_0QxvqP9Yo7Qa^HPE)wji3fmrHSvSM z+$>m>^#k!4&+Gj%PQ0dX?r9jlqo&_F$o{ufq|Hi2`|X3~jv4IA*qa@5M-5NT`FS!0 zjNc0ec!{4rot|z_<42v3ah`;IQghk>$FGA!3OQu~C9{?G_8=|yF3?xI8>Amx8q5nmt$Zp!m`|=G z{U@M1`1x`tuyGjQ)0W)tB$Flu@IDDVo%?9=lE)$bKhT42rEkp&;BWKsI~K~yEA*Y0 zX9?w9OdhrK62)!zZ7Q5;X4-Gc323D<9c#u{RbJ5F%-oamZc`k27xJtN;R5d~#Br}x zgM)n3xp!sb!83q^kKJ+?nQnB>Ud939nJ3xM?Z0}IM(>1S8w~a2oGsk$_&0y;YgFHz zMc?fS`tCORZo=cGl~Ai5pd!~|7;M) zs`^iTJGh1h;V+9<|F>j=&RRzwj>^xE^*rql%bBsmY7vh!mZXyC!mle{as)aB=k@x& zap3t|@wjA*cvw0L>;G-ob0gSB3ncs0*GRWFufOU{o*KgTNiR{}wq&!Xbi1r(4?c?T zW?!k+bNXjnW^aPlY#8J4L$~VVzy)W`BN&5HYR4F3p7y03?@y%B5p$2{m1SfDVNd?0 zvU;>XqA?v&nSXUzyw;+H1u@hO# zSlXhv%xuoRzu29O?_`X7P89C8vax>JQoN53 zU1V2+J7*GZfqX%^H>`{&G&g#;@eNg3{y4X{obraxt7ctJZ4=D^s{|h^+Bm`)0{HP+ zksj9`?pv~N$^JYAI6JDRdfGcLzk+x2OI1#_SeIYR7*fM|HH*_TWK&JDkD2qd;ft2s z-W@yO1;;8oJ}_gd=hyrAdEkL;Kl(%Jy^OY>#MdYNn}w zWyYmNgOz+&wnEo~J-IY$1_ZV}!LDCf;v|Z^h^_}?b^Q;A~ z=3u-ZZ9^8^es%=7ErR;Uk?R@;i zmSO{6tGH19ir?9vcs=vbnytuN%KJz06XzEpUye0DaKw)NZ^=c$p>KG)W9qNHmkxXm zpw~#Qy}(mE@f=U#`3%p>L2M1Yn=|$_UI(&buVh77f9kyw8hp!-d*yg2N1(|fpD&L8 zG}My`n-5IdELCtmOK(S4~C;c5AP#q>_yHpup_a)D*^kWSt^ z$1{%{?-X~nI+9}#cV1%Q&vmrG*C~%)E!;JqfNx@w>B!({TTL1nv0VIddKW(A=vYZ) zhCc=|c8eE)#a&glRbW}?(@szIS4?64@2M{r`n1ZuqGylMhjL%?WuA1iqhH<5xa8|a zw#IyOCks4(i_(i<8$4@1ZSraM@S-cXJlN)+525w`ME^&yVd)$LKTN*FrTDDZD4g+f zmdfX8rRTV^A92^I8>I}{O?0mbwwpycofpnN&{>j$q^at?$=HX`ch0&(@c|AxNA%Mt zz@assdysKKcHSa<@CxbMHIH4Kl@A%~`nd<`Q><@G22R~idt&<^);rM`UpXP2azw{` zBMst+`nthB8Qt-5_hecPbHo$Ey@xs8nse-84%A9WU(Q&!C+?i< zRs*LPzlR8X8prn6fZVx2G=g5(w~*(|(KP3Sj?wK!UR|bYh*hy%0b%stSC;wpA2R+Z zBYm@B32|C~*@6swRcQjfwL-Ft`prad#ZEHqIm+S;UlVqg59{UsP#F1S=Nr~prK^Hd zEifNZIo!dKMh0~9zJm8xm>VE7hxQ6a0IxhKt1Q6rrv?wh<1hapC_`h%8D#*!2Y6XO z&z|GNqmP(*snON*Wi0bJt?lK_<6g1-d7REFY~B>$c|W*m9;Z5OoCofNhx0i4*vmhm zZmWpPP(0g8}~ER`@7=*oGJu)~qobUNC1(iso^AJl}zM8TLtH3t^J4C96_cESk@Fo5ew#7c0c%fxYzI(1)+@3g;Z7a5-?4LouO-JeP zli>~QEuOKrjMBgN(}wubxWey)fu69f-`WU%DdBgCw>fLYJj?Mmrwa1-0 z{0=0$qu{@=J?_=>jeIrV=RGjnjp9R>*vdIm+y2g~B`%g>pYnRE<@ej-9ijjnv0 z$AF7$%_c2*0zZo&&s>uyp*#bDu8wY52fQJDE4;S|eDGhY=*ztTtTWbeY11$JJeT{T z<^p`*COOIT^RjL8{Ku{SbBo5<(KMAT5#7=aj+JgeH#YMj>?yoUra+^l!ZO55pT#$W zzRP9E=z2V_LjTe8qtb)&`W&G4YP!;5y&xnQv> zXs5cUOb3z9B?qMv>r&G+cY_*@-j<(U>V6~0$@l`u=_6uW@8`JqHI*Wz* zP~`4~S>5ei*3McZ@6zW>bzT$v9uchunEUFiKAs7l37(~GRw~JRX{+B)W5Q*?^3FK# zOL#DUx>>krK9s7o=MDk~*?jYB26^2hj=-Cy5ze1BB7W9AY!o3IMEk+91N zi|L#9H0hGUa|iYw`siv<0~DeoJ-wB2}XQI5Vh#Q63!ZFME}x?b?##Zz=Jalnv{+e10b8?0?r-Rv#5Bfq3C z>8=WBRS184Py5gq-_5;w|65~>aAC|Cl>L8z`Pv=tOAoi1;yO!g&V_3pY>hgRfkt?1 zR6KFV)knx131Io(GaX}xQ5f~y`+CAn_KdS8wb4g<@a^jyXKTM?v;qCFhp<8RRI0B& zn091$jw&qVWA(jvTUN2=-<&C%Y^|QUm$WrX3;kj};OKqL_UPkd(jwjX#yGCH5fo&GPLbOIV+A2P8K}l~>3ejmRdO{khh}cwf%w zS)?~Qy~@)ZL$-#n?7X?uy=E>&U4psQ*Ms>}r|7f;I&n_OUi!AmzEryWK$)9ly@vAw z;8E@&)H(|Ik3m27L??Y4I+WONwB&w17J5{M;mWHx)v>dvZxH_LBhH**BKd!FSWfTY-5jeY2bz9D%iF{yQ;%VL7~^q_{&QUUC~tUQ1^9^HRK^(S_$tq$8ZIu4lycarK z^n)VVuQH4Y!FMU^pMwe$o$0@OL!Jd*k!)lwxt}!=SEzKN1F&_zubH1v4^ublqIq2h z>EzcWTEgpQt)WDD2-A5-iA))5rycHU@Gy3(Kpwmn;tHNeO@Hy>J*04GTVUkroW4QM zD*rL`{XJpjJYU1MwHKY0x*46dMC~QrhwmR#UR?Q+MOwovmkbE-zlCRk|31WAN4AHb zv0gC6^z(ci;0N)BZYJGJ%v z!hkO9yCg2lc+s!&MVI-=D5ZxsS#);w2b-{$gRld9%Z~9Ief#ZcN037&_0CwgH;*Uj z<3pceY=RGlKBfD%c;2*?r|wVV?4qpfqDNTY{{FdkF=x3JuRh-`-Zri&cZX*iIV5`N z*S6A~Hae_*WopUjuuUu7iqu(NT{rFZI5u7ELY#Yzt`hSqdTp=%&RHpR@pN=kX)4lc zJJCYPR&3tZp&HB9p?1?sx8Kob^1(U zGwbk9eFJOQ-ezw+J|YD>W>}FOwxcuk%An6l^*^;O@X(!y{&~K111Dhh7w%ApbptlWqv##$kT$FlXnNako#S); zp0opeNTYQbqbn#KXFJ&7`g^4f+M$T?M(Hc@-T4!Fqu^Wwp2lYgI-P9qVB(29g+>uC zm34?SbH-g|eShW~e@!dio=C-@@9SR5is&3-ePe$sxNBiH+Fp~cAiSX6VNZNQYgksa z&3}&uJ_=)~|9HXOfiE5JN#>f&t1ax|tozYF8Gqv4ZN@L_J3b#R%IO=tBzvrBeYq>e zzuV(!^pbQ{Nnfskc~;~IcMMUl&m$`<^ZK#;i*DXg)VG3g+2uUe|Fr+02K(Idb=dX2 z&1R0=FcCeqn%_ETz1B~gxWD4yI>z3Afwu7QZ~FxUo%%C^i5{^6Sh9^^ullX4v7ERY zO*s`!gKObO)&`CcU&`|+Y5ta{&Q%F?ktvPbQE@GB258eUlFKUR^%;Gqz%xPqQ{=@D zszEp`Sr1-HBYY*R)Aq)_ZQ^0(75ozXO5x>LKl&xlr0B(aIrq~o<0n%?C|5VfDP5bD6}~(q);SU{gAMFKaC}&qLrRzhcRNOZ`54`i7tnTQSdCM4W8C z@&zt|M~+}u^~5JTT*kvPcaXBrhtF-&oJSdqe?t+Tj$nfCT%)tlA1P}$;qT`e=&fd) zH~WIY0hxR{?dL!J6yKErE|&zj2nTZ>j&ShMiGa->8vCQXTg0i~Qub-JtM)x2i%u`d z%R5eZ!9ED$lmnp3D3+2@KrSHpRYGIzX&{|EBiW>@ck`& zD5fkFk2u!kUdlxOJATvsQGDJinmWZZ@WRh&^A#cQ{x$O+=}tZIIryXYozgy>$ME&p zKJHcalBMx=u`TSV=fnL*v zE8*1xz#imB9`;uK0$)WNT~cRXSpL3I(;h$l1AVF%y4_wB$>my;F6WFK!WPH8Tl@Ub zBe)Z<6ng{nZYK&Kn01D`=h-ohVI9R*K}WgWzT5V&@uy;UH_9gF)FHo4c0@QkWx8Vt zhmba^_uAxC%|~n4SMqD{nP}#*$P(c&*R!xqdiX%PHRCPI7JcZ`#oU8y&P(B3=tKQ8 zWUp`wGcz2{G?Q*U_*==FfENo?Z^7YA>KmDt-^d&|#+?4fmfX{OnA_ec`mIgQwM4&r z=VL=*{Znx8Efd}Dc$Y~d+wqIsL(qV1IB|Pt(GQ(%lGNUPZHip zcq{dqm>2#r!e^Q9D%}zK;z#+OOS)B^&>%0}hX_9}FWm?EUP$_Jo!mv~hi~ZM`@Mu; z7>fD$I?_A;*ljmx+hY9!VGlTHU z3BO`geWnn;#LsW)Gl}o3NyohBjQUg)zBDf#=li+KT5^Z?kE+jD!rhQJ{>pxn@-`b; zP#n|=8~Kzayx(Zbt7zClIe!A~J>Ye8Aa3S!L7dv^@E~!Wg3$mkYb|CZ@026NA0d9N z!o>%`c!qOa{ru2|IAr?a7Z%X=ro8GVrT-RT`>_RWBmK|G>+_7E<>9@=r^2+P8{m5r z>3%}GQa{|_h5wJcB`@7BzByym%r$oOP)=UjKH_?S^<+4w6fMN#(849$8^*b^VGDi| z3BtCjK5A#m-mHERlqH&ck2JDT$!GFG^^v@|O~fUGvPB>K|CiI}a`z!m?7VdA2@iQl z>F(#7G3f9$omCqG9zM{)cPHU?&~_gO-&F<%coKIvank}ExnJi%M;`oE;+UVvM?Ck1 z1tavQO_t9h@0YlX=ORD7X~!=RuDd~gvp$e1hOU%HKRtW}vSmxidxTv|*x#_O<0!A{N8V1t#_HauymX5RTSU4g0i71i z^Y0Ba^#cz5_V5MRCOPNnzyJDmr~fZk|80OqK^q$y(f^nG{nt-7mG5Q3 zfpaW0_meK<^GL-MwP_vk;=73_0Y2mcn|ZyyS=0xr}|SizG8vwsoJoD z@C5NY(JxMyLw|$2A08zD7RvH=?>w0;qJ~$cbxC1N%zv392h zc<$}2YFZKYDZ&mD_VdoF4b)*sda2)MNJIF)6YuwP6MyI@d>~r-$=!Iw^d}^JpGUr}%z2z)9=i`8d)4n?ic1Ec$;_44m#OkA!qG@aX?d ziGXfONB?gsCEY)bw4+H!|8Men&9CDvQaoO{x{9dE9)A*&jY6`%L~qEpT8x1 zVP3kc_`c}PIN2f3T#|WsS)4AmZXSC0J z!k6Two6Yyte*vdSc{nW%cxK87&onIqr%3@$=6)<6wrOL=|0XxDES>LrJvec$bA3J^ zGyXTN$V*qu_h-?;hsx1u^Wbs+Z_~<=bO&>M-%7gUXVT)}p9x=;m+p^ze}!}}ms^Rv zau2>rxR;mi6~5cvObd&38rk$t@6V$}2j_ykFsgm-A#A@-i_q* z-&|H{tlTQKD63Dp?1-#vLf<&}ubmZBR)k@^)4vWr1su*r9W95f82iuZC#S!khjYqr zzk@%>Yrjpv_ojdWk5{Tx4B{BZDI z!h7@5eVy-}q-z^hF5`bwIxpRweD{;?OXXH^ez`4#@6Jp2Wxk(&3tEKj_vt)ZFfJav zWmNm^CG67yEv_4d)AhjKmsgg{_p{*iv2v>llLfL?^j!^-51c#yblsq9JV3&iWk-Y2|qn=plbQI{PL%W=I0ZBoV?g; z51c~pPX@Rt&m7`TW=PNYrF*<9NOx0^_V<*NR+&mqx>Karehuj=f~E04q`hF(Gxm#L zZvrpk4}FSwd?g|_LS0%=w1h&JX7~_uLrMJecj{K z;o-J6y5WJB7)MudF3R)5$qcGsAf?$?Ch8m8lY6>0Z| z<@3HC+y{gB4!(a%ny;{~+9>_9CHJb<6l#F`lw^R?5$}=iU!}YH^*?a;@EopMy4Qi7 zq-paur{0TC0cX*t@L5SY*y1jO2G{d$Pxm6w%lMYu4lXya_xL=0E53{VyrX(KKIGcR zWlh>v)H0Zfr!5bA3-hBAt?PFdS@m0^*o~uB{hFCJ>-nc1?Bpyu;jDP?Av;;2vlKZC zO*md)MOkZ`vJc#@6F`%Cp#ovGtw(ICI>gfahLz9O&Q6W*rY24B@%xKoE&JXP;vr57z!ck1{n?2MEp zK0c$4M@H9?yGNE{`!aihw-qfqqm5<1?&SQtpp7R5Z9F}!qvBa}JEM)|BjLAk_C@tw z&c(Yutm{0!iR+-Q+!tVIF+No}WeI(ozS_MKKXiwCFK*8iVYBi1?4F>E80|l1-{eKK zzgx(=$5ZDnwJDx@zDzv}po3>{HvZw+pUBPwO_;l8|Ezx7Rdo>EAbUc637l@`j0fS9 zC2Yr8yoY##AN@0XRC6h%0k+}`=Um@%;@he(#Z~U6FeVwFIV_Nok;^DJ5ele{3 z`QiHrZ!>+o^1u<&G5#-y=im5u@;iRG$^SFL@5xKY{C|0;a3fEr@bKd#;~eJc{J@I>>HF3=Dmjan!NHf|KCRXa7^eWz4mJj(7y9UW7^lO zV+)@=Uazl?T2>w}{sVoXrU(A|@7Ry>$DPsR%pLL(VVueHg*a+_A*iRppSuaL`7ieU zBC^+xibL;izC4Kg+kF27>Zdz17LL%U`69y1{NI=N*~)om@MW_@{0Brs@$&-u3jUws zgA#m4FXg#N^K9S+cp2Y_(xB}8JD^QGcqPfh{VB#bBCWBdg}V`DCk|ndrjqz|tLM1U zC+4_u)_7zaza4txgSLF<4$GOOeM!LCps|B|_}Lyn28{NH(D~lkYQpzX#<##f+?Npe zLu8YbRZrY~p{)QoSF8TxsSmuNJ3yj@8NW4!$?r&Th;s=q*4Qq*DT6Wig`bE%dwca1N~u4n9NFxl)%6%cGh5To_x2f19qnHQ zACw%Sp7@&=EBrT&sfeD`t>CWrWRt;rjQ2$FUa04>##CI-bR+G+^KfH|ecOGf_9R^0 z0sZw~f+skR^){!ae{o)71NOY-M%J)bsIn{S)PYm`_V__d_X|V+BBhPvKbVO%>_k_w z(0~5@DDTX14R5f2m9?QMC!tRbbgO0k@Kg%eJmH^zyH0(`>(<&?+sc=&uhu(z8knXZi8Gk13osoOVo*7QVI%$rI;(!X!`aS=Y~eD~-l| z`XF-Y`NM%s2>YN-AE_d43-mDiUQPcq_L>*e|IyWqAOBkE#(8nf|CZ=UP-X{CGq4ZhD)zvcbM^wDAaB;gAQ`vYMe(AR;!b^D4nHNM~h?-y;H?Z&&OIg##|%UMg}^_nBS+$#iZ z*-&aJkF&$=BJ{dxzrmML-@xR48}YGQ2rUlnDYH3WU%X>+|AUQR`=VTWj$J_;eK0eJ zJsG?!eTEs5zFRxYxdmfdCvAn}}S zFm;9U2YHSYrn)s!j}FSJH+ULd`?=G=M4p~z-8C4;2`B6nIG*T6#*e^mwg{7LL%7kd z&pjr3<;BzgpObB)gRnq1ZB7uz_-_wP0H3bp6!xHZ53pX&{-oL;gUh6e@Q`>$yn_$G zaeP3+J?hZ#h5uYncz>zRbVwCecou#>eVk>|cM{z2sqBz9fUh|z_)9P z;B@A}`B<37ujj@@P|v&a(p;`I8oygv`_D{s{BzeIvemD?kc!zC_0SIgO?|u#*%3j< zO!eF71tU5~ig6R&_xO#-TAi_eqvmdm*RKq4Zn@zBGoC#66=eLaguh5#>+-_CK=>;E zTVup?pX2*0qMJ!hcqT|R!@6c zXU5j!m%q9510H&I?q7Qt&-3uOn)LSoOZ`kf8~*s_!}>6Bogux#bwkprkL1r6t|#mg zji)haJ?#dy}MA2~p`8iwrPt3ywW7fUL znpfuGfQ|RebBNRCJYdx^_f6JA&0gYg|E$sy-hQUsYhDZ7DwU;vN&M|9)6m4%!PgN# zPI`G>eZEClanL{6SJ9jT$Di>{|9-9@m@BMPdct=@3(aRkT5KW=--a{jvxTrfiaw-+ z_Rl`Sd%kY}2w}e?3|i$r4xV{(sJVx@-%$37Anzlzo8gCm_S=X@woSqJ(YHATqXP?k(lLS^ZsY5#m%B8`i&Kge_JYAD?GC=OKgm4(r!Tc^S%6`zsCY z_Uv7vX_!X15cW82zSCd(eRezTnwDOPe^3#3EDUhZV87;wj#c#w%}>KP=GY0vb8kZ! z|Nok~E&0?RDQ_U;P1V!Ti#^sOd}3Q#E2qv8>T3yi(wH^u0GE5gWjFc;e$iRc0K5F6 z9R41+-(Qj~5|7}&{B*`n89n%ycB=FnGqa_fJ09C>d-yIl58Q7#V+zn`GYy<0G9n|^ zF%I1#Xc%92WjV`|$-H=11c2<8Nkc8j?MC zc$_5s2Ev2BFB$SI>wnG1NvD0NH6cF}{z<}5n(s=Jj`hFhQ>06iPGe(>?|bm9{C@<$ zrPEJG|92U8hPIc}DtYNXNVsH(NymFBWn7|qsr`A^`9J%Dc(wbxNdry8eyjF~h39zB za;)ed)h^N%(JifjKWB+o#h=PYnxpW+M8WSO4LChVyW=M%SP5*bI%nV59(6|FpOlAZ z$G}<5{UP4qPzf$Iv_Y_z@C<##$UD_Tc$Sl<#;?OOzXE>P|5?v*kuT5EHyT85=;aRZ zjsEYNe%Zo33+nTJox8X2Jw)8J0(caE$9)5Z&HdnWeJ6W_crFk6v&M13ZX`YXwHxLJ zW%p8crw>bbJ;VB+?xyS0-0mH+ZOZP@SveIsHT2D{iY9zTEzbHkJYJSZhaZU!n!l0m zNykHw`KzEwLmsYc zh(D}+TEpVp-4^v%`eF-b?zYU}cNRb3nSJ$)|8>B4x6Wo`jc3Ip6H;?`;0MKdyXS40 zU@zqS-HSF=rW|yo->BbGA8f$StfH<}-X>c*Yn-rhe&|!re2#Fi>a%&NC5cAL_N@eHS)y1TI`lVK><67u zo@tOzw;jQ*(~@qmvtedg+^jFo-R8L^*{+M$4HjCb@0V!NGsv_Ln zO-h{T^qTUEpEL)0t9BP%2CU_q=+h25U%lU#Pv5c)+)Q1o4xrn+pXH479D5sAm@wb} zZY5!_!xvu>Z;3}FcL;k5KJf_CI4WA)O4y)iL0BjoRuT3J>BYm^6C=M+3*Ksjw>qfn zH)*4Lcz!tGG3dAd1?tB6o2ih;Bzvjze)+Rj@~*ahZ*V3je6{}~tapy^SRL_e$+uJXx4eA4gd?|3Phs6I5cVz6Q!-?2RjLx7xJ2i8 z#-VDhXa4p8du>_@CbOA%+SCE$v^g&X{=(llmttPs0$;_@5qikCZS@=*{+c?#I>2iI ze+i#9=u;yboA@i3gNoL-6CSP``us(H`0Mr_a6y+D5FY{S1;NVWfnM_70?sS)!gdm- zx*Z{m@pb=P@lRfSny}@nCt)GqFy8EEZNnZwZqoj?wWo-KgmS;#)D>+px)ta9KOO4T ze~oV|xDT(1zunq|^f1!3WGr`gpih>zjx%SPlxThl57o5fa)0Vd+G8{0&@VgOShUZ# zDeLU35Ff!I-QgYJ&GVrA%CW^1Zs+N*wdJ_W-sVhub3fK8_H5G@xu2u=*XQeB&_i-x zeQOYUOk|8*MVQX8P}p(8SXXFRO5T5$Em`9#^JH|RXIM*dH5XDkbN`U?adrs0u+|Rt z$J5g-aNGa9@_F61^u|)XXD*Z8>^4zfv$t*EJ1lGcYL_!IIWy;C<;j?HwkdO?^vtl% zX78}{*%EZ}5<{C|JNN+SJ{euN#BK>_^KF#{44tjSJukLZiQP9?GeJ(!hw-Us$^Ce` z=6|A<_-+_3v<&k9hVTgcaRsX-T=Rfu5}o6?Z)04`NqDAO_EUJIg|;kehhJGk*v-5+ zR%AtH_0O~-?Qu`%wOz{j47K{c!?VpEHI<=X+sbsU&ZA@xeCaNwr~Q=O%v=>v~$M}x5CR&D&FPsXY^Qu?2{UylL_ zJ3=ng10Mza9`caF0v;L)==jt+`pYdwPpz<7dz25*8-rQqJirO>dm7f+eYtST_zr3O zL9cpNW3Z9)=vLeTF_V6{S1?<0`xqn5TD9gajDf$NO58rr*Qpr0WD6L2T74@ozjU5w z$ogOLeMk3b!gsREnf+M8o%Z978apDJ9XfmgvgIZ2jIn?# zng4F&d$7-^;whC2ZEy7V`7GCd{Ar?FkT*v9{p76)^3GCT;Uf7E;Nu4PJSJFavo0nDpe+`iXG4#mB|P zZjmFp54V0@!mC}MNY|}Rcs2JY(i0WF`W(CF-UTi+aEjL~uoK^$nN2(}GsS*9XYASv ztMr=}*fr7>?&l1&wVa9eP5RmcbF-WeixDY#D7S8m6-pE>< z6J5=DCxi{l%m8K-m_q@~2Mo;Wz9G^ke=yfgtOoX|JSq5fY*+FuaCq@vaEeN%ArnvO ziF}L>*iFN>Rh^FuZ!>3`hUFkx3{TYP*Tp$uoZ(4gxDa`_10hGgiR3#AUi&aSrSrW?)0`PEo@+~{IJboiq%-GJPo%xIHmWfNtN;ZFlRuEDt)ar2UMZFkt(82jA`v z48f5viD)9-kMobc_T<5pvb_i{`{bf~t#qt|k)hRU8Ol%wYgwEzn2w1K>LcsMTP5qq zJ7apz=mBOeU}A{wem5DdEHKQ zA?*vi=pNf)+;Y_}1=43EFUbFBIPUXq&Pfv-&SZ*ZXffaz9QJ_YC>&D4L1Tj%6DsVK z>@dC#@~f6m2N5j6ffs)^z&~Ylg}D21Zj88pLw`vS_fKJ*;KDE8Wen*h{A;Qs&wGOB zS3{gbTy-9OFebFW<>I5Q7lOX1Vt@5|*34M%gz z8u{t?`bqOW`0+5t^&!?*1CQn&r0A#cQrbO-}@KS zj;WM<8s6Z!ETl1QRVscFo+^7jeeCtPrSVg=yb@Xt0>v9YGDUYQ zbKXD`?PYv5uxGgm(emU%*WAGcJ@0Ad+#`4*CR#G5G_;ILAAHDO3=Rq5O#M$Po!|)v z+U?Z=s|oo$^yd&4&J1b6-jG1PSKm9^E>^y-R%hbA$=G8rVgFdGGj`o2zQ3k;2pHd? zujo!E^%EnTf^d)Ux^1=W^-bYhfDFp#p^#=?$~Cf$`m*=?L|ebr-QyvfW3+|R=*Qf# z$2V86Zy%{OT3%r*^;rEbk^3eTnJ@0`pFH_eP~nUGi#j{8y8BK zLVjPw*jnQE%YFB$UkV<2jPdu`n^>MTcIRlS8#qj9Y8Y z&vO2olc+`So5$E6(1vwfHv)d+^)24m9zX1L@yuA)FYQSKRBOx zUWHX79Rc~mI=mI0!~QG55T5i?&Plu8<8Br0lW${gu^V588qP8+xwDaZUy&o<-;ZT1 z5BqPf7vDVYx3dyIn(fg~oZ2eST9$`j6#Obi;kN9rz>PEYhjIIX@CfjWVy9`t?_sy_ zQ(K-Gm#PuH9&0odMC&I*s;y=#6 zw?yOO?eTP-WJVXTsrP5P;-1bY5P#eq;-@~qxV#&g7CC7*&B`QFHEQ$KoMqTIEA@L9e8~B3i_{LtFl(?39TFb@0p}1xpZUn3sQ98aKF#alT*5A%T}9~O z7kl~bGAZOy%4M$1xiJZ+PI|4r13qQ_c^IeJ!f6*}^_RI5Irpn}#7D;~Q2N%S6D5u_ zJU#3UK23pHFFhIB8{Ig>GX-AsiKyfkI2ZQBk(aS)o^b3bA&$NZ?#05JvE#46b?C(q zSB*!)Q}{x^BzJS9efdRO{S~<11+NN!;V%Cp;jTJ~pME&oiL`Q-Co;tQ;rVV<_djwc z`NSX1PDQuD>zTMWsh50t^{-R?AHTp>{p&@i(fYpH7I_-@hE22Xr=1yFKGTDY4fwo5 zZ4b>Cg8#dGTI2f%4>0%0@tx%C(?nas;L06pChpzDT{Ut({F&p(S>){nKK}P@thuz! zfTuQ9ZscS{JEDG$oVj2s&zIu|d;dFa{(Wfo$OKRPG#>g@h@bQ=oOwnbDNL16xBJ$- z$Ei}?;+|#SJIkt~Zl=FtGtHq_PFOt)JC*Gedz;OerZd+k@{9JzmiIHJXz%r;{^{L2 z@8sNL_+(OBVnc50bZ1g4vJu%nNn;A-)M{KwlP*I&kVlg;(+~D~n_Zou{vSIgH0&0S zFdnA8lojc5&{fN<;j=xzedvjGYMZm6mFVA#Z(KJtgGN!ti>T(un~Scuv~G14KWG-? z?#Gh(5>;2cwbI^mUcUvI?jBXK68%REaW@!TswVH{C5{~Vj3gpUtVOiz3GA!A*~Bq$e6%oI!hWur)xd8}B9{aB|1#UDRy~m)1;PhhW(Kn4>^1Mq z7D2Ow?+fU~z(X?ToOWoU`rR!*!MWL*wsI@lfeoTPkv8~3Go8N$-d@3InzenNv2-cF zvpl%%^l$oSJaMY4@UYci#8dKyx~!S&F;1Tz(3(L|$Md9%Fdz3aXEilurfry&PxnrA zihJj}MTURJdc~Z7$~)t8kt1ENRd9!L7^2Wes~4wgI6JA zCUwW$h_}ftoCaUPQ_}mtiag^i+2c3tz5@MlgBb%8yT)0y9m$PH+Oc=;;v5$Iy6pyZ zR;@ds!^K9)dBa!gm+F&}^^%+F2dwM%v__?OHVDR~n=VPu-f8Wj&G8p4xwp|N?uuS-L61q9s9XPk<8!am z)40$1bvxmx)9<6a5|yELfHrZ>uY7rKbn$a2qcQW5oy=9-Iq2%Te>};32b0xLp_3KR zsTew0#n4W4>L{OjWSCZ-HF+25O#RTg+Tu=RSCzvWsCALyp+s8mwwFpr7+cPU9?=YE z+~n7z2w6a%EeQK7ZMjJ2=g^0T`>u2p=HX^e$ey!^%Jfp%IkeRf;d#YbUxGn zd>Ih-KedaK2&ItOy%NlQ!6`cu=Yc4YDQsb$g_Q7%H&l4xLW-Xpz3@PoW zKZ0XTo0U=-$S5z`Wo`1b?x((|bkwy-eH$1_(Ma@xP8T1g&iQ%U$*c3V+e)pd^u~71 z>}x|;#FjE?cQO@&4#@|RiQgHMuB2^hI>x7C4y7Vp|b=8r6QIPB#`eYAdsdNkVp>Y1n zTn>LA`ZaOT?DG5Ar$Jc>jfr{bI-nc6`SMNWm~EaT(i0Iz}4XX$xYSvT*|8IDs7TXO(Gkl2R?uAg>KMR z1)9INwN)LsR`VeIyeI5RIFK8e+p7*-N4z=foqjPM8a5WiKZbq!05(fcdj~I0 z#q3yH_0*%lit_v&>GeD&c!EnCB}c|Nub{np>Z>74-j6&Fe4a0nU(c5xJSSVDc=5w< z8#7LJR8M^+lW=HDouB8FQ}$$)4IVYT*X!NXt=gXF&+&wZJs-FgAs)i%OkC^*z@*Kc zC@|}>rIz?GZ@8`zz7Akw?_b_aAJE*E^W>KI4#to%n~e?QyBj%MEa^pmAUloskPDwh z|Bv_}Xw#j*)qT)*A#KUem-o=VJ(*f%>YPk{F{rQcwHJOJ$<&ulP*?Wi=$@Pc(tcTK zwkH{jlc~8UMXO|LUXXu2XN>35jIqGnfh0H!NE4IX!?sv@_r=-CcieI2+UEA#(;6d- zJCX;M4U(4UHNe;NTH5(K<_tB0K^aNrkzqONfAt;UI?N9nERC^egI6!VodIr7zD3=h z+GpUWdj0ZJk3VpZoxmQeI`#Qwr*^a@Q%@+b%3qgEm*(SO+VkD^WZKiQd9*m@W|c3E6pM-)XI~le;*po@aT#|Ez#6H5uLmeQ|hR+E&H>l`@NaJiQ2VF4DEy8FL?V z2l8Iu`97;8fOA6t=f=MP=O)2H_c^o{Smf_Tj@2W>&|3{Ij|VVz^i>d*tQ=MLT5WrgEf&y+_Y>r zIP<&)9Q8z|HeDwfqq=~jk$VB%%zU5?IRnfOknU>amh_B7*9^z#wVa!WZ(c(hdzbd~ z6-g&X-^E6+@Trc%*RmGtSYw`$eOB`2b;bZa({|Hbo?hB^9e$Sv?~8fATylqRWA|!L zZdmrZ;r$KQz$@_hhHKlB8?M7{6ZY$*`j%kA$CIdonWG%Eny(h#^fP4TFz$s)r{6(q zx$zMgw3_DjOmn#N!>ZM~1!JPIv1lCATu)<|_8Vv%d$j7nMgF)I`NXCfE1B0h)knzR zT5VsY_B~!kdsQ8HPrv`X_($(Vhu)kTli@q$L)J-arN2pjGo~#fE$!g3C#ml^`Upya zHMxHgXJ<1`0$=d13E*9;GV)>N!MRRw#6!q#oym&LCpC^=3A!dW#qKm|qqNc8dzcsd zVH+Ls&j8>&GP3&I)SKp(>psuC0sh9`&snanv+fdGYg-QOQ%d_3(MLKoci?+0I!aRYQy6oc zvHLh%mVUKcvIHIXoBKI)*6YTXiLzDEjK82O*`O8num-h}U+ z>qhs)ya~`IF52*2(8~7!zdg(yMWfwCeaE+qwPODRI;|I-9(bo+iaak6O+*j%4fwqQ z*mLQt^a*!K0Q;js8(jGp`0pyUf#CMzgV5z=BZh8H3VAd3_I#SL4p1$ciH6WGuJXKY zr<}4Ypx=A+9h5gO=wGtW*W^YS6waO39)h2({fs5Z3 zU;4V>(C0606`WEhLOy-JpS)-Gk*Wh9AZV zwm+i0k`34=BdufSTHB_3n#VG)_ac&c%m)mw!l#Sj(ZwN;iWfs3ZSbtJEo0~86vj9= zNwmI?z1++Z{*X^==v$M=pNo};^0;ruO9o}(8!7(+hkdP9tz(s($C=Nv9kisTS=@`% zG`B78MX_nm^)}I7eM1i^?v8)+=O`L0!}pflV;{oLW{JLaf5o>C?MyKbn)7aWN%C8^ zIL-mLrt;(r^ayrN>T1VQMSa%8)$Xdz^qozW8}PYqT9{!D&KbvZycG7o8@bQi%*mx2 zN8YD-bufRPLtmxOn)%f!&6BrGXYBZ{?(d0QOnoZ16nVzq*RhI+pcUgvDn_0}`rJik zU6e4^3m?N*aEeQl%$My|_2b>vmQoq-Loz1m%%c8dF9U|90HxT>`wG;>Sy$Uj%xP_ z&BfZ8i;dQgT5q$=zTiB)d$IIx%IKx+9Rckg7i^Uy+6Zn~SLQ#$=ZUbMqv5v!A2|N} zqS}4){G!^G?i~wsZ>5#)T_D~2E?@VKO80JGnvSlu(j~;#2X&tm)OTbaR_#jnjv;6L z`AM}qS!pyMAzw{fqo;X^%9!{Td22`It#&P{uP-2d;tFuLXzk zqhWU4#@gE_oSF={2q%>tj4#F})<)+i|88rrf^^WTJ`d6LQgK1Zv`^<9@f!6 z@J{+XYm-jS4Yp;Cs9M{+!fRW_BzOj-8s~5T9e7}q~ zX529Ir$Xtp$Utl?R*BwUkEbo~A^g_*a!>Lc^ywMW)i!>FvJ=yup7u;K&frU7MFy3z; z;G#-P_!5OHz4+B%yVrQ-G1t?058$hV|B|Mi3yWB!-eQH>l+x(k$MM>5S_^G+m& zJ}I8;NV>Q7CfzTzG}TrN!GA{8&f-DKhgS^vDaZEQv7ZG#Pudw=UVt?n^W7W zbbaqquO3`==R&U6PS>{;(XCS6r0Jeji2XCNgL(Sn7vQ_Wd-b9d-=ZDr?ww;tX)pYG zPyCrXD0Zt~L7%aHTcT&!sik`7=>|{vZa{}K*Q-U#6Qt$bdspw=E4R$hZKNG@((E?Hbz|Z+fQVt3i3Ytkg2zWp|6S z&MG}4Iv7Vr{@aM{m^$ABk0oV8RzFsKsrM7IEvnw|yYAlh!268(z4R95^=HiQWkU=2 zQEgNsJ|iFV`3+ZxxTBvJ(#Y=19m=}DLNG7)>*IcyIDKCs zIt93llHKk{q;qKQwS8QgGbU}b=BGNWnQ7y<<0ODlPu}t~>tRO+$VVEp#suvOR1eyd zb#R9HE54WgA@DEYyD#@C zeUF4O=U{!MXPlSkbI`YrIgIXqiHdK#tFp!pPyb64^YdkQwzs3)ew==X-?iSmy@?v`6$=q*;?&Zkal9w)=D#8X*#Ps*4b58wdZB4j2#;7CWP4@fQ4d_^!&x+rWq1ZK+?}m>qQlB(@fUf}Gw#RzNe0&a+4!s}| zj3tsq$mdiEW67AmG?qxFso(5E&!il5tkkWe$D`5tLs`zcz-^Qh=vb+__&SDb1oMEw z_?((g`3uC?N(Y=I~et3Od^=qrj;!`TWO5{;h zwgi~vp*)goI%CbPDm#XB1zu-bzLX`@?IPMt_V+|9`K8}5rsJD2vi@$$pzk7o9@hGD z<_Zs;&&Yz3>^Karv`>4f{g&(R^4HMXMtZhq;WK1ujpGsUeHc8qfXimau1$Zvw^2BXK4tJ*5ODCwAo(thaQB_4ShANgVPvs6!PerCR!PmB4$j0W>@rNQQB z$>w+N+N4uL`Qw49@OP?Q#Zh*cUw!&~@)wY%A`iv|q%rs@z2K1lvb78b`VQJmc|Sxx z#=mrgxw^`@d@T#Kgy|i7pLquMzPFrbWAF3l*=tKY?RR5Ncl;BKW#QeF3AH0UFzow~ zh0lV%Vs%(vqS{hSZHd*RR|E9Wz^a-EPW1Zy3S&L45RWgcm19$JRR($JGd!^MK+hXA( z6606MFAh)Yy=XIjL(qDBDC=mCQ_L0d`JmmPIp_P`kc!!n{>2XG6ROS3m?62x-CBo^ zbQIh8>{&7IZtwyo@$#WMu6Cv_8W8%*($WPk91~=N%Voz>1 zx8?o#`q1{^h%LfJ7PwQq4YuyN*S*&C(MVkUk?=SxoO*T?Igt~^_GE8!_T98^vMrIy z?Jl+%>uj}mslI#JZ$*0;f2!?De?|5kq@PV01?~pOvd!vy!TVZD zd19^P5uFHoemykbTI5dQ{ETAiAlg4byKo<3R{VI=*An ze-bo-A|OU4LaAV5Pt=THH|?KmDpeIyO(j*PDiFL3R@3M(?>h779%e4n%QZJ+XT02b zyf@4|XNKjqmqmyX+q4r(+NN8y(>*+;9mb)%8AYV2_xpX{-ltANXzlKKYrVDZL=Wfe zy}$ka+u#27xBr||*TKeR?G$B~@H__M&c>(8bEUYu=<{6Ho{TmsK{|lni-gTM6Zf>N zDEyB)wu3Vje4K}#*)OM&CNA=@3Oryu75T2ko&e@E;#((^m+T*n@S8i$umjBh2qSj- zIqV@!%s$sU5%70m4uI#-N<2%a)-n`HXdut1*^bhW*rYh})-QZ{keIDZTb@ zk%xVWGXoe~6i)q1e|az70meI!<-M1mF7LetGFRg}yLewf_J1ZqADD0SRwLFZ0qhlw z|6T~-{9<{p#Giz>uz-_2B9vXry&>GE_{(wjjX3aI#G!KXh2Oh7%{dKiA2LrNe-j;^ z-_vyvzNZD-ngCrwN3O(g)C=tCQs(g;9n}T&@=~E4`r`R$DX0IKGNVw){&xrS#@U6x z547&X*th%;&UQjSe9xyktG#yZxIq3@zEwoHb9;5ijfPw)$z!-_w!zL9?J z_i=U*-!`2*2>*uX0Pd4;FUMWxPdx7loKt}h@e9tn11E5CzrkB)M%iWCi2H_|9n(N3 zgm3qT;Kv}}j^0B3OXuH*-?;j(Cbi%k`hF+-m%TU#e*W0kum@`5wrPGjI6lpWKB?Pn zSjR+vz!{vDNdx!}X+OS6ivDsz?2+9m@5k10?Stp&pTk(jc_n=M_7BUR-T(Ee@A2&3 zEc~t`en-`j`6v9$)%~T<4$y8n=E)or^gv6#g*t5L`QdwNQxG@5|=! zjodRgWbHf7*U^T^E9Bh4Jxub+J`bBhn-vaGcKiM(`WNzE-2r&{wj$dDZIJycdsm$OgR@;y#-4t~Y2>-!?TS3v<9SWk2=T4$)dFS5X$M>FA6!5oXQ`X3S75w?ZSorc=%-hH1MkH`E|eeYrCjtw|DNy78~O3O%II5= ziGAxV`c^;QJ^l5<1eZ^w|oPxF(s8^_-|^Co=Z&*t^6K0U9u z>BD)w&1dHIt_?X87oDBgyKbNQlXnOQ+c*Y%{3Z#%$&`hkJdSV8Kacma*ZKVh_e(go znYqGm@m)65yY9aY=C~&(`*4`IVa-wES^2FUjHM-m3o)kFYV3R#?EE9b04@G@2FKnUuAf2YPcT#6!(#BsDt~f zw8JvQw$$%cn2$UKxp0Bk5z2ty9cXdz4%0Q16Lat5cn5&rHJAQ=>V}ZBZ(VlG7;!o_ zX!N@s^O=DuF!LR~9P32Cj4g2P8u}ga>32l*yWOW>jz3J3?sg1i=sVEG#r2DE@>kL? z;r;sMcQUMgS;jPV{0GZ>XHd^yw|d66D4-kac?Ryks-7>uh@LC|3}+Q~J*awq>PyZg zPff^9{L}k$*YI4%pKi;Q@LUG;|IV&EvXy@Q{|(mFw>_Ah@QwR(6L_W@^N>sT{U_&= zeekK`A3ty^=@WPsAAJq)h|lbOqVRuR$-W-Uvj1xt^6(Ds%oEI$HD2^{uJI1Y8gK6e z+RaYb4aZB`HS4jzW}oY&A9HDw^3D_PeCG*$Y~r6I7N$M+PYq4{hW4AQvfs>IIty!L z@bx9{OmU`V|H}yHt$6RH*Zp(Yq(2XHUiTa5qs-5KGsNHLn!Fup)`ef=_Zox_^Jvfe z+^n}>NZtl2;6Jdg%|4y&?WI3B$9c4sLHNh_iNK@CG!Fd7et66y%llVnC9VYR^WeE; z5HtpB_)g^rXpCGyBm1oA2kWGGPXcQr-3?m&Hy5BPjvy^l!mYnVq6KgLe_1AL>*bq<059d51^ zvhjVR>g->@Z^HjrJkSmNMoS6owgPzG47;1Fn*F2BpokU9-K} zi5yc|ZtUq};KsLt5Wo8R1^LC^HTs52JQeJBkmG&IlYQ28W}$6{=Y^cyjgCWnd}d$e zc&z+FH{@4!em)Nyw)jyFJArK@&j<4h+OCDkKI=@u+6#ZHgZQRwb0O0&f!{s>JWdGl zqD`W`vn7M&crVQfRdD@yu;wMiP11ip*$y~Mhk7ME?E(E@!p>*qeU~!!3+RgW@(K3Q z5EqWtkI@U}CY+ZQp3rvwKmVS(fUel)mqCZ+)CpoqncEQd%v8oo=fA&mnv3-Y&SB2K zIXpFV9rjoeJ2=<=1NO4B&!MfLU+&{z&5ix>=bUf-4aUL0Sq}R*dZzyZ?*Zijzl&VN zdEg>Ar&ye5(=wgINlfxn@t%7#>=lmYQ0(T;P*uIIm>OmL% z8~q0Mjqebw7#>8F_6_>a@|^SM@N4f}5~7ck?~O{IEArpW-!4yy86i<1DOczCfO~O&uwD7WXXTk-pH1 zLSJR?DZq0ts;aNFH<G1 zF@4hBKN@jajLY5pEE7+T}Gk zYrB6BV)lJ{Mz|8Taw*5^kTW5U^$fln<3JDg`(0HyL;O7LiDiKKGpvd2+cx>7{R5S` zDFX|#B{=W?Jl4ea2|dJ(+!xC}m%Wm45d0zD6LPMEte1h8D_)q#GZk!SCqVgjK%N@t z^H5Dl`s!0of$Ob&-ygm%boI6d`8L3eZHQwje{U&dn-?m{J}vR%ylN3p+5Qn4<7J_sLOTgX#x)zwCAL4J{Ue{p9_`T zhCeR-2JCq6gw$23dbNpA7iS?)x_<m|H;kC2M9Pc7mSDvVZAZhoOGLKppj8^7%cJ+>*b7&Gk?2ZNeTU-m&Rz!dhBM z|LPX_zm}V3VQj%SqDwKi!8@)kh<96P|5I_Fjdr_y$5r1v0eLC`!~8PjGxo1E`Q^xG z%>5JG`JVZExR>MpF6rTZoOkY7^iOd(FOgW3<2L-_k9QIVwEEB=7;nQiZv^g&KFCP_ ze7r2jZ+ewx`+D*5a;KEM_rYC~7V(<#k$V)^?v*+%CFF~61CY@-cM`hvO`tnq=vM3n zUHUb&3+YbT%f9TiOgL%i_5%;v_$s#T-b=HX6GL~aL$HC71a!eO#C&eYbH$U4OL|9| z@QgM5kq2?de%i?6z>9N_D+(-UtYqjVNN*7M2c;cG67|rn=nC{712y zp6Ul*Xs0T)K^j*Cd9$*A*b*>p7e(G&(9Pn`J&r%1EkM6tg}&%(zl!`g^1o{Gzm9zR z&zq2+HaNQROgqZpo&rxj2KJ$B-%E^>pF}?AdR-=e+jE%f0S@0Z%dx+g^_9LQbF?I>|eaHewNJM%G5$Fs!yy*c`kkF$S)Z?H5VrpLTzJNyP|?3tu~Ym{DkLzj!+ z0e--=P)6`o`X=N=+m!U-JU8NwD(dMZewPB{#C04$-k;su0{G_LoM+E<_}wY$@xB2! z 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 vO>N0kW$+{XdFLs0mK diff --git a/pc-bios/openbios-sparc64 b/pc-bios/openbios-sparc64 index 70a223d573aead9505cf2579e59af37b897c6202..7e746b45224e493871de5e593756c30d3dc56bc9 100644 GIT binary patch delta 24052 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 S>b*9%nU`(}x~JQ0?0*6KG+g@t delta 22976 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 Date: Mon, 11 Jul 2011 20:01:09 +0200 Subject: [PATCH 171/209] QMP: add snapshot-blkdev-sync command Add QMP bits for snapshot_blkdev command. This is the same as snapshot_blkdev in the human monitor. The command is synchronous. In the future async commands and or a break down of the functionality into multiple commands might be added. Also change the 'snapshot_file' argument to 'snapshot-file' in the human monitor, so that it matches QMP. Signed-off-by: Jes Sorensen Signed-off-by: Luiz Capitulino --- blockdev.c | 4 ++-- hmp-commands.hx | 2 +- qmp-commands.hx | 34 ++++++++++++++++++++++++++++++++++ 3 files changed, 37 insertions(+), 3 deletions(-) diff --git a/blockdev.c b/blockdev.c index a97a801e95..0b8d3a4f83 100644 --- a/blockdev.c +++ b/blockdev.c @@ -568,7 +568,7 @@ void do_commit(Monitor *mon, const QDict *qdict) int do_snapshot_blkdev(Monitor *mon, const QDict *qdict, QObject **ret_data) { const char *device = qdict_get_str(qdict, "device"); - const char *filename = qdict_get_try_str(qdict, "snapshot_file"); + const char *filename = qdict_get_try_str(qdict, "snapshot-file"); const char *format = qdict_get_try_str(qdict, "format"); BlockDriverState *bs; BlockDriver *drv, *old_drv, *proto_drv; @@ -577,7 +577,7 @@ int do_snapshot_blkdev(Monitor *mon, const QDict *qdict, QObject **ret_data) char old_filename[1024]; if (!filename) { - qerror_report(QERR_MISSING_PARAMETER, "snapshot_file"); + qerror_report(QERR_MISSING_PARAMETER, "snapshot-file"); ret = -1; goto out; } diff --git a/hmp-commands.hx b/hmp-commands.hx index 6ad8806785..c857827618 100644 --- a/hmp-commands.hx +++ b/hmp-commands.hx @@ -840,7 +840,7 @@ ETEXI { .name = "snapshot_blkdev", - .args_type = "device:B,snapshot_file:s?,format:s?", + .args_type = "device:B,snapshot-file:s?,format:s?", .params = "device [new-image-file] [format]", .help = "initiates a live snapshot\n\t\t\t" "of device. If a new image file is specified, the\n\t\t\t" diff --git a/qmp-commands.hx b/qmp-commands.hx index 92c5c3a318..5d44edf4e7 100644 --- a/qmp-commands.hx +++ b/qmp-commands.hx @@ -691,6 +691,40 @@ Example: -> { "execute": "block_resize", "arguments": { "device": "scratch", "size": 1073741824 } } <- { "return": {} } +EQMP + + { + .name = "blockdev-snapshot-sync", + .args_type = "device:B,snapshot-file:s?,format:s?", + .params = "device [new-image-file] [format]", + .user_print = monitor_user_noop, + .mhandler.cmd_new = do_snapshot_blkdev, + }, + +SQMP +blockdev-snapshot-sync +---------------------- + +Synchronous snapshot of a block device. snapshot-file specifies the +target of the new image. If the file exists, or if it is a device, the +snapshot will be created in the existing file/device. If does not +exist, a new file will be created. format specifies the format of the +snapshot image, default is qcow2. + +Arguments: + +- "device": device name to snapshot (json-string) +- "snapshot-file": name of new image file (json-string) +- "format": format of new image (json-string, optional) + +Example: + +-> { "execute": "blockdev-snapshot", "arguments": { "device": "ide-hd0", + "snapshot-file": + "/some/place/my-image", + "format": "qcow2" } } +<- { "return": {} } + EQMP { From 5c0263204da1870243107acd6c73b21f2e7b78f0 Mon Sep 17 00:00:00 2001 From: Luiz Capitulino Date: Mon, 11 Jul 2011 14:24:44 -0300 Subject: [PATCH 172/209] Introduce compiler.h header file This moves compiler related macros from qemu-common.h to compiler.h. The reason for this change is that there are simple header files that depend only on the compiler macros, so including qemu-common.h is overkill. Besides, qemu-common.h is bloated and will benefit from some splitting. Please, also note that the QEMU_BUILD_BUG_ON() macro is being fixed to not use double underscores as a prefix and the license text was added by Vassili Karpov (malc), who is one of the authors of the new file. Signed-off-by: Luiz Capitulino --- compiler.h | 34 ++++++++++++++++++++++++++++++++++ qemu-common.h | 25 +------------------------ 2 files changed, 35 insertions(+), 24 deletions(-) create mode 100644 compiler.h diff --git a/compiler.h b/compiler.h new file mode 100644 index 0000000000..9af5dc6c91 --- /dev/null +++ b/compiler.h @@ -0,0 +1,34 @@ +/* public domain */ + +#ifndef COMPILER_H +#define COMPILER_H + +#include "config-host.h" + +#define QEMU_NORETURN __attribute__ ((__noreturn__)) +#ifdef CONFIG_GCC_ATTRIBUTE_WARN_UNUSED_RESULT +#define QEMU_WARN_UNUSED_RESULT __attribute__((warn_unused_result)) +#else +#define QEMU_WARN_UNUSED_RESULT +#endif + +#define QEMU_BUILD_BUG_ON(x) \ + typedef char qemu_build_bug_on__##__LINE__[(x)?-1:1]; + +#if defined __GNUC__ +# if (__GNUC__ < 4) || \ + defined(__GNUC_MINOR__) && (__GNUC__ == 4) && (__GNUC_MINOR__ < 4) + /* gcc versions before 4.4.x don't support gnu_printf, so use printf. */ +# define GCC_ATTR __attribute__((__unused__, format(printf, 1, 2))) +# define GCC_FMT_ATTR(n, m) __attribute__((format(printf, n, m))) +# else + /* Use gnu_printf when supported (qemu uses standard format strings). */ +# define GCC_ATTR __attribute__((__unused__, format(gnu_printf, 1, 2))) +# define GCC_FMT_ATTR(n, m) __attribute__((format(gnu_printf, n, m))) +# endif +#else +#define GCC_ATTR /**/ +#define GCC_FMT_ATTR(n, m) +#endif + +#endif /* COMPILER_H */ diff --git a/qemu-common.h b/qemu-common.h index c2b79bd60e..ba55719700 100644 --- a/qemu-common.h +++ b/qemu-common.h @@ -2,16 +2,9 @@ #ifndef QEMU_COMMON_H #define QEMU_COMMON_H +#include "compiler.h" #include "config-host.h" -#define QEMU_NORETURN __attribute__ ((__noreturn__)) -#ifdef CONFIG_GCC_ATTRIBUTE_WARN_UNUSED_RESULT -#define QEMU_WARN_UNUSED_RESULT __attribute__((warn_unused_result)) -#else -#define QEMU_WARN_UNUSED_RESULT -#endif - -#define QEMU_BUILD_BUG_ON(x) typedef char __build_bug_on__##__LINE__[(x)?-1:1]; #define TFR(expr) do { if ((expr) != -1) break; } while (errno == EINTR) typedef struct QEMUTimer QEMUTimer; @@ -82,22 +75,6 @@ struct iovec { #include #endif -#if defined __GNUC__ -# if (__GNUC__ < 4) || \ - defined(__GNUC_MINOR__) && (__GNUC__ == 4) && (__GNUC_MINOR__ < 4) - /* gcc versions before 4.4.x don't support gnu_printf, so use printf. */ -# define GCC_ATTR __attribute__((__unused__, format(printf, 1, 2))) -# define GCC_FMT_ATTR(n, m) __attribute__((format(printf, n, m))) -# else - /* Use gnu_printf when supported (qemu uses standard format strings). */ -# define GCC_ATTR __attribute__((__unused__, format(gnu_printf, 1, 2))) -# define GCC_FMT_ATTR(n, m) __attribute__((format(gnu_printf, n, m))) -# endif -#else -#define GCC_ATTR /**/ -#define GCC_FMT_ATTR(n, m) -#endif - typedef int (*fprintf_function)(FILE *f, const char *fmt, ...) GCC_FMT_ATTR(2, 3); From d3608b7cc6f84caf9bc2b42e0a942ff561b73c4f Mon Sep 17 00:00:00 2001 From: Luiz Capitulino Date: Mon, 11 Jul 2011 15:01:57 -0300 Subject: [PATCH 173/209] Error: Fix build when qemu-common.h is not included Commit e4ea5e2d0e0e4c5188ab45b66f3195062ae059dc added the use of the macro GCC_FMT_ATTR to error.h, however compiler.h is not included by error.h This will cause a build error when files including error.h don't include qemu-common.h (or compiler.h). Not an issue today because the only file including it is json-parser.h and it does include qemu-common.h, but let's get it fixed. Signed-off-by: Luiz Capitulino --- error.h | 1 + 1 file changed, 1 insertion(+) diff --git a/error.h b/error.h index 0f92a6f570..6361f407fd 100644 --- a/error.h +++ b/error.h @@ -12,6 +12,7 @@ #ifndef ERROR_H #define ERROR_H +#include "compiler.h" #include /** From e18df14185e817ba735bce57ecdef9a55fb3d093 Mon Sep 17 00:00:00 2001 From: Anthony Liguori Date: Tue, 19 Jul 2011 14:50:29 -0500 Subject: [PATCH 174/209] Add hard build dependency on glib GLib is an extremely common library that has a portable thread implementation along with tons of other goodies. GLib and GObject have a fantastic amount of infrastructure we can leverage in QEMU including an object oriented programming infrastructure. Short term, it has a very nice thread pool implementation that we could leverage in something like virtio-9p. It also has a test harness implementation that this series will use. Signed-off-by: Anthony Liguori Signed-off-by: Michael Roth Signed-off-by: Luiz Capitulino --- Makefile | 2 ++ Makefile.objs | 1 + Makefile.target | 1 + configure | 13 +++++++++++++ 4 files changed, 17 insertions(+) diff --git a/Makefile b/Makefile index b3ffbe2407..42ae4e52d2 100644 --- a/Makefile +++ b/Makefile @@ -106,6 +106,8 @@ audio/audio.o audio/fmodaudio.o: QEMU_CFLAGS += $(FMOD_CFLAGS) QEMU_CFLAGS+=$(CURL_CFLAGS) +QEMU_CFLAGS+=$(GLIB_CFLAGS) + ui/cocoa.o: ui/cocoa.m ui/sdl.o audio/sdlaudio.o ui/sdl_zoom.o baum.o: QEMU_CFLAGS += $(SDL_CFLAGS) diff --git a/Makefile.objs b/Makefile.objs index c43ed05c89..55d18bbbec 100644 --- a/Makefile.objs +++ b/Makefile.objs @@ -376,3 +376,4 @@ vl.o: QEMU_CFLAGS+=$(GPROF_CFLAGS) vl.o: QEMU_CFLAGS+=$(SDL_CFLAGS) +vl.o: QEMU_CFLAGS+=$(GLIB_CFLAGS) diff --git a/Makefile.target b/Makefile.target index e20a313b9d..cde509ba76 100644 --- a/Makefile.target +++ b/Makefile.target @@ -204,6 +204,7 @@ QEMU_CFLAGS += $(VNC_TLS_CFLAGS) QEMU_CFLAGS += $(VNC_SASL_CFLAGS) QEMU_CFLAGS += $(VNC_JPEG_CFLAGS) QEMU_CFLAGS += $(VNC_PNG_CFLAGS) +QEMU_CFLAGS += $(GLIB_CFLAGS) # xen support obj-$(CONFIG_XEN) += xen-all.o xen_machine_pv.o xen_domainbuild.o xen-mapcache.o diff --git a/configure b/configure index e57efb179c..c0c8fdf607 100755 --- a/configure +++ b/configure @@ -1802,6 +1802,18 @@ EOF fi fi +########################################## +# glib support probe +if $pkg_config --modversion gthread-2.0 gio-2.0 > /dev/null 2>&1 ; then + glib_cflags=`$pkg_config --cflags gthread-2.0 gio-2.0 2>/dev/null` + glib_libs=`$pkg_config --libs gthread-2.0 gio-2.0 2>/dev/null` + libs_softmmu="$glib_libs $libs_softmmu" + libs_tools="$glib_libs $libs_tools" +else + echo "glib-2.0 required to compile QEMU" + exit 1 +fi + ########################################## # pthread probe PTHREADLIBS_LIST="-lpthread -lpthreadGC2" @@ -2849,6 +2861,7 @@ if test "$bluez" = "yes" ; then echo "CONFIG_BLUEZ=y" >> $config_host_mak echo "BLUEZ_CFLAGS=$bluez_cflags" >> $config_host_mak fi +echo "GLIB_CFLAGS=$glib_cflags" >> $config_host_mak if test "$xen" = "yes" ; then echo "CONFIG_XEN_BACKEND=y" >> $config_host_mak echo "CONFIG_XEN_CTRL_INTERFACE_VERSION=$xen_ctrl_version" >> $config_host_mak From 54d83804a1f69e932e014842e7e7a4744334123d Mon Sep 17 00:00:00 2001 From: Michael Roth Date: Tue, 19 Jul 2011 14:50:30 -0500 Subject: [PATCH 175/209] qlist: add qlist_first()/qlist_next() Signed-off-by: Michael Roth Signed-off-by: Luiz Capitulino --- qlist.h | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/qlist.h b/qlist.h index dbe7b92db5..d426bd4a4b 100644 --- a/qlist.h +++ b/qlist.h @@ -16,6 +16,7 @@ #include "qobject.h" #include "qemu-queue.h" #include "qemu-common.h" +#include "qemu-queue.h" typedef struct QListEntry { QObject *value; @@ -50,4 +51,14 @@ QObject *qlist_peek(QList *qlist); int qlist_empty(const QList *qlist); QList *qobject_to_qlist(const QObject *obj); +static inline const QListEntry *qlist_first(const QList *qlist) +{ + return QTAILQ_FIRST(&qlist->head); +} + +static inline const QListEntry *qlist_next(const QListEntry *entry) +{ + return QTAILQ_NEXT(entry, next); +} + #endif /* QLIST_H */ From c7aa841e9f75f0ff77fec9f918779b1951ab6b9d Mon Sep 17 00:00:00 2001 From: Michael Roth Date: Tue, 19 Jul 2011 14:50:31 -0500 Subject: [PATCH 176/209] qapi: add module init types for qapi Signed-off-by: Michael Roth Signed-off-by: Luiz Capitulino --- module.h | 2 ++ 1 file changed, 2 insertions(+) diff --git a/module.h b/module.h index 9263f1c7e2..ef667304c4 100644 --- a/module.h +++ b/module.h @@ -24,12 +24,14 @@ typedef enum { MODULE_INIT_BLOCK, MODULE_INIT_DEVICE, MODULE_INIT_MACHINE, + MODULE_INIT_QAPI, MODULE_INIT_MAX } module_init_type; #define block_init(function) module_init(function, MODULE_INIT_BLOCK) #define device_init(function) module_init(function, MODULE_INIT_DEVICE) #define machine_init(function) module_init(function, MODULE_INIT_MACHINE) +#define qapi_init(function) module_init(function, MODULE_INIT_QAPI) void register_module_init(void (*fn)(void), module_init_type type); From 2345c77c6d383bd804527720551b79b1d0400693 Mon Sep 17 00:00:00 2001 From: Michael Roth Date: Tue, 19 Jul 2011 14:50:32 -0500 Subject: [PATCH 177/209] qapi: add QAPI visitor core Base definitions/includes for Visiter interface used by generated visiter/marshalling code. Includes a GenericList type. Our lists require an embedded element. Since these types are generated, if you want to use them in a different type of data structure, there's no easy way to add another embedded element. The solution is to have non-embedded lists and that what this is. Signed-off-by: Michael Roth Signed-off-by: Luiz Capitulino --- Makefile.objs | 6 +++ configure | 1 + qapi/qapi-types-core.h | 20 +++++++ qapi/qapi-visit-core.c | 118 +++++++++++++++++++++++++++++++++++++++++ qapi/qapi-visit-core.h | 76 ++++++++++++++++++++++++++ 5 files changed, 221 insertions(+) create mode 100644 qapi/qapi-types-core.h create mode 100644 qapi/qapi-visit-core.c create mode 100644 qapi/qapi-visit-core.h diff --git a/Makefile.objs b/Makefile.objs index 55d18bbbec..3b68f5914a 100644 --- a/Makefile.objs +++ b/Makefile.objs @@ -372,6 +372,12 @@ endif libcacard-y = cac.o event.o vcard.o vreader.o vcard_emul_nss.o vcard_emul_type.o card_7816.o +###################################################################### +# qapi + +qapi-nested-y = qapi-visit-core.o +qapi-obj-y = $(addprefix qapi/, $(qapi-nested-y)) + vl.o: QEMU_CFLAGS+=$(GPROF_CFLAGS) vl.o: QEMU_CFLAGS+=$(SDL_CFLAGS) diff --git a/configure b/configure index c0c8fdf607..ad1e1e134d 100755 --- a/configure +++ b/configure @@ -3486,6 +3486,7 @@ DIRS="tests tests/cris slirp audio block net pc-bios/optionrom" DIRS="$DIRS pc-bios/spapr-rtas" DIRS="$DIRS roms/seabios roms/vgabios" DIRS="$DIRS fsdev ui" +DIRS="$DIRS qapi" FILES="Makefile tests/Makefile" FILES="$FILES tests/cris/Makefile tests/cris/.gdbinit" FILES="$FILES pc-bios/optionrom/Makefile pc-bios/keymaps" diff --git a/qapi/qapi-types-core.h b/qapi/qapi-types-core.h new file mode 100644 index 0000000000..a79bc2b3c3 --- /dev/null +++ b/qapi/qapi-types-core.h @@ -0,0 +1,20 @@ +/* + * Core Definitions for QAPI-generated Types + * + * Copyright IBM, Corp. 2011 + * + * Authors: + * Anthony Liguori + * + * This work is licensed under the terms of the GNU LGPL, version 2.1 or later. + * See the COPYING.LIB file in the top-level directory. + * + */ + +#ifndef QAPI_TYPES_CORE_H +#define QAPI_TYPES_CORE_H + +#include "qemu-common.h" +#include "error.h" + +#endif diff --git a/qapi/qapi-visit-core.c b/qapi/qapi-visit-core.c new file mode 100644 index 0000000000..ddef3eda12 --- /dev/null +++ b/qapi/qapi-visit-core.c @@ -0,0 +1,118 @@ +/* + * Core Definitions for QAPI Visitor Classes + * + * Copyright IBM, Corp. 2011 + * + * Authors: + * Anthony Liguori + * + * This work is licensed under the terms of the GNU LGPL, version 2.1 or later. + * See the COPYING.LIB file in the top-level directory. + * + */ + +#include "qapi/qapi-visit-core.h" + +void visit_start_handle(Visitor *v, void **obj, const char *kind, + const char *name, Error **errp) +{ + if (!error_is_set(errp) && v->start_handle) { + v->start_handle(v, obj, kind, name, errp); + } +} + +void visit_end_handle(Visitor *v, Error **errp) +{ + if (!error_is_set(errp) && v->end_handle) { + v->end_handle(v, errp); + } +} + +void visit_start_struct(Visitor *v, void **obj, const char *kind, + const char *name, size_t size, Error **errp) +{ + if (!error_is_set(errp)) { + v->start_struct(v, obj, kind, name, size, errp); + } +} + +void visit_end_struct(Visitor *v, Error **errp) +{ + if (!error_is_set(errp)) { + v->end_struct(v, errp); + } +} + +void visit_start_list(Visitor *v, const char *name, Error **errp) +{ + if (!error_is_set(errp)) { + v->start_list(v, name, errp); + } +} + +GenericList *visit_next_list(Visitor *v, GenericList **list, Error **errp) +{ + if (!error_is_set(errp)) { + return v->next_list(v, list, errp); + } + + return 0; +} + +void visit_end_list(Visitor *v, Error **errp) +{ + if (!error_is_set(errp)) { + v->end_list(v, errp); + } +} + +void visit_start_optional(Visitor *v, bool *present, const char *name, + Error **errp) +{ + if (!error_is_set(errp) && v->start_optional) { + v->start_optional(v, present, name, errp); + } +} + +void visit_end_optional(Visitor *v, Error **errp) +{ + if (!error_is_set(errp) && v->end_optional) { + v->end_optional(v, errp); + } +} + +void visit_type_enum(Visitor *v, int *obj, const char *strings[], + const char *kind, const char *name, Error **errp) +{ + if (!error_is_set(errp)) { + v->type_enum(v, obj, strings, kind, name, errp); + } +} + +void visit_type_int(Visitor *v, int64_t *obj, const char *name, Error **errp) +{ + if (!error_is_set(errp)) { + v->type_int(v, obj, name, errp); + } +} + +void visit_type_bool(Visitor *v, bool *obj, const char *name, Error **errp) +{ + if (!error_is_set(errp)) { + v->type_bool(v, obj, name, errp); + } +} + +void visit_type_str(Visitor *v, char **obj, const char *name, Error **errp) +{ + if (!error_is_set(errp)) { + v->type_str(v, obj, name, errp); + } +} + +void visit_type_number(Visitor *v, double *obj, const char *name, Error **errp) +{ + if (!error_is_set(errp)) { + v->type_number(v, obj, name, errp); + } +} diff --git a/qapi/qapi-visit-core.h b/qapi/qapi-visit-core.h new file mode 100644 index 0000000000..e850746b75 --- /dev/null +++ b/qapi/qapi-visit-core.h @@ -0,0 +1,76 @@ +/* + * Core Definitions for QAPI Visitor Classes + * + * Copyright IBM, Corp. 2011 + * + * Authors: + * Anthony Liguori + * + * This work is licensed under the terms of the GNU LGPL, version 2.1 or later. + * See the COPYING.LIB file in the top-level directory. + * + */ +#ifndef QAPI_VISITOR_CORE_H +#define QAPI_VISITOR_CORE_H + +#include "qapi/qapi-types-core.h" +#include + +typedef struct GenericList +{ + void *value; + struct GenericList *next; +} GenericList; + +typedef struct Visitor Visitor; + +struct Visitor +{ + /* Must be set */ + void (*start_struct)(Visitor *v, void **obj, const char *kind, + const char *name, size_t size, Error **errp); + void (*end_struct)(Visitor *v, Error **errp); + + void (*start_list)(Visitor *v, const char *name, Error **errp); + GenericList *(*next_list)(Visitor *v, GenericList **list, Error **errp); + void (*end_list)(Visitor *v, Error **errp); + + void (*type_enum)(Visitor *v, int *obj, const char *strings[], + const char *kind, const char *name, Error **errp); + + void (*type_int)(Visitor *v, int64_t *obj, const char *name, Error **errp); + void (*type_bool)(Visitor *v, bool *obj, const char *name, Error **errp); + void (*type_str)(Visitor *v, char **obj, const char *name, Error **errp); + void (*type_number)(Visitor *v, double *obj, const char *name, + Error **errp); + + /* May be NULL */ + void (*start_optional)(Visitor *v, bool *present, const char *name, + Error **errp); + void (*end_optional)(Visitor *v, Error **errp); + + void (*start_handle)(Visitor *v, void **obj, const char *kind, + const char *name, Error **errp); + void (*end_handle)(Visitor *v, Error **errp); +}; + +void visit_start_handle(Visitor *v, void **obj, const char *kind, + const char *name, Error **errp); +void visit_end_handle(Visitor *v, Error **errp); +void visit_start_struct(Visitor *v, void **obj, const char *kind, + const char *name, size_t size, Error **errp); +void visit_end_struct(Visitor *v, Error **errp); +void visit_start_list(Visitor *v, const char *name, Error **errp); +GenericList *visit_next_list(Visitor *v, GenericList **list, Error **errp); +void visit_end_list(Visitor *v, Error **errp); +void visit_start_optional(Visitor *v, bool *present, const char *name, + Error **errp); +void visit_end_optional(Visitor *v, Error **errp); +void visit_type_enum(Visitor *v, int *obj, const char *strings[], + const char *kind, const char *name, Error **errp); +void visit_type_int(Visitor *v, int64_t *obj, const char *name, Error **errp); +void visit_type_bool(Visitor *v, bool *obj, const char *name, Error **errp); +void visit_type_str(Visitor *v, char **obj, const char *name, Error **errp); +void visit_type_number(Visitor *v, double *obj, const char *name, Error **errp); + +#endif From c40cc0a0ddbcda2af446b40271025b3dbee119ce Mon Sep 17 00:00:00 2001 From: Michael Roth Date: Tue, 19 Jul 2011 14:50:33 -0500 Subject: [PATCH 178/209] qapi: add QMP input visitor A type of Visiter class that is used to walk a qobject's structure and assign each entry to the corresponding native C type. Command marshaling function will use this to pull out QMP command parameters recieved over the wire and pass them as native arguments to the corresponding C functions. Signed-off-by: Michael Roth Signed-off-by: Luiz Capitulino --- Makefile.objs | 2 +- qapi/qmp-input-visitor.c | 301 +++++++++++++++++++++++++++++++++++++++ qapi/qmp-input-visitor.h | 27 ++++ qerror.h | 3 + 4 files changed, 332 insertions(+), 1 deletion(-) create mode 100644 qapi/qmp-input-visitor.c create mode 100644 qapi/qmp-input-visitor.h diff --git a/Makefile.objs b/Makefile.objs index 3b68f5914a..d86ecc1a3f 100644 --- a/Makefile.objs +++ b/Makefile.objs @@ -375,7 +375,7 @@ libcacard-y = cac.o event.o vcard.o vreader.o vcard_emul_nss.o vcard_emul_type.o ###################################################################### # qapi -qapi-nested-y = qapi-visit-core.o +qapi-nested-y = qapi-visit-core.o qmp-input-visitor.o qapi-obj-y = $(addprefix qapi/, $(qapi-nested-y)) vl.o: QEMU_CFLAGS+=$(GPROF_CFLAGS) diff --git a/qapi/qmp-input-visitor.c b/qapi/qmp-input-visitor.c new file mode 100644 index 0000000000..6a1adc9fce --- /dev/null +++ b/qapi/qmp-input-visitor.c @@ -0,0 +1,301 @@ +/* + * Input Visitor + * + * Copyright IBM, Corp. 2011 + * + * Authors: + * Anthony Liguori + * + * This work is licensed under the terms of the GNU LGPL, version 2.1 or later. + * See the COPYING.LIB file in the top-level directory. + * + */ + +#include "qmp-input-visitor.h" +#include "qemu-queue.h" +#include "qemu-common.h" +#include "qemu-objects.h" +#include "qerror.h" + +#define QIV_STACK_SIZE 1024 + +typedef struct StackObject +{ + const QObject *obj; + const QListEntry *entry; +} StackObject; + +struct QmpInputVisitor +{ + Visitor visitor; + QObject *obj; + StackObject stack[QIV_STACK_SIZE]; + int nb_stack; +}; + +static QmpInputVisitor *to_qiv(Visitor *v) +{ + return container_of(v, QmpInputVisitor, visitor); +} + +static const QObject *qmp_input_get_object(QmpInputVisitor *qiv, + const char *name) +{ + const QObject *qobj; + + if (qiv->nb_stack == 0) { + qobj = qiv->obj; + } else { + qobj = qiv->stack[qiv->nb_stack - 1].obj; + } + + if (name && qobject_type(qobj) == QTYPE_QDICT) { + return qdict_get(qobject_to_qdict(qobj), name); + } else if (qiv->nb_stack > 0 && qobject_type(qobj) == QTYPE_QLIST) { + return qlist_entry_obj(qiv->stack[qiv->nb_stack - 1].entry); + } + + return qobj; +} + +static void qmp_input_push(QmpInputVisitor *qiv, const QObject *obj, Error **errp) +{ + qiv->stack[qiv->nb_stack].obj = obj; + if (qobject_type(obj) == QTYPE_QLIST) { + qiv->stack[qiv->nb_stack].entry = qlist_first(qobject_to_qlist(obj)); + } + qiv->nb_stack++; + + if (qiv->nb_stack >= QIV_STACK_SIZE) { + error_set(errp, QERR_BUFFER_OVERRUN); + return; + } +} + +static void qmp_input_pop(QmpInputVisitor *qiv, Error **errp) +{ + qiv->nb_stack--; + if (qiv->nb_stack < 0) { + error_set(errp, QERR_BUFFER_OVERRUN); + return; + } +} + +static void qmp_input_start_struct(Visitor *v, void **obj, const char *kind, + const char *name, size_t size, Error **errp) +{ + QmpInputVisitor *qiv = to_qiv(v); + const QObject *qobj = qmp_input_get_object(qiv, name); + + if (!qobj || qobject_type(qobj) != QTYPE_QDICT) { + error_set(errp, QERR_INVALID_PARAMETER_TYPE, name ? name : "null", + "QDict"); + return; + } + + qmp_input_push(qiv, qobj, errp); + if (error_is_set(errp)) { + return; + } + + if (obj) { + *obj = qemu_mallocz(size); + } +} + +static void qmp_input_end_struct(Visitor *v, Error **errp) +{ + QmpInputVisitor *qiv = to_qiv(v); + + qmp_input_pop(qiv, errp); +} + +static void qmp_input_start_list(Visitor *v, const char *name, Error **errp) +{ + QmpInputVisitor *qiv = to_qiv(v); + const QObject *qobj = qmp_input_get_object(qiv, name); + + if (!qobj || qobject_type(qobj) != QTYPE_QLIST) { + error_set(errp, QERR_INVALID_PARAMETER_TYPE, name ? name : "null", + "list"); + return; + } + + qmp_input_push(qiv, qobj, errp); +} + +static GenericList *qmp_input_next_list(Visitor *v, GenericList **list, + Error **errp) +{ + QmpInputVisitor *qiv = to_qiv(v); + GenericList *entry; + StackObject *so = &qiv->stack[qiv->nb_stack - 1]; + + if (so->entry == NULL) { + return NULL; + } + + entry = qemu_mallocz(sizeof(*entry)); + if (*list) { + so->entry = qlist_next(so->entry); + if (so->entry == NULL) { + qemu_free(entry); + return NULL; + } + (*list)->next = entry; + } + *list = entry; + + + return entry; +} + +static void qmp_input_end_list(Visitor *v, Error **errp) +{ + QmpInputVisitor *qiv = to_qiv(v); + + qmp_input_pop(qiv, errp); +} + +static void qmp_input_type_int(Visitor *v, int64_t *obj, const char *name, + Error **errp) +{ + QmpInputVisitor *qiv = to_qiv(v); + const QObject *qobj = qmp_input_get_object(qiv, name); + + if (!qobj || qobject_type(qobj) != QTYPE_QINT) { + error_set(errp, QERR_INVALID_PARAMETER_TYPE, name ? name : "null", + "integer"); + return; + } + + *obj = qint_get_int(qobject_to_qint(qobj)); +} + +static void qmp_input_type_bool(Visitor *v, bool *obj, const char *name, + Error **errp) +{ + QmpInputVisitor *qiv = to_qiv(v); + const QObject *qobj = qmp_input_get_object(qiv, name); + + if (!qobj || qobject_type(qobj) != QTYPE_QBOOL) { + error_set(errp, QERR_INVALID_PARAMETER_TYPE, name ? name : "null", + "boolean"); + return; + } + + *obj = qbool_get_int(qobject_to_qbool(qobj)); +} + +static void qmp_input_type_str(Visitor *v, char **obj, const char *name, + Error **errp) +{ + QmpInputVisitor *qiv = to_qiv(v); + const QObject *qobj = qmp_input_get_object(qiv, name); + + if (!qobj || qobject_type(qobj) != QTYPE_QSTRING) { + error_set(errp, QERR_INVALID_PARAMETER_TYPE, name ? name : "null", + "string"); + return; + } + + *obj = qemu_strdup(qstring_get_str(qobject_to_qstring(qobj))); +} + +static void qmp_input_type_number(Visitor *v, double *obj, const char *name, + Error **errp) +{ + QmpInputVisitor *qiv = to_qiv(v); + const QObject *qobj = qmp_input_get_object(qiv, name); + + if (!qobj || qobject_type(qobj) != QTYPE_QFLOAT) { + error_set(errp, QERR_INVALID_PARAMETER_TYPE, name ? name : "null", + "double"); + return; + } + + *obj = qfloat_get_double(qobject_to_qfloat(qobj)); +} + +static void qmp_input_type_enum(Visitor *v, int *obj, const char *strings[], + const char *kind, const char *name, + Error **errp) +{ + int64_t value = 0; + char *enum_str; + + assert(strings); + + qmp_input_type_str(v, &enum_str, name, errp); + if (error_is_set(errp)) { + return; + } + + while (strings[value] != NULL) { + if (strcmp(strings[value], enum_str) == 0) { + break; + } + value++; + } + + if (strings[value] == NULL) { + error_set(errp, QERR_INVALID_PARAMETER, name ? name : "null"); + return; + } + + *obj = value; +} + +static void qmp_input_start_optional(Visitor *v, bool *present, + const char *name, Error **errp) +{ + QmpInputVisitor *qiv = to_qiv(v); + const QObject *qobj = qmp_input_get_object(qiv, name); + + if (!qobj) { + *present = false; + return; + } + + *present = true; +} + +static void qmp_input_end_optional(Visitor *v, Error **errp) +{ +} + +Visitor *qmp_input_get_visitor(QmpInputVisitor *v) +{ + return &v->visitor; +} + +void qmp_input_visitor_cleanup(QmpInputVisitor *v) +{ + qobject_decref(v->obj); + qemu_free(v); +} + +QmpInputVisitor *qmp_input_visitor_new(QObject *obj) +{ + QmpInputVisitor *v; + + v = qemu_mallocz(sizeof(*v)); + + v->visitor.start_struct = qmp_input_start_struct; + v->visitor.end_struct = qmp_input_end_struct; + v->visitor.start_list = qmp_input_start_list; + v->visitor.next_list = qmp_input_next_list; + v->visitor.end_list = qmp_input_end_list; + v->visitor.type_enum = qmp_input_type_enum; + v->visitor.type_int = qmp_input_type_int; + v->visitor.type_bool = qmp_input_type_bool; + v->visitor.type_str = qmp_input_type_str; + v->visitor.type_number = qmp_input_type_number; + v->visitor.start_optional = qmp_input_start_optional; + v->visitor.end_optional = qmp_input_end_optional; + + v->obj = obj; + qobject_incref(v->obj); + + return v; +} diff --git a/qapi/qmp-input-visitor.h b/qapi/qmp-input-visitor.h new file mode 100644 index 0000000000..3f798f0335 --- /dev/null +++ b/qapi/qmp-input-visitor.h @@ -0,0 +1,27 @@ +/* + * Input Visitor + * + * Copyright IBM, Corp. 2011 + * + * Authors: + * Anthony Liguori + * + * This work is licensed under the terms of the GNU LGPL, version 2.1 or later. + * See the COPYING.LIB file in the top-level directory. + * + */ + +#ifndef QMP_INPUT_VISITOR_H +#define QMP_INPUT_VISITOR_H + +#include "qapi-visit-core.h" +#include "qobject.h" + +typedef struct QmpInputVisitor QmpInputVisitor; + +QmpInputVisitor *qmp_input_visitor_new(QObject *obj); +void qmp_input_visitor_cleanup(QmpInputVisitor *v); + +Visitor *qmp_input_get_visitor(QmpInputVisitor *v); + +#endif diff --git a/qerror.h b/qerror.h index 16c830d8b7..9a9fa5b7eb 100644 --- a/qerror.h +++ b/qerror.h @@ -124,6 +124,9 @@ QError *qobject_to_qerror(const QObject *obj); #define QERR_JSON_PARSE_ERROR \ "{ 'class': 'JSONParseError', 'data': { 'message': %s } }" +#define QERR_BUFFER_OVERRUN \ + "{ 'class': 'BufferOverrun', 'data': {} }" + #define QERR_KVM_MISSING_CAP \ "{ 'class': 'KVMMissingCap', 'data': { 'capability': %s, 'feature': %s } }" From e4e6aa14ed377d808aba4643cd922403606d4dee Mon Sep 17 00:00:00 2001 From: Michael Roth Date: Tue, 19 Jul 2011 14:50:34 -0500 Subject: [PATCH 179/209] qapi: add QMP output visitor Type of Visiter class that serves as the inverse of the input visitor: it takes a series of native C types and uses their values to construct a corresponding QObject. The command marshaling/dispatcher functions will use this to convert the output of QMP functions into a QObject that can be sent over the wire. Signed-off-by: Michael Roth Signed-off-by: Luiz Capitulino --- Makefile.objs | 2 +- qapi/qmp-output-visitor.c | 239 ++++++++++++++++++++++++++++++++++++++ qapi/qmp-output-visitor.h | 28 +++++ 3 files changed, 268 insertions(+), 1 deletion(-) create mode 100644 qapi/qmp-output-visitor.c create mode 100644 qapi/qmp-output-visitor.h diff --git a/Makefile.objs b/Makefile.objs index d86ecc1a3f..301d56576c 100644 --- a/Makefile.objs +++ b/Makefile.objs @@ -375,7 +375,7 @@ libcacard-y = cac.o event.o vcard.o vreader.o vcard_emul_nss.o vcard_emul_type.o ###################################################################### # qapi -qapi-nested-y = qapi-visit-core.o qmp-input-visitor.o +qapi-nested-y = qapi-visit-core.o qmp-input-visitor.o qmp-output-visitor.o qapi-obj-y = $(addprefix qapi/, $(qapi-nested-y)) vl.o: QEMU_CFLAGS+=$(GPROF_CFLAGS) diff --git a/qapi/qmp-output-visitor.c b/qapi/qmp-output-visitor.c new file mode 100644 index 0000000000..c398cac4f0 --- /dev/null +++ b/qapi/qmp-output-visitor.c @@ -0,0 +1,239 @@ +/* + * Core Definitions for QAPI/QMP Command Registry + * + * Copyright IBM, Corp. 2011 + * + * Authors: + * Anthony Liguori + * + * This work is licensed under the terms of the GNU LGPL, version 2.1 or later. + * See the COPYING.LIB file in the top-level directory. + * + */ + +#include "qmp-output-visitor.h" +#include "qemu-queue.h" +#include "qemu-common.h" +#include "qemu-objects.h" +#include "qerror.h" + +typedef struct QStackEntry +{ + QObject *value; + QTAILQ_ENTRY(QStackEntry) node; +} QStackEntry; + +typedef QTAILQ_HEAD(QStack, QStackEntry) QStack; + +struct QmpOutputVisitor +{ + Visitor visitor; + QStack stack; +}; + +#define qmp_output_add(qov, name, value) \ + qmp_output_add_obj(qov, name, QOBJECT(value)) +#define qmp_output_push(qov, value) qmp_output_push_obj(qov, QOBJECT(value)) + +static QmpOutputVisitor *to_qov(Visitor *v) +{ + return container_of(v, QmpOutputVisitor, visitor); +} + +static void qmp_output_push_obj(QmpOutputVisitor *qov, QObject *value) +{ + QStackEntry *e = qemu_mallocz(sizeof(*e)); + + e->value = value; + QTAILQ_INSERT_HEAD(&qov->stack, e, node); +} + +static QObject *qmp_output_pop(QmpOutputVisitor *qov) +{ + QStackEntry *e = QTAILQ_FIRST(&qov->stack); + QObject *value; + QTAILQ_REMOVE(&qov->stack, e, node); + value = e->value; + qemu_free(e); + return value; +} + +static QObject *qmp_output_first(QmpOutputVisitor *qov) +{ + QStackEntry *e = QTAILQ_LAST(&qov->stack, QStack); + return e->value; +} + +static QObject *qmp_output_last(QmpOutputVisitor *qov) +{ + QStackEntry *e = QTAILQ_FIRST(&qov->stack); + return e->value; +} + +static void qmp_output_add_obj(QmpOutputVisitor *qov, const char *name, + QObject *value) +{ + QObject *cur; + + if (QTAILQ_EMPTY(&qov->stack)) { + qmp_output_push_obj(qov, value); + return; + } + + cur = qmp_output_last(qov); + + switch (qobject_type(cur)) { + case QTYPE_QDICT: + qdict_put_obj(qobject_to_qdict(cur), name, value); + break; + case QTYPE_QLIST: + qlist_append_obj(qobject_to_qlist(cur), value); + break; + default: + qobject_decref(qmp_output_pop(qov)); + qmp_output_push_obj(qov, value); + break; + } +} + +static void qmp_output_start_struct(Visitor *v, void **obj, const char *kind, + const char *name, size_t unused, + Error **errp) +{ + QmpOutputVisitor *qov = to_qov(v); + QDict *dict = qdict_new(); + + qmp_output_add(qov, name, dict); + qmp_output_push(qov, dict); +} + +static void qmp_output_end_struct(Visitor *v, Error **errp) +{ + QmpOutputVisitor *qov = to_qov(v); + qmp_output_pop(qov); +} + +static void qmp_output_start_list(Visitor *v, const char *name, Error **errp) +{ + QmpOutputVisitor *qov = to_qov(v); + QList *list = qlist_new(); + + qmp_output_add(qov, name, list); + qmp_output_push(qov, list); +} + +static GenericList *qmp_output_next_list(Visitor *v, GenericList **list, + Error **errp) +{ + GenericList *retval = *list; + *list = retval->next; + return retval; +} + +static void qmp_output_end_list(Visitor *v, Error **errp) +{ + QmpOutputVisitor *qov = to_qov(v); + qmp_output_pop(qov); +} + +static void qmp_output_type_int(Visitor *v, int64_t *obj, const char *name, + Error **errp) +{ + QmpOutputVisitor *qov = to_qov(v); + qmp_output_add(qov, name, qint_from_int(*obj)); +} + +static void qmp_output_type_bool(Visitor *v, bool *obj, const char *name, + Error **errp) +{ + QmpOutputVisitor *qov = to_qov(v); + qmp_output_add(qov, name, qbool_from_int(*obj)); +} + +static void qmp_output_type_str(Visitor *v, char **obj, const char *name, + Error **errp) +{ + QmpOutputVisitor *qov = to_qov(v); + if (*obj) { + qmp_output_add(qov, name, qstring_from_str(*obj)); + } else { + qmp_output_add(qov, name, qstring_from_str("")); + } +} + +static void qmp_output_type_number(Visitor *v, double *obj, const char *name, + Error **errp) +{ + QmpOutputVisitor *qov = to_qov(v); + qmp_output_add(qov, name, qfloat_from_double(*obj)); +} + +static void qmp_output_type_enum(Visitor *v, int *obj, const char *strings[], + const char *kind, const char *name, + Error **errp) +{ + int i = 0; + int value = *obj; + char *enum_str; + + assert(strings); + while (strings[i++] != NULL); + if (value >= i - 1) { + error_set(errp, QERR_INVALID_PARAMETER, name ? name : "null"); + return; + } + + enum_str = (char *)strings[value]; + qmp_output_type_str(v, &enum_str, name, errp); +} + +QObject *qmp_output_get_qobject(QmpOutputVisitor *qov) +{ + QObject *obj = qmp_output_first(qov); + if (obj) { + qobject_incref(obj); + } + return obj; +} + +Visitor *qmp_output_get_visitor(QmpOutputVisitor *v) +{ + return &v->visitor; +} + +void qmp_output_visitor_cleanup(QmpOutputVisitor *v) +{ + QStackEntry *e, *tmp; + + QTAILQ_FOREACH_SAFE(e, &v->stack, node, tmp) { + QTAILQ_REMOVE(&v->stack, e, node); + if (e->value) { + qobject_decref(e->value); + } + qemu_free(e); + } + + qemu_free(v); +} + +QmpOutputVisitor *qmp_output_visitor_new(void) +{ + QmpOutputVisitor *v; + + v = qemu_mallocz(sizeof(*v)); + + v->visitor.start_struct = qmp_output_start_struct; + v->visitor.end_struct = qmp_output_end_struct; + v->visitor.start_list = qmp_output_start_list; + v->visitor.next_list = qmp_output_next_list; + v->visitor.end_list = qmp_output_end_list; + v->visitor.type_enum = qmp_output_type_enum; + v->visitor.type_int = qmp_output_type_int; + v->visitor.type_bool = qmp_output_type_bool; + v->visitor.type_str = qmp_output_type_str; + v->visitor.type_number = qmp_output_type_number; + + QTAILQ_INIT(&v->stack); + + return v; +} diff --git a/qapi/qmp-output-visitor.h b/qapi/qmp-output-visitor.h new file mode 100644 index 0000000000..4a649c2504 --- /dev/null +++ b/qapi/qmp-output-visitor.h @@ -0,0 +1,28 @@ +/* + * Output Visitor + * + * Copyright IBM, Corp. 2011 + * + * Authors: + * Anthony Liguori + * + * This work is licensed under the terms of the GNU LGPL, version 2.1 or later. + * See the COPYING.LIB file in the top-level directory. + * + */ + +#ifndef QMP_OUTPUT_VISITOR_H +#define QMP_OUTPUT_VISITOR_H + +#include "qapi-visit-core.h" +#include "qobject.h" + +typedef struct QmpOutputVisitor QmpOutputVisitor; + +QmpOutputVisitor *qmp_output_visitor_new(void); +void qmp_output_visitor_cleanup(QmpOutputVisitor *v); + +QObject *qmp_output_get_qobject(QmpOutputVisitor *v); +Visitor *qmp_output_get_visitor(QmpOutputVisitor *v); + +#endif From d5f3c29cf8d59fad4f3c657fc0a26510043bec65 Mon Sep 17 00:00:00 2001 From: Michael Roth Date: Tue, 19 Jul 2011 14:50:35 -0500 Subject: [PATCH 180/209] qapi: add QAPI dealloc visitor Type of Visitor class that can be passed into a qapi-generated C type's visitor function to free() any heap-allocated data types. Signed-off-by: Michael Roth Signed-off-by: Luiz Capitulino --- Makefile.objs | 2 +- qapi/qapi-dealloc-visitor.c | 147 ++++++++++++++++++++++++++++++++++++ qapi/qapi-dealloc-visitor.h | 26 +++++++ 3 files changed, 174 insertions(+), 1 deletion(-) create mode 100644 qapi/qapi-dealloc-visitor.c create mode 100644 qapi/qapi-dealloc-visitor.h diff --git a/Makefile.objs b/Makefile.objs index 301d56576c..92c7b56512 100644 --- a/Makefile.objs +++ b/Makefile.objs @@ -375,7 +375,7 @@ libcacard-y = cac.o event.o vcard.o vreader.o vcard_emul_nss.o vcard_emul_type.o ###################################################################### # qapi -qapi-nested-y = qapi-visit-core.o qmp-input-visitor.o qmp-output-visitor.o +qapi-nested-y = qapi-visit-core.o qmp-input-visitor.o qmp-output-visitor.o qapi-dealloc-visitor.o qapi-obj-y = $(addprefix qapi/, $(qapi-nested-y)) vl.o: QEMU_CFLAGS+=$(GPROF_CFLAGS) diff --git a/qapi/qapi-dealloc-visitor.c b/qapi/qapi-dealloc-visitor.c new file mode 100644 index 0000000000..8cde4dd0d4 --- /dev/null +++ b/qapi/qapi-dealloc-visitor.c @@ -0,0 +1,147 @@ +/* + * Dealloc Visitor + * + * Copyright IBM, Corp. 2011 + * + * Authors: + * Michael Roth + * + * This work is licensed under the terms of the GNU LGPL, version 2.1 or later. + * See the COPYING.LIB file in the top-level directory. + * + */ + +#include "qapi-dealloc-visitor.h" +#include "qemu-queue.h" +#include "qemu-common.h" +#include "qemu-objects.h" + +typedef struct StackEntry +{ + void *value; + QTAILQ_ENTRY(StackEntry) node; +} StackEntry; + +struct QapiDeallocVisitor +{ + Visitor visitor; + QTAILQ_HEAD(, StackEntry) stack; +}; + +static QapiDeallocVisitor *to_qov(Visitor *v) +{ + return container_of(v, QapiDeallocVisitor, visitor); +} + +static void qapi_dealloc_push(QapiDeallocVisitor *qov, void *value) +{ + StackEntry *e = qemu_mallocz(sizeof(*e)); + + e->value = value; + QTAILQ_INSERT_HEAD(&qov->stack, e, node); +} + +static void *qapi_dealloc_pop(QapiDeallocVisitor *qov) +{ + StackEntry *e = QTAILQ_FIRST(&qov->stack); + QObject *value; + QTAILQ_REMOVE(&qov->stack, e, node); + value = e->value; + qemu_free(e); + return value; +} + +static void qapi_dealloc_start_struct(Visitor *v, void **obj, const char *kind, + const char *name, size_t unused, + Error **errp) +{ + QapiDeallocVisitor *qov = to_qov(v); + qapi_dealloc_push(qov, obj); +} + +static void qapi_dealloc_end_struct(Visitor *v, Error **errp) +{ + QapiDeallocVisitor *qov = to_qov(v); + void **obj = qapi_dealloc_pop(qov); + if (obj) { + qemu_free(*obj); + } +} + +static void qapi_dealloc_start_list(Visitor *v, const char *name, Error **errp) +{ +} + +static GenericList *qapi_dealloc_next_list(Visitor *v, GenericList **list, + Error **errp) +{ + GenericList *retval = *list; + qemu_free(retval->value); + *list = retval->next; + return retval; +} + +static void qapi_dealloc_end_list(Visitor *v, Error **errp) +{ +} + +static void qapi_dealloc_type_str(Visitor *v, char **obj, const char *name, + Error **errp) +{ + if (obj) { + qemu_free(*obj); + } +} + +static void qapi_dealloc_type_int(Visitor *v, int64_t *obj, const char *name, + Error **errp) +{ +} + +static void qapi_dealloc_type_bool(Visitor *v, bool *obj, const char *name, + Error **errp) +{ +} + +static void qapi_dealloc_type_number(Visitor *v, double *obj, const char *name, + Error **errp) +{ +} + +static void qapi_dealloc_type_enum(Visitor *v, int *obj, const char *strings[], + const char *kind, const char *name, + Error **errp) +{ +} + +Visitor *qapi_dealloc_get_visitor(QapiDeallocVisitor *v) +{ + return &v->visitor; +} + +void qapi_dealloc_visitor_cleanup(QapiDeallocVisitor *v) +{ + qemu_free(v); +} + +QapiDeallocVisitor *qapi_dealloc_visitor_new(void) +{ + QapiDeallocVisitor *v; + + v = qemu_mallocz(sizeof(*v)); + + v->visitor.start_struct = qapi_dealloc_start_struct; + v->visitor.end_struct = qapi_dealloc_end_struct; + v->visitor.start_list = qapi_dealloc_start_list; + v->visitor.next_list = qapi_dealloc_next_list; + v->visitor.end_list = qapi_dealloc_end_list; + v->visitor.type_enum = qapi_dealloc_type_enum; + v->visitor.type_int = qapi_dealloc_type_int; + v->visitor.type_bool = qapi_dealloc_type_bool; + v->visitor.type_str = qapi_dealloc_type_str; + v->visitor.type_number = qapi_dealloc_type_number; + + QTAILQ_INIT(&v->stack); + + return v; +} diff --git a/qapi/qapi-dealloc-visitor.h b/qapi/qapi-dealloc-visitor.h new file mode 100644 index 0000000000..5842bc79bd --- /dev/null +++ b/qapi/qapi-dealloc-visitor.h @@ -0,0 +1,26 @@ +/* + * Dealloc Visitor + * + * Copyright IBM, Corp. 2011 + * + * Authors: + * Michael Roth + * + * This work is licensed under the terms of the GNU LGPL, version 2.1 or later. + * See the COPYING.LIB file in the top-level directory. + * + */ + +#ifndef QAPI_DEALLOC_VISITOR_H +#define QAPI_DEALLOC_VISITOR_H + +#include "qapi-visit-core.h" + +typedef struct QapiDeallocVisitor QapiDeallocVisitor; + +QapiDeallocVisitor *qapi_dealloc_visitor_new(void); +void qapi_dealloc_visitor_cleanup(QapiDeallocVisitor *d); + +Visitor *qapi_dealloc_get_visitor(QapiDeallocVisitor *v); + +#endif From 43c20a43ca4b3fa265469887186eb0fee68e4a0d Mon Sep 17 00:00:00 2001 From: Michael Roth Date: Tue, 19 Jul 2011 14:50:36 -0500 Subject: [PATCH 181/209] qapi: add QMP command registration/lookup functions Registration/lookup functions for that provide a lookup table for dispatching QMP commands. Signed-off-by: Michael Roth Signed-off-by: Luiz Capitulino --- Makefile.objs | 1 + qapi/qmp-core.h | 39 +++++++++++++++++++++++++++++++++++++++ qapi/qmp-registry.c | 40 ++++++++++++++++++++++++++++++++++++++++ 3 files changed, 80 insertions(+) create mode 100644 qapi/qmp-core.h create mode 100644 qapi/qmp-registry.c diff --git a/Makefile.objs b/Makefile.objs index 92c7b56512..c918ee7f8f 100644 --- a/Makefile.objs +++ b/Makefile.objs @@ -376,6 +376,7 @@ libcacard-y = cac.o event.o vcard.o vreader.o vcard_emul_nss.o vcard_emul_type.o # qapi qapi-nested-y = qapi-visit-core.o qmp-input-visitor.o qmp-output-visitor.o qapi-dealloc-visitor.o +qapi-nested-y += qmp-registry.o qapi-obj-y = $(addprefix qapi/, $(qapi-nested-y)) vl.o: QEMU_CFLAGS+=$(GPROF_CFLAGS) diff --git a/qapi/qmp-core.h b/qapi/qmp-core.h new file mode 100644 index 0000000000..8b96d2c676 --- /dev/null +++ b/qapi/qmp-core.h @@ -0,0 +1,39 @@ +/* + * Core Definitions for QAPI/QMP Dispatch + * + * Copyright IBM, Corp. 2011 + * + * Authors: + * Anthony Liguori + * + * This work is licensed under the terms of the GNU LGPL, version 2.1 or later. + * See the COPYING.LIB file in the top-level directory. + * + */ + +#ifndef QMP_CORE_H +#define QMP_CORE_H + +#include "qobject.h" +#include "qdict.h" +#include "error.h" + +typedef void (QmpCommandFunc)(QDict *, QObject **, Error **); + +typedef enum QmpCommandType +{ + QCT_NORMAL, +} QmpCommandType; + +typedef struct QmpCommand +{ + const char *name; + QmpCommandType type; + QmpCommandFunc *fn; + QTAILQ_ENTRY(QmpCommand) node; +} QmpCommand; + +void qmp_register_command(const char *name, QmpCommandFunc *fn); +QmpCommand *qmp_find_command(const char *name); + +#endif diff --git a/qapi/qmp-registry.c b/qapi/qmp-registry.c new file mode 100644 index 0000000000..3fe8866d44 --- /dev/null +++ b/qapi/qmp-registry.c @@ -0,0 +1,40 @@ +/* + * Core Definitions for QAPI/QMP Dispatch + * + * Copyright IBM, Corp. 2011 + * + * Authors: + * Anthony Liguori + * Michael Roth + * + * This work is licensed under the terms of the GNU LGPL, version 2.1 or later. + * See the COPYING.LIB file in the top-level directory. + * + */ + +#include "qapi/qmp-core.h" + +static QTAILQ_HEAD(, QmpCommand) qmp_commands = + QTAILQ_HEAD_INITIALIZER(qmp_commands); + +void qmp_register_command(const char *name, QmpCommandFunc *fn) +{ + QmpCommand *cmd = qemu_mallocz(sizeof(*cmd)); + + cmd->name = name; + cmd->type = QCT_NORMAL; + cmd->fn = fn; + QTAILQ_INSERT_TAIL(&qmp_commands, cmd, node); +} + +QmpCommand *qmp_find_command(const char *name) +{ + QmpCommand *i; + + QTAILQ_FOREACH(i, &qmp_commands, node) { + if (strcmp(i->name, name) == 0) { + return i; + } + } + return NULL; +} From ab02ab2aa7a36d78a579642caa404abd99acdc6e Mon Sep 17 00:00:00 2001 From: Michael Roth Date: Tue, 19 Jul 2011 14:50:37 -0500 Subject: [PATCH 182/209] qapi: add QMP dispatch functions Given an object recieved via QMP, this code uses the dispatch table provided by qmp_registry.c to call the corresponding marshalling/dispatch function and format return values/errors for delivery to the QMP. Currently only synchronous QMP functions are supported, but this will also be used for async QMP functions and QMP guest proxy dispatch as well. Signed-off-by: Michael Roth Signed-off-by: Luiz Capitulino --- Makefile.objs | 2 +- qapi/qmp-core.h | 2 + qapi/qmp-dispatch.c | 124 ++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 127 insertions(+), 1 deletion(-) create mode 100644 qapi/qmp-dispatch.c diff --git a/Makefile.objs b/Makefile.objs index c918ee7f8f..52ad77ba91 100644 --- a/Makefile.objs +++ b/Makefile.objs @@ -376,7 +376,7 @@ libcacard-y = cac.o event.o vcard.o vreader.o vcard_emul_nss.o vcard_emul_type.o # qapi qapi-nested-y = qapi-visit-core.o qmp-input-visitor.o qmp-output-visitor.o qapi-dealloc-visitor.o -qapi-nested-y += qmp-registry.o +qapi-nested-y += qmp-registry.o qmp-dispatch.o qapi-obj-y = $(addprefix qapi/, $(qapi-nested-y)) vl.o: QEMU_CFLAGS+=$(GPROF_CFLAGS) diff --git a/qapi/qmp-core.h b/qapi/qmp-core.h index 8b96d2c676..f1c26e4b2e 100644 --- a/qapi/qmp-core.h +++ b/qapi/qmp-core.h @@ -35,5 +35,7 @@ typedef struct QmpCommand void qmp_register_command(const char *name, QmpCommandFunc *fn); QmpCommand *qmp_find_command(const char *name); +QObject *qmp_dispatch(QObject *request); #endif + diff --git a/qapi/qmp-dispatch.c b/qapi/qmp-dispatch.c new file mode 100644 index 0000000000..558469325c --- /dev/null +++ b/qapi/qmp-dispatch.c @@ -0,0 +1,124 @@ +/* + * Core Definitions for QAPI/QMP Dispatch + * + * Copyright IBM, Corp. 2011 + * + * Authors: + * Anthony Liguori + * + * This work is licensed under the terms of the GNU LGPL, version 2.1 or later. + * See the COPYING.LIB file in the top-level directory. + * + */ + +#include "qemu-objects.h" +#include "qapi/qmp-core.h" +#include "json-parser.h" +#include "error.h" +#include "error_int.h" +#include "qerror.h" + +static QDict *qmp_dispatch_check_obj(const QObject *request, Error **errp) +{ + const QDictEntry *ent; + const char *arg_name; + const QObject *arg_obj; + bool has_exec_key = false; + QDict *dict = NULL; + + if (qobject_type(request) != QTYPE_QDICT) { + error_set(errp, QERR_QMP_BAD_INPUT_OBJECT, + "request is not a dictionary"); + return NULL; + } + + dict = qobject_to_qdict(request); + + for (ent = qdict_first(dict); ent; + ent = qdict_next(dict, ent)) { + arg_name = qdict_entry_key(ent); + arg_obj = qdict_entry_value(ent); + + if (!strcmp(arg_name, "execute")) { + if (qobject_type(arg_obj) != QTYPE_QSTRING) { + error_set(errp, QERR_QMP_BAD_INPUT_OBJECT_MEMBER, "execute", + "string"); + return NULL; + } + has_exec_key = true; + } else if (strcmp(arg_name, "arguments")) { + error_set(errp, QERR_QMP_EXTRA_MEMBER, arg_name); + return NULL; + } + } + + if (!has_exec_key) { + error_set(errp, QERR_QMP_BAD_INPUT_OBJECT, "execute"); + return NULL; + } + + return dict; +} + +static QObject *do_qmp_dispatch(QObject *request, Error **errp) +{ + const char *command; + QDict *args, *dict; + QmpCommand *cmd; + QObject *ret = NULL; + + + dict = qmp_dispatch_check_obj(request, errp); + if (!dict || error_is_set(errp)) { + return NULL; + } + + command = qdict_get_str(dict, "execute"); + cmd = qmp_find_command(command); + if (cmd == NULL) { + error_set(errp, QERR_COMMAND_NOT_FOUND, command); + return NULL; + } + + if (!qdict_haskey(dict, "arguments")) { + args = qdict_new(); + } else { + args = qdict_get_qdict(dict, "arguments"); + QINCREF(args); + } + + switch (cmd->type) { + case QCT_NORMAL: + cmd->fn(args, &ret, errp); + if (!error_is_set(errp) && ret == NULL) { + ret = QOBJECT(qdict_new()); + } + break; + } + + QDECREF(args); + + return ret; +} + +QObject *qmp_dispatch(QObject *request) +{ + Error *err = NULL; + QObject *ret; + QDict *rsp; + + ret = do_qmp_dispatch(request, &err); + + rsp = qdict_new(); + if (err) { + qdict_put_obj(rsp, "error", error_get_qobject(err)); + error_free(err); + } else if (ret) { + qdict_put_obj(rsp, "return", ret); + } else { + QDECREF(rsp); + return NULL; + } + + return QOBJECT(rsp); +} From e89ac222aa3fe37b0cb0a98c572d80cd6b1729aa Mon Sep 17 00:00:00 2001 From: Michael Roth Date: Tue, 19 Jul 2011 14:50:38 -0500 Subject: [PATCH 183/209] qapi: add ordereddict.py helper library We need this to parse dictionaries with schema ordering intact so that C prototypes can be generated deterministically. Signed-off-by: Michael Roth Signed-off-by: Luiz Capitulino --- scripts/ordereddict.py | 127 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 127 insertions(+) create mode 100644 scripts/ordereddict.py diff --git a/scripts/ordereddict.py b/scripts/ordereddict.py new file mode 100644 index 0000000000..7242b5060d --- /dev/null +++ b/scripts/ordereddict.py @@ -0,0 +1,127 @@ +# Copyright (c) 2009 Raymond Hettinger +# +# 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. + +from UserDict import DictMixin + +class OrderedDict(dict, DictMixin): + + def __init__(self, *args, **kwds): + if len(args) > 1: + raise TypeError('expected at most 1 arguments, got %d' % len(args)) + try: + self.__end + except AttributeError: + self.clear() + self.update(*args, **kwds) + + def clear(self): + self.__end = end = [] + end += [None, end, end] # sentinel node for doubly linked list + self.__map = {} # key --> [key, prev, next] + dict.clear(self) + + def __setitem__(self, key, value): + if key not in self: + end = self.__end + curr = end[1] + curr[2] = end[1] = self.__map[key] = [key, curr, end] + dict.__setitem__(self, key, value) + + def __delitem__(self, key): + dict.__delitem__(self, key) + key, prev, next = self.__map.pop(key) + prev[2] = next + next[1] = prev + + def __iter__(self): + end = self.__end + curr = end[2] + while curr is not end: + yield curr[0] + curr = curr[2] + + def __reversed__(self): + end = self.__end + curr = end[1] + while curr is not end: + yield curr[0] + curr = curr[1] + + def popitem(self, last=True): + if not self: + raise KeyError('dictionary is empty') + if last: + key = reversed(self).next() + else: + key = iter(self).next() + value = self.pop(key) + return key, value + + def __reduce__(self): + items = [[k, self[k]] for k in self] + tmp = self.__map, self.__end + del self.__map, self.__end + inst_dict = vars(self).copy() + self.__map, self.__end = tmp + if inst_dict: + return (self.__class__, (items,), inst_dict) + return self.__class__, (items,) + + def keys(self): + return list(self) + + setdefault = DictMixin.setdefault + update = DictMixin.update + pop = DictMixin.pop + values = DictMixin.values + items = DictMixin.items + iterkeys = DictMixin.iterkeys + itervalues = DictMixin.itervalues + iteritems = DictMixin.iteritems + + def __repr__(self): + if not self: + return '%s()' % (self.__class__.__name__,) + return '%s(%r)' % (self.__class__.__name__, self.items()) + + def copy(self): + return self.__class__(self) + + @classmethod + def fromkeys(cls, iterable, value=None): + d = cls() + for key in iterable: + d[key] = value + return d + + def __eq__(self, other): + if isinstance(other, OrderedDict): + if len(self) != len(other): + return False + for p, q in zip(self.items(), other.items()): + if p != q: + return False + return True + return dict.__eq__(self, other) + + def __ne__(self, other): + return not self == other From 0f923be25321a479405443f33c50c79ee9f5b628 Mon Sep 17 00:00:00 2001 From: Michael Roth Date: Tue, 19 Jul 2011 14:50:39 -0500 Subject: [PATCH 184/209] qapi: add qapi.py helper libraries Signed-off-by: Michael Roth Signed-off-by: Luiz Capitulino --- scripts/qapi.py | 203 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 203 insertions(+) create mode 100644 scripts/qapi.py diff --git a/scripts/qapi.py b/scripts/qapi.py new file mode 100644 index 0000000000..56af2329bb --- /dev/null +++ b/scripts/qapi.py @@ -0,0 +1,203 @@ +# +# QAPI helper library +# +# Copyright IBM, Corp. 2011 +# +# Authors: +# Anthony Liguori +# +# This work is licensed under the terms of the GNU GPLv2. +# See the COPYING.LIB file in the top-level directory. + +from ordereddict import OrderedDict + +def tokenize(data): + while len(data): + if data[0] in ['{', '}', ':', ',', '[', ']']: + yield data[0] + data = data[1:] + elif data[0] in ' \n': + data = data[1:] + elif data[0] == "'": + data = data[1:] + string = '' + while data[0] != "'": + string += data[0] + data = data[1:] + data = data[1:] + yield string + +def parse(tokens): + if tokens[0] == '{': + ret = OrderedDict() + tokens = tokens[1:] + while tokens[0] != '}': + key = tokens[0] + tokens = tokens[1:] + + tokens = tokens[1:] # : + + value, tokens = parse(tokens) + + if tokens[0] == ',': + tokens = tokens[1:] + + ret[key] = value + tokens = tokens[1:] + return ret, tokens + elif tokens[0] == '[': + ret = [] + tokens = tokens[1:] + while tokens[0] != ']': + value, tokens = parse(tokens) + if tokens[0] == ',': + tokens = tokens[1:] + ret.append(value) + tokens = tokens[1:] + return ret, tokens + else: + return tokens[0], tokens[1:] + +def evaluate(string): + return parse(map(lambda x: x, tokenize(string)))[0] + +def parse_schema(fp): + exprs = [] + expr = '' + expr_eval = None + + for line in fp: + if line.startswith('#') or line == '\n': + continue + + if line.startswith(' '): + expr += line + elif expr: + expr_eval = evaluate(expr) + if expr_eval.has_key('enum'): + add_enum(expr_eval['enum']) + elif expr_eval.has_key('union'): + add_enum('%sKind' % expr_eval['union']) + exprs.append(expr_eval) + expr = line + else: + expr += line + + if expr: + expr_eval = evaluate(expr) + if expr_eval.has_key('enum'): + add_enum(expr_eval['enum']) + elif expr_eval.has_key('union'): + add_enum('%sKind' % expr_eval['union']) + exprs.append(expr_eval) + + return exprs + +def parse_args(typeinfo): + for member in typeinfo: + argname = member + argentry = typeinfo[member] + optional = False + structured = False + if member.startswith('*'): + argname = member[1:] + optional = True + if isinstance(argentry, OrderedDict): + structured = True + yield (argname, argentry, optional, structured) + +def de_camel_case(name): + new_name = '' + for ch in name: + if ch.isupper() and new_name: + new_name += '_' + if ch == '-': + new_name += '_' + else: + new_name += ch.lower() + return new_name + +def camel_case(name): + new_name = '' + first = True + for ch in name: + if ch in ['_', '-']: + first = True + elif first: + new_name += ch.upper() + first = False + else: + new_name += ch.lower() + return new_name + +def c_var(name): + return '_'.join(name.split('-')).lstrip("*") + +def c_list_type(name): + return '%sList' % name + +def type_name(name): + if type(name) == list: + return c_list_type(name[0]) + return name + +enum_types = [] + +def add_enum(name): + global enum_types + enum_types.append(name) + +def is_enum(name): + global enum_types + return (name in enum_types) + +def c_type(name): + if name == 'str': + return 'char *' + elif name == 'int': + return 'int64_t' + elif name == 'bool': + return 'bool' + elif name == 'number': + return 'double' + elif type(name) == list: + return '%s *' % c_list_type(name[0]) + elif is_enum(name): + return name + elif name == None or len(name) == 0: + return 'void' + elif name == name.upper(): + return '%sEvent *' % camel_case(name) + else: + return '%s *' % name + +def genindent(count): + ret = "" + for i in range(count): + ret += " " + return ret + +indent_level = 0 + +def push_indent(indent_amount=4): + global indent_level + indent_level += indent_amount + +def pop_indent(indent_amount=4): + global indent_level + indent_level -= indent_amount + +def cgen(code, **kwds): + indent = genindent(indent_level) + lines = code.split('\n') + lines = map(lambda x: indent + x, lines) + return '\n'.join(lines) % kwds + '\n' + +def mcgen(code, **kwds): + return cgen('\n'.join(code.split('\n')[1:-1]), **kwds) + +def basename(filename): + return filename.split("/")[-1] + +def guardname(filename): + return filename.replace("/", "_").replace("-", "_").split(".")[0].upper() From fb3182ce6e200a0fedc603d444034d6cbc3d3f0f Mon Sep 17 00:00:00 2001 From: Michael Roth Date: Tue, 19 Jul 2011 14:50:40 -0500 Subject: [PATCH 185/209] qapi: add qapi-types.py code generator This is the code generator for qapi types. It will generation the following files: $(prefix)qapi-types.h - C types corresponding to types defined in the schema you pass in $(prefix)qapi-types.c - Cleanup functions for the above C types The $(prefix) is used to as a namespace to keep the generated code from one schema/code-generation separated from others so code and be generated from multiple schemas with clobbering previously created code. Signed-off-by: Michael Roth Signed-off-by: Luiz Capitulino --- scripts/qapi-types.py | 270 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 270 insertions(+) create mode 100644 scripts/qapi-types.py diff --git a/scripts/qapi-types.py b/scripts/qapi-types.py new file mode 100644 index 0000000000..cece32546a --- /dev/null +++ b/scripts/qapi-types.py @@ -0,0 +1,270 @@ +# +# QAPI types generator +# +# Copyright IBM, Corp. 2011 +# +# Authors: +# Anthony Liguori +# +# This work is licensed under the terms of the GNU GPLv2. +# See the COPYING.LIB file in the top-level directory. + +from ordereddict import OrderedDict +from qapi import * +import sys +import os +import getopt +import errno + +def generate_fwd_struct(name, members): + return mcgen(''' +typedef struct %(name)s %(name)s; + +typedef struct %(name)sList +{ + %(name)s *value; + struct %(name)sList *next; +} %(name)sList; +''', + name=name) + +def generate_struct(structname, fieldname, members): + ret = mcgen(''' +struct %(name)s +{ +''', + name=structname) + + for argname, argentry, optional, structured in parse_args(members): + if optional: + ret += mcgen(''' + bool has_%(c_name)s; +''', + c_name=c_var(argname)) + if structured: + push_indent() + ret += generate_struct("", argname, argentry) + pop_indent() + else: + ret += mcgen(''' + %(c_type)s %(c_name)s; +''', + c_type=c_type(argentry), c_name=c_var(argname)) + + if len(fieldname): + fieldname = " " + fieldname + ret += mcgen(''' +}%(field)s; +''', + field=fieldname) + + return ret + +def generate_enum_lookup(name, values): + ret = mcgen(''' +const char *%(name)s_lookup[] = { +''', + name=name) + i = 0 + for value in values: + ret += mcgen(''' + "%(value)s", +''', + value=c_var(value).lower()) + + ret += mcgen(''' + NULL, +}; + +''') + return ret + +def generate_enum(name, values): + lookup_decl = mcgen(''' +extern const char *%(name)s_lookup[]; +''', + name=name) + + enum_decl = mcgen(''' +typedef enum %(name)s +{ +''', + name=name) + + i = 0 + for value in values: + enum_decl += mcgen(''' + %(abbrev)s_%(value)s = %(i)d, +''', + abbrev=de_camel_case(name).upper(), + value=c_var(value).upper(), + i=i) + i += 1 + + enum_decl += mcgen(''' +} %(name)s; +''', + name=name) + + return lookup_decl + enum_decl + +def generate_union(name, typeinfo): + ret = mcgen(''' +struct %(name)s +{ + %(name)sKind kind; + union { +''', + name=name) + + for key in typeinfo: + ret += mcgen(''' + %(c_type)s %(c_name)s; +''', + c_type=c_type(typeinfo[key]), + c_name=c_var(key)) + + ret += mcgen(''' + }; +}; +''') + + return ret + +def generate_type_cleanup_decl(name): + ret = mcgen(''' +void qapi_free_%(type)s(%(c_type)s obj); +''', + c_type=c_type(name),type=name) + return ret + +def generate_type_cleanup(name): + ret = mcgen(''' +void qapi_free_%(type)s(%(c_type)s obj) +{ + QapiDeallocVisitor *md; + Visitor *v; + + if (!obj) { + return; + } + + md = qapi_dealloc_visitor_new(); + v = qapi_dealloc_get_visitor(md); + visit_type_%(type)s(v, &obj, NULL, NULL); + qapi_dealloc_visitor_cleanup(md); +} +''', + c_type=c_type(name),type=name) + return ret + + +try: + opts, args = getopt.gnu_getopt(sys.argv[1:], "p:o:", ["prefix=", "output-dir="]) +except getopt.GetoptError, err: + print str(err) + sys.exit(1) + +output_dir = "" +prefix = "" +c_file = 'qapi-types.c' +h_file = 'qapi-types.h' + +for o, a in opts: + if o in ("-p", "--prefix"): + prefix = a + elif o in ("-o", "--output-dir"): + output_dir = a + "/" + +c_file = output_dir + prefix + c_file +h_file = output_dir + prefix + h_file + +try: + os.makedirs(output_dir) +except os.error, e: + if e.errno != errno.EEXIST: + raise + +fdef = open(c_file, 'w') +fdecl = open(h_file, 'w') + +fdef.write(mcgen(''' +/* AUTOMATICALLY GENERATED, DO NOT MODIFY */ + +/* + * deallocation functions for schema-defined QAPI types + * + * Copyright IBM, Corp. 2011 + * + * Authors: + * Anthony Liguori + * Michael Roth + * + * This work is licensed under the terms of the GNU LGPL, version 2.1 or later. + * See the COPYING.LIB file in the top-level directory. + * + */ + +#include "qapi/qapi-dealloc-visitor.h" +#include "%(prefix)sqapi-types.h" +#include "%(prefix)sqapi-visit.h" + +''', prefix=prefix)) + +fdecl.write(mcgen(''' +/* AUTOMATICALLY GENERATED, DO NOT MODIFY */ + +/* + * schema-defined QAPI types + * + * Copyright IBM, Corp. 2011 + * + * Authors: + * Anthony Liguori + * + * This work is licensed under the terms of the GNU LGPL, version 2.1 or later. + * See the COPYING.LIB file in the top-level directory. + * + */ + +#ifndef %(guard)s +#define %(guard)s + +#include "qapi/qapi-types-core.h" +''', + guard=guardname(h_file))) + +exprs = parse_schema(sys.stdin) + +for expr in exprs: + ret = "\n" + if expr.has_key('type'): + ret += generate_fwd_struct(expr['type'], expr['data']) + elif expr.has_key('enum'): + ret += generate_enum(expr['enum'], expr['data']) + fdef.write(generate_enum_lookup(expr['enum'], expr['data'])) + elif expr.has_key('union'): + ret += generate_fwd_struct(expr['union'], expr['data']) + "\n" + ret += generate_enum('%sKind' % expr['union'], expr['data'].keys()) + else: + continue + fdecl.write(ret) + +for expr in exprs: + ret = "\n" + if expr.has_key('type'): + ret += generate_struct(expr['type'], "", expr['data']) + "\n" + ret += generate_type_cleanup_decl(expr['type']) + fdef.write(generate_type_cleanup(expr['type']) + "\n") + elif expr.has_key('union'): + ret += generate_union(expr['union'], expr['data']) + else: + continue + fdecl.write(ret) + +fdecl.write(''' +#endif +''') + +fdecl.flush() +fdecl.close() From 06d64c62ddc38c77af775c16165d4b38f43d02e9 Mon Sep 17 00:00:00 2001 From: Michael Roth Date: Tue, 19 Jul 2011 14:50:41 -0500 Subject: [PATCH 186/209] qapi: add qapi-visit.py code generator This is the code generator for qapi visiter functions used to marshal/unmarshal/dealloc qapi types. It generates the following 2 files: $(prefix)qapi-visit.c: visiter function for a particular c type, used to automagically convert qobjects into the corresponding C type and vice-versa, and well as for deallocation memory for an existing C type $(prefix)qapi-visit.h: declarations for previously mentioned visiter functions $(prefix) is used as decribed for qapi-types.py Signed-off-by: Michael Roth Signed-off-by: Luiz Capitulino --- scripts/qapi-visit.py | 246 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 246 insertions(+) create mode 100644 scripts/qapi-visit.py diff --git a/scripts/qapi-visit.py b/scripts/qapi-visit.py new file mode 100644 index 0000000000..252230ef25 --- /dev/null +++ b/scripts/qapi-visit.py @@ -0,0 +1,246 @@ +# +# QAPI visitor generator +# +# Copyright IBM, Corp. 2011 +# +# Authors: +# Anthony Liguori +# Michael Roth +# +# This work is licensed under the terms of the GNU GPLv2. +# See the COPYING.LIB file in the top-level directory. + +from ordereddict import OrderedDict +from qapi import * +import sys +import os +import getopt +import errno + +def generate_visit_struct_body(field_prefix, members): + ret = "" + if len(field_prefix): + field_prefix = field_prefix + "." + for argname, argentry, optional, structured in parse_args(members): + if optional: + ret += mcgen(''' +visit_start_optional(m, (obj && *obj) ? &(*obj)->%(c_prefix)shas_%(c_name)s : NULL, "%(name)s", errp); +if ((*obj)->%(prefix)shas_%(c_name)s) { +''', + c_prefix=c_var(field_prefix), prefix=field_prefix, + c_name=c_var(argname), name=argname) + push_indent() + + if structured: + ret += mcgen(''' +visit_start_struct(m, NULL, "", "%(name)s", 0, errp); +''', + name=argname) + ret += generate_visit_struct_body(field_prefix + argname, argentry) + ret += mcgen(''' +visit_end_struct(m, errp); +''') + else: + ret += mcgen(''' +visit_type_%(type)s(m, (obj && *obj) ? &(*obj)->%(c_prefix)s%(c_name)s : NULL, "%(name)s", errp); +''', + c_prefix=c_var(field_prefix), prefix=field_prefix, + type=type_name(argentry), c_name=c_var(argname), + name=argname) + + if optional: + pop_indent() + ret += mcgen(''' +} +visit_end_optional(m, errp); +''') + return ret + +def generate_visit_struct(name, members): + ret = mcgen(''' + +void visit_type_%(name)s(Visitor *m, %(name)s ** obj, const char *name, Error **errp) +{ + visit_start_struct(m, (void **)obj, "%(name)s", name, sizeof(%(name)s), errp); +''', + name=name) + push_indent() + ret += generate_visit_struct_body("", members) + pop_indent() + + ret += mcgen(''' + visit_end_struct(m, errp); +} +''') + return ret + +def generate_visit_list(name, members): + return mcgen(''' + +void visit_type_%(name)sList(Visitor *m, %(name)sList ** obj, const char *name, Error **errp) +{ + GenericList *i; + + visit_start_list(m, name, errp); + + for (i = visit_next_list(m, (GenericList **)obj, errp); i; i = visit_next_list(m, &i, errp)) { + %(name)sList *native_i = (%(name)sList *)i; + visit_type_%(name)s(m, &native_i->value, NULL, errp); + } + + visit_end_list(m, errp); +} +''', + name=name) + +def generate_visit_enum(name, members): + return mcgen(''' + +void visit_type_%(name)s(Visitor *m, %(name)s * obj, const char *name, Error **errp) +{ + visit_type_enum(m, (int *)obj, %(name)s_lookup, "%(name)s", name, errp); +} +''', + name=name) + +def generate_visit_union(name, members): + ret = generate_visit_enum('%sKind' % name, members.keys()) + + ret += mcgen(''' + +void visit_type_%(name)s(Visitor *m, %(name)s ** obj, const char *name, Error **errp) +{ +} +''', + name=name) + + return ret + +def generate_declaration(name, members, genlist=True): + ret = mcgen(''' + +void visit_type_%(name)s(Visitor *m, %(name)s ** obj, const char *name, Error **errp); +''', + name=name) + + if genlist: + ret += mcgen(''' +void visit_type_%(name)sList(Visitor *m, %(name)sList ** obj, const char *name, Error **errp); +''', + name=name) + + return ret + +def generate_decl_enum(name, members, genlist=True): + return mcgen(''' + +void visit_type_%(name)s(Visitor *m, %(name)s * obj, const char *name, Error **errp); +''', + name=name) + +try: + opts, args = getopt.gnu_getopt(sys.argv[1:], "p:o:", ["prefix=", "output-dir="]) +except getopt.GetoptError, err: + print str(err) + sys.exit(1) + +output_dir = "" +prefix = "" +c_file = 'qapi-visit.c' +h_file = 'qapi-visit.h' + +for o, a in opts: + if o in ("-p", "--prefix"): + prefix = a + elif o in ("-o", "--output-dir"): + output_dir = a + "/" + +c_file = output_dir + prefix + c_file +h_file = output_dir + prefix + h_file + +try: + os.makedirs(output_dir) +except os.error, e: + if e.errno != errno.EEXIST: + raise + +fdef = open(c_file, 'w') +fdecl = open(h_file, 'w') + +fdef.write(mcgen(''' +/* THIS FILE IS AUTOMATICALLY GENERATED, DO NOT MODIFY */ + +/* + * schema-defined QAPI visitor functions + * + * Copyright IBM, Corp. 2011 + * + * Authors: + * Anthony Liguori + * + * This work is licensed under the terms of the GNU LGPL, version 2.1 or later. + * See the COPYING.LIB file in the top-level directory. + * + */ + +#include "%(header)s" +''', + header=basename(h_file))) + +fdecl.write(mcgen(''' +/* THIS FILE IS AUTOMATICALLY GENERATED, DO NOT MODIFY */ + +/* + * schema-defined QAPI visitor function + * + * Copyright IBM, Corp. 2011 + * + * Authors: + * Anthony Liguori + * + * This work is licensed under the terms of the GNU LGPL, version 2.1 or later. + * See the COPYING.LIB file in the top-level directory. + * + */ + +#ifndef %(guard)s +#define %(guard)s + +#include "qapi/qapi-visit-core.h" +#include "%(prefix)sqapi-types.h" +''', + prefix=prefix, guard=guardname(h_file))) + +exprs = parse_schema(sys.stdin) + +for expr in exprs: + if expr.has_key('type'): + ret = generate_visit_struct(expr['type'], expr['data']) + ret += generate_visit_list(expr['type'], expr['data']) + fdef.write(ret) + + ret = generate_declaration(expr['type'], expr['data']) + fdecl.write(ret) + elif expr.has_key('union'): + ret = generate_visit_union(expr['union'], expr['data']) + fdef.write(ret) + + ret = generate_decl_enum('%sKind' % expr['union'], expr['data'].keys()) + ret += generate_declaration(expr['union'], expr['data']) + fdecl.write(ret) + elif expr.has_key('enum'): + ret = generate_visit_enum(expr['enum'], expr['data']) + fdef.write(ret) + + ret = generate_decl_enum(expr['enum'], expr['data']) + fdecl.write(ret) + +fdecl.write(''' +#endif +''') + +fdecl.flush() +fdecl.close() + +fdef.flush() +fdef.close() From c17d9908a942e355c70bfb32b6ebdc0e6e5daa87 Mon Sep 17 00:00:00 2001 From: Michael Roth Date: Tue, 19 Jul 2011 14:50:42 -0500 Subject: [PATCH 187/209] qapi: add qapi-commands.py code generator This is the code generator for qapi command marshaling/dispatch. Currently only generators for synchronous qapi/qmp functions are supported. This script generates the following files: $(prefix)qmp-marshal.c: command marshal/dispatch functions for each QMP command defined in the schema. Functions generated by qapi-visit.py are used to convert qobjects recieved from the wire into function parameters, and uses the same visiter functions to convert native C return values to qobjects from transmission back over the wire. $(prefix)qmp-commands.h: Function prototypes for the QMP commands specified in the schema. $(prefix) is used in the same manner as with qapi-types.py Signed-off-by: Michael Roth Signed-off-by: Luiz Capitulino --- scripts/qapi-commands.py | 385 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 385 insertions(+) create mode 100644 scripts/qapi-commands.py diff --git a/scripts/qapi-commands.py b/scripts/qapi-commands.py new file mode 100644 index 0000000000..9ad4c54991 --- /dev/null +++ b/scripts/qapi-commands.py @@ -0,0 +1,385 @@ +# +# QAPI command marshaller generator +# +# Copyright IBM, Corp. 2011 +# +# Authors: +# Anthony Liguori +# Michael Roth +# +# This work is licensed under the terms of the GNU GPLv2. +# See the COPYING.LIB file in the top-level directory. + +from ordereddict import OrderedDict +from qapi import * +import sys +import os +import getopt +import errno + +def generate_decl_enum(name, members, genlist=True): + return mcgen(''' + +void visit_type_%(name)s(Visitor *m, %(name)s * obj, const char *name, Error **errp); +''', + name=name) + +def generate_command_decl(name, args, ret_type): + arglist="" + for argname, argtype, optional, structured in parse_args(args): + argtype = c_type(argtype) + if argtype == "char *": + argtype = "const char *" + if optional: + arglist += "bool has_%s, " % c_var(argname) + arglist += "%s %s, " % (argtype, c_var(argname)) + return mcgen(''' +%(ret_type)s qmp_%(name)s(%(args)sError **errp); +''', + ret_type=c_type(ret_type), name=c_var(name), args=arglist).strip() + +def gen_sync_call(name, args, ret_type, indent=0): + ret = "" + arglist="" + retval="" + if ret_type: + retval = "retval = " + for argname, argtype, optional, structured in parse_args(args): + if optional: + arglist += "has_%s, " % c_var(argname) + arglist += "%s, " % (c_var(argname)) + push_indent(indent) + ret = mcgen(''' +%(retval)sqmp_%(name)s(%(args)serrp); + +''', + name=c_var(name), args=arglist, retval=retval).rstrip() + if ret_type: + ret += "\n" + mcgen('''' +%(marshal_output_call)s +''', + marshal_output_call=gen_marshal_output_call(name, ret_type)).rstrip() + pop_indent(indent) + return ret.rstrip() + + +def gen_marshal_output_call(name, ret_type): + if not ret_type: + return "" + return "qmp_marshal_output_%s(retval, ret, errp);" % c_var(name) + +def gen_visitor_output_containers_decl(ret_type): + ret = "" + push_indent() + if ret_type: + ret += mcgen(''' +QmpOutputVisitor *mo; +QapiDeallocVisitor *md; +Visitor *v; +''') + pop_indent() + + return ret + +def gen_visitor_input_containers_decl(args): + ret = "" + + push_indent() + if len(args) > 0: + ret += mcgen(''' +QmpInputVisitor *mi; +QapiDeallocVisitor *md; +Visitor *v; +''') + pop_indent() + + return ret.rstrip() + +def gen_visitor_input_vars_decl(args): + ret = "" + push_indent() + for argname, argtype, optional, structured in parse_args(args): + if optional: + ret += mcgen(''' +bool has_%(argname)s = false; +''', + argname=c_var(argname)) + if c_type(argtype).endswith("*"): + ret += mcgen(''' +%(argtype)s %(argname)s = NULL; +''', + argname=c_var(argname), argtype=c_type(argtype)) + else: + ret += mcgen(''' +%(argtype)s %(argname)s; +''', + argname=c_var(argname), argtype=c_type(argtype)) + + pop_indent() + return ret.rstrip() + +def gen_visitor_input_block(args, obj, dealloc=False): + ret = "" + if len(args) == 0: + return ret + + push_indent() + + if dealloc: + ret += mcgen(''' +md = qapi_dealloc_visitor_new(); +v = qapi_dealloc_get_visitor(md); +''') + else: + ret += mcgen(''' +mi = qmp_input_visitor_new(%(obj)s); +v = qmp_input_get_visitor(mi); +''', + obj=obj) + + for argname, argtype, optional, structured in parse_args(args): + if optional: + ret += mcgen(''' +visit_start_optional(v, &has_%(c_name)s, "%(name)s", errp); +if (has_%(c_name)s) { +''', + c_name=c_var(argname), name=argname) + push_indent() + ret += mcgen(''' +visit_type_%(argtype)s(v, &%(c_name)s, "%(name)s", errp); +''', + c_name=c_var(argname), name=argname, argtype=argtype) + if optional: + pop_indent() + ret += mcgen(''' +} +visit_end_optional(v, errp); +''') + + if dealloc: + ret += mcgen(''' +qapi_dealloc_visitor_cleanup(md); +''') + else: + ret += mcgen(''' +qmp_input_visitor_cleanup(mi); +''') + pop_indent() + return ret.rstrip() + +def gen_marshal_output(name, args, ret_type): + if not ret_type: + return "" + ret = mcgen(''' +static void qmp_marshal_output_%(c_name)s(%(c_ret_type)s ret_in, QObject **ret_out, Error **errp) +{ + QapiDeallocVisitor *md = qapi_dealloc_visitor_new(); + QmpOutputVisitor *mo = qmp_output_visitor_new(); + Visitor *v; + + v = qmp_output_get_visitor(mo); + visit_type_%(ret_type)s(v, &ret_in, "unused", errp); + if (!error_is_set(errp)) { + *ret_out = qmp_output_get_qobject(mo); + } + qmp_output_visitor_cleanup(mo); + v = qapi_dealloc_get_visitor(md); + visit_type_%(ret_type)s(v, &ret_in, "unused", errp); + qapi_dealloc_visitor_cleanup(md); +} +''', + c_ret_type=c_type(ret_type), c_name=c_var(name), ret_type=ret_type) + + return ret + +def gen_marshal_input(name, args, ret_type): + ret = mcgen(''' +static void qmp_marshal_input_%(c_name)s(QDict *args, QObject **ret, Error **errp) +{ +''', + c_name=c_var(name)) + + if ret_type: + if c_type(ret_type).endswith("*"): + retval = " %s retval = NULL;" % c_type(ret_type) + else: + retval = " %s retval;" % c_type(ret_type) + ret += mcgen(''' +%(retval)s +''', + retval=retval) + + if len(args) > 0: + ret += mcgen(''' +%(visitor_input_containers_decl)s +%(visitor_input_vars_decl)s + +%(visitor_input_block)s + +''', + visitor_input_containers_decl=gen_visitor_input_containers_decl(args), + visitor_input_vars_decl=gen_visitor_input_vars_decl(args), + visitor_input_block=gen_visitor_input_block(args, "QOBJECT(args)")) + + ret += mcgen(''' + if (error_is_set(errp)) { + goto out; + } +%(sync_call)s +''', + sync_call=gen_sync_call(name, args, ret_type, indent=4)) + ret += mcgen(''' + +out: +''') + ret += mcgen(''' +%(visitor_input_block_cleanup)s + return; +} +''', + visitor_input_block_cleanup=gen_visitor_input_block(args, None, dealloc=True)) + return ret + +def gen_registry(commands): + registry="" + push_indent() + for cmd in commands: + registry += mcgen(''' +qmp_register_command("%(name)s", qmp_marshal_input_%(c_name)s); +''', + name=cmd['command'], c_name=c_var(cmd['command'])) + pop_indent() + ret = mcgen(''' +static void qmp_init_marshal(void) +{ +%(registry)s +} + +qapi_init(qmp_init_marshal); +''', + registry=registry.rstrip()) + return ret + +def gen_command_decl_prologue(header, guard, prefix=""): + ret = mcgen(''' +/* THIS FILE IS AUTOMATICALLY GENERATED, DO NOT MODIFY */ + +/* + * schema-defined QAPI function prototypes + * + * Copyright IBM, Corp. 2011 + * + * Authors: + * Anthony Liguori + * + * This work is licensed under the terms of the GNU LGPL, version 2.1 or later. + * See the COPYING.LIB file in the top-level directory. + * + */ + +#ifndef %(guard)s +#define %(guard)s + +#include "%(prefix)sqapi-types.h" +#include "error.h" + +''', + header=basename(h_file), guard=guardname(h_file), prefix=prefix) + return ret + +def gen_command_def_prologue(prefix="", proxy=False): + ret = mcgen(''' +/* THIS FILE IS AUTOMATICALLY GENERATED, DO NOT MODIFY */ + +/* + * schema-defined QMP->QAPI command dispatch + * + * Copyright IBM, Corp. 2011 + * + * Authors: + * Anthony Liguori + * + * This work is licensed under the terms of the GNU LGPL, version 2.1 or later. + * See the COPYING.LIB file in the top-level directory. + * + */ + +#include "qemu-objects.h" +#include "qapi/qmp-core.h" +#include "qapi/qapi-visit-core.h" +#include "qapi/qmp-output-visitor.h" +#include "qapi/qmp-input-visitor.h" +#include "qapi/qapi-dealloc-visitor.h" +#include "%(prefix)sqapi-types.h" +#include "%(prefix)sqapi-visit.h" + +''', + prefix=prefix) + if not proxy: + ret += '#include "%sqmp-commands.h"' % prefix + return ret + "\n" + + +try: + opts, args = getopt.gnu_getopt(sys.argv[1:], "p:o:", ["prefix=", "output-dir=", "type="]) +except getopt.GetoptError, err: + print str(err) + sys.exit(1) + +output_dir = "" +prefix = "" +dispatch_type = "sync" +c_file = 'qmp-marshal.c' +h_file = 'qmp-commands.h' + +for o, a in opts: + if o in ("-p", "--prefix"): + prefix = a + elif o in ("-o", "--output-dir"): + output_dir = a + "/" + elif o in ("-t", "--type"): + dispatch_type = a + +c_file = output_dir + prefix + c_file +h_file = output_dir + prefix + h_file + +try: + os.makedirs(output_dir) +except os.error, e: + if e.errno != errno.EEXIST: + raise + +exprs = parse_schema(sys.stdin) +commands = filter(lambda expr: expr.has_key('command'), exprs) + +if dispatch_type == "sync": + fdecl = open(h_file, 'w') + fdef = open(c_file, 'w') + ret = gen_command_decl_prologue(header=basename(h_file), guard=guardname(h_file), prefix=prefix) + fdecl.write(ret) + ret = gen_command_def_prologue(prefix=prefix) + fdef.write(ret) + + for cmd in commands: + arglist = [] + ret_type = None + if cmd.has_key('data'): + arglist = cmd['data'] + if cmd.has_key('returns'): + ret_type = cmd['returns'] + ret = generate_command_decl(cmd['command'], arglist, ret_type) + "\n" + fdecl.write(ret) + if ret_type: + ret = gen_marshal_output(cmd['command'], arglist, ret_type) + "\n" + fdef.write(ret) + ret = gen_marshal_input(cmd['command'], arglist, ret_type) + "\n" + fdef.write(ret) + + fdecl.write("\n#endif"); + ret = gen_registry(commands) + fdef.write(ret) + + fdef.flush() + fdef.close() + fdecl.flush() + fdecl.close() From 501e5104490fca89ad31e6a430e2cf01f928d5d1 Mon Sep 17 00:00:00 2001 From: Michael Roth Date: Tue, 19 Jul 2011 14:50:43 -0500 Subject: [PATCH 188/209] qapi: test schema used for unit tests This is how QMP commands/parameters/types would be defined. We use a subset of that functionality here to implement functions/types for unit testing. Signed-off-by: Michael Roth Signed-off-by: Luiz Capitulino --- qapi-schema-test.json | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) create mode 100644 qapi-schema-test.json diff --git a/qapi-schema-test.json b/qapi-schema-test.json new file mode 100644 index 0000000000..3acedad7ee --- /dev/null +++ b/qapi-schema-test.json @@ -0,0 +1,22 @@ +# *-*- Mode: Python -*-* + +# for testing enums +{ 'enum': 'EnumOne', + 'data': [ 'value1', 'value2', 'value3' ] } +{ 'type': 'NestedEnumsOne', + 'data': { 'enum1': 'EnumOne', '*enum2': 'EnumOne', 'enum3': 'EnumOne', '*enum4': 'EnumOne' } } + +# for testing nested structs +{ 'type': 'UserDefOne', + 'data': { 'integer': 'int', 'string': 'str' } } + +{ 'type': 'UserDefTwo', + 'data': { 'string': 'str', + 'dict': { 'string': 'str', + 'dict': { 'userdef': 'UserDefOne', 'string': 'str' }, + '*dict2': { 'userdef': 'UserDefOne', 'string': 'str' } } } } + +# testing commands +{ 'command': 'user_def_cmd', 'data': {} } +{ 'command': 'user_def_cmd1', 'data': {'ud1a': 'UserDefOne'} } +{ 'command': 'user_def_cmd2', 'data': {'ud1a': 'UserDefOne', 'ud1b': 'UserDefOne'}, 'returns': 'UserDefTwo' } From 640e540446d174144784225bfe223d8e40e7736d Mon Sep 17 00:00:00 2001 From: Michael Roth Date: Tue, 19 Jul 2011 14:50:44 -0500 Subject: [PATCH 189/209] qapi: add test-visitor, tests for gen. visitor code Signed-off-by: Michael Roth Signed-off-by: Luiz Capitulino --- Makefile | 19 ++- test-visitor.c | 306 +++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 323 insertions(+), 2 deletions(-) create mode 100644 test-visitor.c diff --git a/Makefile b/Makefile index 42ae4e52d2..5d2cf5b3c9 100644 --- a/Makefile +++ b/Makefile @@ -162,6 +162,20 @@ check-qlist: check-qlist.o qlist.o qint.o $(CHECK_PROG_DEPS) check-qfloat: check-qfloat.o qfloat.o $(CHECK_PROG_DEPS) check-qjson: check-qjson.o qfloat.o qint.o qdict.o qstring.o qlist.o qbool.o qjson.o json-streamer.o json-lexer.o json-parser.o error.o qerror.o qemu-error.o $(CHECK_PROG_DEPS) +$(qapi-obj-y): $(GENERATED_HEADERS) +qapi-dir := qapi-generated +test-visitor.o: QEMU_CFLAGS += -I $(qapi-dir) + +$(qapi-dir)/test-qapi-types.c: $(qapi-dir)/test-qapi-types.h +$(qapi-dir)/test-qapi-types.h: $(SRC_PATH)/qapi-schema-test.json $(SRC_PATH)/scripts/qapi-types.py + $(call quiet-command,python $(SRC_PATH)/scripts/qapi-types.py -o "$(qapi-dir)" -p "test-" < $<, " GEN $@") +$(qapi-dir)/test-qapi-visit.c: $(qapi-dir)/test-qapi-visit.h +$(qapi-dir)/test-qapi-visit.h: $(SRC_PATH)/qapi-schema-test.json $(SRC_PATH)/scripts/qapi-visit.py + $(call quiet-command,python $(SRC_PATH)/scripts/qapi-visit.py -o "$(qapi-dir)" -p "test-" < $<, " GEN $@") + +test-visitor.o: $(addprefix $(qapi-dir)/, test-qapi-types.c test-qapi-types.h test-qapi-visit.c test-qapi-visit.h) $(qapi-obj-y) +test-visitor: test-visitor.o qfloat.o qint.o qdict.o qstring.o qlist.o qbool.o $(qapi-obj-y) error.o osdep.o qemu-malloc.o $(oslib-obj-y) qjson.o json-streamer.o json-lexer.o json-parser.o qerror.o qemu-error.o qemu-tool.o $(qapi-dir)/test-qapi-visit.o $(qapi-dir)/test-qapi-types.o + QEMULIBS=libhw32 libhw64 libuser libdis libdis-user clean: @@ -170,11 +184,12 @@ clean: rm -f qemu-options.def rm -f *.o *.d *.a *.lo $(TOOLS) TAGS cscope.* *.pod *~ */*~ rm -Rf .libs - rm -f slirp/*.o slirp/*.d audio/*.o audio/*.d block/*.o block/*.d net/*.o net/*.d fsdev/*.o fsdev/*.d ui/*.o ui/*.d + rm -f slirp/*.o slirp/*.d audio/*.o audio/*.d block/*.o block/*.d net/*.o net/*.d fsdev/*.o fsdev/*.d ui/*.o ui/*.d qapi/*.o qapi/*.d rm -f qemu-img-cmds.h rm -f trace.c trace.h trace.c-timestamp trace.h-timestamp rm -f trace-dtrace.dtrace trace-dtrace.dtrace-timestamp rm -f trace-dtrace.h trace-dtrace.h-timestamp + rm -rf $(qapi-dir) $(MAKE) -C tests clean for d in $(ALL_SUBDIRS) $(QEMULIBS) libcacard; do \ if test -d $$d; then $(MAKE) -C $$d $@ || exit 1; fi; \ @@ -363,4 +378,4 @@ tarbin: $(mandir)/man8/qemu-nbd.8 # Include automatically generated dependency files --include $(wildcard *.d audio/*.d slirp/*.d block/*.d net/*.d ui/*.d) +-include $(wildcard *.d audio/*.d slirp/*.d block/*.d net/*.d ui/*.d qapi/*.d) diff --git a/test-visitor.c b/test-visitor.c new file mode 100644 index 0000000000..5133ad6b19 --- /dev/null +++ b/test-visitor.c @@ -0,0 +1,306 @@ +#include +#include "qapi/qmp-output-visitor.h" +#include "qapi/qmp-input-visitor.h" +#include "test-qapi-types.h" +#include "test-qapi-visit.h" +#include "qemu-objects.h" + +typedef struct TestStruct +{ + int64_t x; + int64_t y; +} TestStruct; + +typedef struct TestStructList +{ + TestStruct *value; + struct TestStructList *next; +} TestStructList; + +static void visit_type_TestStruct(Visitor *v, TestStruct **obj, const char *name, Error **errp) +{ + visit_start_struct(v, (void **)obj, "TestStruct", name, sizeof(TestStruct), errp); + visit_type_int(v, &(*obj)->x, "x", errp); + visit_type_int(v, &(*obj)->y, "y", errp); + visit_end_struct(v, errp); +} + +static void visit_type_TestStructList(Visitor *m, TestStructList ** obj, const char *name, Error **errp) +{ + GenericList *i; + + visit_start_list(m, name, errp); + + for (i = visit_next_list(m, (GenericList **)obj, errp); i; i = visit_next_list(m, &i, errp)) { + TestStructList *native_i = (TestStructList *)i; + visit_type_TestStruct(m, &native_i->value, NULL, errp); + } + + visit_end_list(m, errp); +} + +/* test core visitor methods */ +static void test_visitor_core(void) +{ + QmpOutputVisitor *mo; + QmpInputVisitor *mi; + Visitor *v; + TestStruct ts = { 42, 82 }; + TestStruct *pts = &ts; + TestStructList *lts = NULL; + Error *err = NULL; + QObject *obj; + QString *str; + int64_t value = 0; + + mo = qmp_output_visitor_new(); + v = qmp_output_get_visitor(mo); + + visit_type_TestStruct(v, &pts, NULL, &err); + + obj = qmp_output_get_qobject(mo); + + str = qobject_to_json(obj); + + printf("%s\n", qstring_get_str(str)); + + QDECREF(str); + + obj = QOBJECT(qint_from_int(0x42)); + + mi = qmp_input_visitor_new(obj); + v = qmp_input_get_visitor(mi); + + visit_type_int(v, &value, NULL, &err); + if (err) { + g_error("%s", error_get_pretty(err)); + } + + g_assert(value == 0x42); + + qobject_decref(obj); + + obj = qobject_from_json("{'x': 42, 'y': 84}"); + mi = qmp_input_visitor_new(obj); + v = qmp_input_get_visitor(mi); + + pts = NULL; + + visit_type_TestStruct(v, &pts, NULL, &err); + if (err) { + g_error("%s", error_get_pretty(err)); + } + + g_assert(pts != NULL); + g_assert(pts->x == 42); + g_assert(pts->y == 84); + + qobject_decref(obj); + + obj = qobject_from_json("[{'x': 42, 'y': 84}, {'x': 12, 'y': 24}]"); + mi = qmp_input_visitor_new(obj); + v = qmp_input_get_visitor(mi); + + visit_type_TestStructList(v, <s, NULL, &err); + if (err) { + g_error("%s", error_get_pretty(err)); + } + + g_assert(lts != NULL); + g_assert(lts->value->x == 42); + g_assert(lts->value->y == 84); + + lts = lts->next; + g_assert(lts != NULL); + g_assert(lts->value->x == 12); + g_assert(lts->value->y == 24); + + g_assert(lts->next == NULL); + + qobject_decref(obj); +} + +/* test deep nesting with refs to other user-defined types */ +static void test_nested_structs(void) +{ + QmpOutputVisitor *mo; + QmpInputVisitor *mi; + Visitor *v; + UserDefOne ud1; + UserDefOne *ud1_p = &ud1, *ud1c_p = NULL; + UserDefTwo ud2; + UserDefTwo *ud2_p = &ud2, *ud2c_p = NULL; + Error *err = NULL; + QObject *obj; + QString *str; + + ud1.integer = 42; + ud1.string = strdup("fourty two"); + + /* sanity check */ + mo = qmp_output_visitor_new(); + v = qmp_output_get_visitor(mo); + visit_type_UserDefOne(v, &ud1_p, "o_O", &err); + if (err) { + g_error("%s", error_get_pretty(err)); + } + obj = qmp_output_get_qobject(mo); + g_assert(obj); + qobject_decref(obj); + + ud2.string = strdup("fourty three"); + ud2.dict.string = strdup("fourty four"); + ud2.dict.dict.userdef = ud1_p; + ud2.dict.dict.string = strdup("fourty five"); + ud2.dict.has_dict2 = true; + ud2.dict.dict2.userdef = ud1_p; + ud2.dict.dict2.string = strdup("fourty six"); + + /* c type -> qobject */ + mo = qmp_output_visitor_new(); + v = qmp_output_get_visitor(mo); + visit_type_UserDefTwo(v, &ud2_p, "unused", &err); + if (err) { + g_error("%s", error_get_pretty(err)); + } + obj = qmp_output_get_qobject(mo); + g_assert(obj); + str = qobject_to_json_pretty(obj); + g_print("%s\n", qstring_get_str(str)); + QDECREF(str); + + /* qobject -> c type, should match original struct */ + mi = qmp_input_visitor_new(obj); + v = qmp_input_get_visitor(mi); + visit_type_UserDefTwo(v, &ud2c_p, NULL, &err); + if (err) { + g_error("%s", error_get_pretty(err)); + } + + g_assert(!g_strcmp0(ud2c_p->string, ud2.string)); + g_assert(!g_strcmp0(ud2c_p->dict.string, ud2.dict.string)); + + ud1c_p = ud2c_p->dict.dict.userdef; + g_assert(ud1c_p->integer == ud1_p->integer); + g_assert(!g_strcmp0(ud1c_p->string, ud1_p->string)); + + g_assert(!g_strcmp0(ud2c_p->dict.dict.string, ud2.dict.dict.string)); + + ud1c_p = ud2c_p->dict.dict2.userdef; + g_assert(ud1c_p->integer == ud1_p->integer); + g_assert(!g_strcmp0(ud1c_p->string, ud1_p->string)); + + g_assert(!g_strcmp0(ud2c_p->dict.dict2.string, ud2.dict.dict2.string)); + qemu_free(ud1.string); + qemu_free(ud2.string); + qemu_free(ud2.dict.string); + qemu_free(ud2.dict.dict.string); + qemu_free(ud2.dict.dict2.string); + + qapi_free_UserDefTwo(ud2c_p); + + qobject_decref(obj); +} + +/* test enum values */ +static void test_enums(void) +{ + QmpOutputVisitor *mo; + QmpInputVisitor *mi; + Visitor *v; + EnumOne enum1 = ENUM_ONE_VALUE2, enum1_cpy = ENUM_ONE_VALUE1; + Error *err = NULL; + QObject *obj; + QString *str; + + /* C type -> QObject */ + mo = qmp_output_visitor_new(); + v = qmp_output_get_visitor(mo); + visit_type_EnumOne(v, &enum1, "unused", &err); + if (err) { + g_error("%s", error_get_pretty(err)); + } + obj = qmp_output_get_qobject(mo); + g_assert(obj); + str = qobject_to_json_pretty(obj); + g_print("%s\n", qstring_get_str(str)); + QDECREF(str); + g_assert(g_strcmp0(qstring_get_str(qobject_to_qstring(obj)), "value2") == 0); + + /* QObject -> C type */ + mi = qmp_input_visitor_new(obj); + v = qmp_input_get_visitor(mi); + visit_type_EnumOne(v, &enum1_cpy, "unused", &err); + if (err) { + g_error("%s", error_get_pretty(err)); + } + g_debug("enum1_cpy, enum1: %d, %d", enum1_cpy, enum1); + g_assert(enum1_cpy == enum1); + + qobject_decref(obj); +} + +/* test enum values nested in schema-defined structs */ +static void test_nested_enums(void) +{ + QmpOutputVisitor *mo; + QmpInputVisitor *mi; + Visitor *v; + NestedEnumsOne *nested_enums, *nested_enums_cpy = NULL; + Error *err = NULL; + QObject *obj; + QString *str; + + nested_enums = qemu_mallocz(sizeof(NestedEnumsOne)); + nested_enums->enum1 = ENUM_ONE_VALUE1; + nested_enums->enum2 = ENUM_ONE_VALUE2; + nested_enums->enum3 = ENUM_ONE_VALUE3; + nested_enums->enum4 = ENUM_ONE_VALUE3; + nested_enums->has_enum2 = false; + nested_enums->has_enum4 = true; + + /* C type -> QObject */ + mo = qmp_output_visitor_new(); + v = qmp_output_get_visitor(mo); + visit_type_NestedEnumsOne(v, &nested_enums, NULL, &err); + if (err) { + g_error("%s", error_get_pretty(err)); + } + obj = qmp_output_get_qobject(mo); + g_assert(obj); + str = qobject_to_json_pretty(obj); + g_print("%s\n", qstring_get_str(str)); + QDECREF(str); + + /* QObject -> C type */ + mi = qmp_input_visitor_new(obj); + v = qmp_input_get_visitor(mi); + visit_type_NestedEnumsOne(v, &nested_enums_cpy, NULL, &err); + if (err) { + g_error("%s", error_get_pretty(err)); + } + g_assert(nested_enums_cpy); + g_assert(nested_enums_cpy->enum1 == nested_enums->enum1); + g_assert(nested_enums_cpy->enum3 == nested_enums->enum3); + g_assert(nested_enums_cpy->enum4 == nested_enums->enum4); + g_assert(nested_enums_cpy->has_enum2 == false); + g_assert(nested_enums_cpy->has_enum4 == true); + + qobject_decref(obj); + qapi_free_NestedEnumsOne(nested_enums); + qapi_free_NestedEnumsOne(nested_enums_cpy); +} + +int main(int argc, char **argv) +{ + g_test_init(&argc, &argv, NULL); + + g_test_add_func("/0.15/visitor_core", test_visitor_core); + g_test_add_func("/0.15/nested_structs", test_nested_structs); + g_test_add_func("/0.15/enums", test_enums); + g_test_add_func("/0.15/nested_enums", test_nested_enums); + + g_test_run(); + + return 0; +} From 69ed8366b1e8602b6b7555902f453d3e5df5dd41 Mon Sep 17 00:00:00 2001 From: Michael Roth Date: Tue, 19 Jul 2011 14:50:45 -0500 Subject: [PATCH 190/209] qapi: add test-qmp-commands, tests for gen. marshalling/dispatch code Signed-off-by: Michael Roth Signed-off-by: Luiz Capitulino --- Makefile | 8 +++- test-qmp-commands.c | 113 ++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 120 insertions(+), 1 deletion(-) create mode 100644 test-qmp-commands.c diff --git a/Makefile b/Makefile index 5d2cf5b3c9..d7c8567c4b 100644 --- a/Makefile +++ b/Makefile @@ -164,7 +164,7 @@ check-qjson: check-qjson.o qfloat.o qint.o qdict.o qstring.o qlist.o qbool.o qjs $(qapi-obj-y): $(GENERATED_HEADERS) qapi-dir := qapi-generated -test-visitor.o: QEMU_CFLAGS += -I $(qapi-dir) +test-visitor.o test-qmp-commands.o: QEMU_CFLAGS += -I $(qapi-dir) $(qapi-dir)/test-qapi-types.c: $(qapi-dir)/test-qapi-types.h $(qapi-dir)/test-qapi-types.h: $(SRC_PATH)/qapi-schema-test.json $(SRC_PATH)/scripts/qapi-types.py @@ -172,10 +172,16 @@ $(qapi-dir)/test-qapi-types.h: $(SRC_PATH)/qapi-schema-test.json $(SRC_PATH)/scr $(qapi-dir)/test-qapi-visit.c: $(qapi-dir)/test-qapi-visit.h $(qapi-dir)/test-qapi-visit.h: $(SRC_PATH)/qapi-schema-test.json $(SRC_PATH)/scripts/qapi-visit.py $(call quiet-command,python $(SRC_PATH)/scripts/qapi-visit.py -o "$(qapi-dir)" -p "test-" < $<, " GEN $@") +$(qapi-dir)/test-qmp-commands.h: $(qapi-dir)/test-qmp-marshal.c +$(qapi-dir)/test-qmp-marshal.c: $(SRC_PATH)/qapi-schema-test.json $(SRC_PATH)/scripts/qapi-commands.py + $(call quiet-command,python $(SRC_PATH)/scripts/qapi-commands.py -o "$(qapi-dir)" -p "test-" < $<, " GEN $@") test-visitor.o: $(addprefix $(qapi-dir)/, test-qapi-types.c test-qapi-types.h test-qapi-visit.c test-qapi-visit.h) $(qapi-obj-y) test-visitor: test-visitor.o qfloat.o qint.o qdict.o qstring.o qlist.o qbool.o $(qapi-obj-y) error.o osdep.o qemu-malloc.o $(oslib-obj-y) qjson.o json-streamer.o json-lexer.o json-parser.o qerror.o qemu-error.o qemu-tool.o $(qapi-dir)/test-qapi-visit.o $(qapi-dir)/test-qapi-types.o +test-qmp-commands.o: $(addprefix $(qapi-dir)/, test-qapi-types.c test-qapi-types.h test-qapi-visit.c test-qapi-visit.h test-qmp-marshal.c test-qmp-commands.h) $(qapi-obj-y) +test-qmp-commands: test-qmp-commands.o qfloat.o qint.o qdict.o qstring.o qlist.o qbool.o $(qapi-obj-y) error.o osdep.o qemu-malloc.o $(oslib-obj-y) qjson.o json-streamer.o json-lexer.o json-parser.o qerror.o qemu-error.o qemu-tool.o $(qapi-dir)/test-qapi-visit.o $(qapi-dir)/test-qapi-types.o $(qapi-dir)/test-qmp-marshal.o module.o + QEMULIBS=libhw32 libhw64 libuser libdis libdis-user clean: diff --git a/test-qmp-commands.c b/test-qmp-commands.c new file mode 100644 index 0000000000..775290439a --- /dev/null +++ b/test-qmp-commands.c @@ -0,0 +1,113 @@ +#include +#include "qemu-objects.h" +#include "test-qmp-commands.h" +#include "qapi/qmp-core.h" +#include "module.h" + +void qmp_user_def_cmd(Error **errp) +{ +} + +void qmp_user_def_cmd1(UserDefOne * ud1, Error **errp) +{ +} + +UserDefTwo * qmp_user_def_cmd2(UserDefOne * ud1a, UserDefOne * ud1b, Error **errp) +{ + UserDefTwo *ret; + UserDefOne *ud1c = qemu_mallocz(sizeof(UserDefOne)); + UserDefOne *ud1d = qemu_mallocz(sizeof(UserDefOne)); + + ud1c->string = strdup(ud1a->string); + ud1c->integer = ud1a->integer; + ud1d->string = strdup(ud1b->string); + ud1d->integer = ud1b->integer; + + ret = qemu_mallocz(sizeof(UserDefTwo)); + ret->string = strdup("blah1"); + ret->dict.string = strdup("blah2"); + ret->dict.dict.userdef = ud1c; + ret->dict.dict.string = strdup("blah3"); + ret->dict.has_dict2 = true; + ret->dict.dict2.userdef = ud1d; + ret->dict.dict2.string = strdup("blah4"); + + return ret; +} + +/* test commands with no input and no return value */ +static void test_dispatch_cmd(void) +{ + QDict *req = qdict_new(); + QObject *resp; + + qdict_put_obj(req, "execute", QOBJECT(qstring_from_str("user_def_cmd"))); + + resp = qmp_dispatch(QOBJECT(req)); + assert(resp != NULL); + assert(!qdict_haskey(qobject_to_qdict(resp), "error")); + g_print("\nresp: %s\n", qstring_get_str(qobject_to_json(resp))); + + qobject_decref(resp); + QDECREF(req); +} + +/* test commands that return an error due to invalid parameters */ +static void test_dispatch_cmd_error(void) +{ + QDict *req = qdict_new(); + QObject *resp; + + qdict_put_obj(req, "execute", QOBJECT(qstring_from_str("user_def_cmd2"))); + + resp = qmp_dispatch(QOBJECT(req)); + assert(resp != NULL); + assert(qdict_haskey(qobject_to_qdict(resp), "error")); + g_print("\nresp: %s\n", qstring_get_str(qobject_to_json_pretty(resp))); + + qobject_decref(resp); + QDECREF(req); +} + +/* test commands that involve both input parameters and return values */ +static void test_dispatch_cmd_io(void) +{ + QDict *req = qdict_new(); + QDict *args = qdict_new(); + QDict *ud1a = qdict_new(); + QDict *ud1b = qdict_new(); + QObject *resp; + + qdict_put_obj(ud1a, "integer", QOBJECT(qint_from_int(42))); + qdict_put_obj(ud1a, "string", QOBJECT(qstring_from_str("hello"))); + qdict_put_obj(ud1b, "integer", QOBJECT(qint_from_int(422))); + qdict_put_obj(ud1b, "string", QOBJECT(qstring_from_str("hello2"))); + qdict_put_obj(args, "ud1a", QOBJECT(ud1a)); + qdict_put_obj(args, "ud1b", QOBJECT(ud1b)); + qdict_put_obj(req, "arguments", QOBJECT(args)); + + qdict_put_obj(req, "execute", QOBJECT(qstring_from_str("user_def_cmd2"))); + + /* TODO: put in full payload and check for errors */ + resp = qmp_dispatch(QOBJECT(req)); + assert(resp != NULL); + assert(!qdict_haskey(qobject_to_qdict(resp), "error")); + g_print("\nresp: %s\n", qstring_get_str(qobject_to_json_pretty(resp))); + + qobject_decref(resp); + QDECREF(req); +} + +int main(int argc, char **argv) +{ + g_test_init(&argc, &argv, NULL); + + g_test_add_func("/0.15/dispatch_cmd", test_dispatch_cmd); + g_test_add_func("/0.15/dispatch_cmd_error", test_dispatch_cmd_error); + g_test_add_func("/0.15/dispatch_cmd_io", test_dispatch_cmd_io); + + module_call_init(MODULE_INIT_QAPI); + g_test_run(); + + return 0; +} From b84da8319586e31c2065b1a39aca5ff15e204d5a Mon Sep 17 00:00:00 2001 From: Michael Roth Date: Tue, 19 Jul 2011 14:50:46 -0500 Subject: [PATCH 191/209] qapi: add QAPI code generation documentation Signed-off-by: Michael Roth Signed-off-by: Luiz Capitulino --- docs/qapi-code-gen.txt | 316 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 316 insertions(+) create mode 100644 docs/qapi-code-gen.txt diff --git a/docs/qapi-code-gen.txt b/docs/qapi-code-gen.txt new file mode 100644 index 0000000000..b7befb5e48 --- /dev/null +++ b/docs/qapi-code-gen.txt @@ -0,0 +1,316 @@ += How to use the QAPI code generator = + +* Note: as of this writing, QMP does not use QAPI. Eventually QMP +commands will be converted to use QAPI internally. The following +information describes QMP/QAPI as it will exist after the +conversion. + +QAPI is a native C API within QEMU which provides management-level +functionality to internal/external users. For external +users/processes, this interface is made available by a JSON-based +QEMU Monitor protocol that is provided by the QMP server. + +To map QMP-defined interfaces to the native C QAPI implementations, +a JSON-based schema is used to define types and function +signatures, and a set of scripts is used to generate types/signatures, +and marshaling/dispatch code. The QEMU Guest Agent also uses these +scripts, paired with a seperate schema, to generate +marshaling/dispatch code for the guest agent server running in the +guest. + +This document will describe how the schemas, scripts, and resulting +code is used. + + +== QMP/Guest agent schema == + +This file defines the types, commands, and events used by QMP. It should +fully describe the interface used by QMP. + +This file is designed to be loosely based on JSON although it's technically +executable Python. While dictionaries are used, they are parsed as +OrderedDicts so that ordering is preserved. + +There are two basic syntaxes used, type definitions and command definitions. + +The first syntax defines a type and is represented by a dictionary. There are +two kinds of types that are supported: complex user-defined types, and enums. + +A complex type is a dictionary containing a single key who's value is a +dictionary. This corresponds to a struct in C or an Object in JSON. An +example of a complex type is: + + { 'type': 'MyType', + 'data' { 'member1': 'str', 'member2': 'int', '*member3': 'str } } + +The use of '*' as a prefix to the name means the member is optional. Optional +members should always be added to the end of the dictionary to preserve +backwards compatibility. + +An enumeration type is a dictionary containing a single key who's value is a +list of strings. An example enumeration is: + + { 'enum': 'MyEnum', 'data': [ 'value1', 'value2', 'value3' ] } + +Generally speaking, complex types and enums should always use CamelCase for +the type names. + +Commands are defined by using a list containing three members. The first +member is the command name, the second member is a dictionary containing +arguments, and the third member is the return type. + +An example command is: + + { 'command': 'my-command', + 'data': { 'arg1': 'str', '*arg2': 'str' }, + 'returns': 'str' ] + +Command names should be all lower case with words separated by a hyphen. + + +== Code generation == + +Schemas are fed into 3 scripts to generate all the code/files that, paired +with the core QAPI libraries, comprise everything required to take JSON +commands read in by a QMP/guest agent server, unmarshal the arguments into +the underlying C types, call into the corresponding C function, and map the +response back to a QMP/guest agent response to be returned to the user. + +As an example, we'll use the following schema, which describes a single +complex user-defined type (which will produce a C struct, along with a list +node structure that can be used to chain together a list of such types in +case we want to accept/return a list of this type with a command), and a +command which takes that type as a parameter and returns the same type: + + mdroth@illuin:~/w/qemu2.git$ cat example-schema.json + { 'type': 'UserDefOne', + 'data': { 'integer': 'int', 'string': 'str' } } + + { 'command': 'my-command', + 'data': {'arg1': 'UserDefOne'}, + 'returns': 'UserDefOne' } + mdroth@illuin:~/w/qemu2.git$ + +=== scripts/qapi-types.py === + +Used to generate the C types defined by a schema. The following files are +created: + +$(prefix)qapi-types.h - C types corresponding to types defined in + the schema you pass in +$(prefix)qapi-types.c - Cleanup functions for the above C types + +The $(prefix) is an optional parameter used as a namespace to keep the +generated code from one schema/code-generation separated from others so code +can be generated/used from multiple schemas without clobbering previously +created code. + +Example: + + mdroth@illuin:~/w/qemu2.git$ python scripts/qapi-types.py \ + --output-dir="qapi-generated" --prefix="example-" < example-schema.json + mdroth@illuin:~/w/qemu2.git$ cat qapi-generated/example-qapi-types.c + /* AUTOMATICALLY GENERATED, DO NOT MODIFY */ + + #include "qapi/qapi-dealloc-visitor.h" + #include "example-qapi-types.h" + #include "example-qapi-visit.h" + + void qapi_free_UserDefOne(UserDefOne * obj) + { + QapiDeallocVisitor *md; + Visitor *v; + + if (!obj) { + return; + } + + md = qapi_dealloc_visitor_new(); + v = qapi_dealloc_get_visitor(md); + visit_type_UserDefOne(v, &obj, NULL, NULL); + qapi_dealloc_visitor_cleanup(md); + } + + mdroth@illuin:~/w/qemu2.git$ cat qapi-generated/example-qapi-types.h + /* AUTOMATICALLY GENERATED, DO NOT MODIFY */ + #ifndef QAPI_GENERATED_EXAMPLE_QAPI_TYPES + #define QAPI_GENERATED_EXAMPLE_QAPI_TYPES + + #include "qapi/qapi-types-core.h" + + typedef struct UserDefOne UserDefOne; + + typedef struct UserDefOneList + { + UserDefOne *value; + struct UserDefOneList *next; + } UserDefOneList; + + struct UserDefOne + { + int64_t integer; + char * string; + }; + + void qapi_free_UserDefOne(UserDefOne * obj); + + #endif + + +=== scripts/qapi-visit.py === + +Used to generate the visitor functions used to walk through and convert +a QObject (as provided by QMP) to a native C data structure and +vice-versa, as well as the visitor function used to dealloc a complex +schema-defined C type. + +The following files are generated: + +$(prefix)qapi-visit.c: visitor function for a particular C type, used + to automagically convert QObjects into the + corresponding C type and vice-versa, as well + as for deallocating memory for an existing C + type + +$(prefix)qapi-visit.h: declarations for previously mentioned visitor + functions + +Example: + + mdroth@illuin:~/w/qemu2.git$ python scripts/qapi-visit.py \ + --output-dir="qapi-generated" --prefix="example-" < example-schema.json + mdroth@illuin:~/w/qemu2.git$ cat qapi-generated/example-qapi-visit.c + /* THIS FILE IS AUTOMATICALLY GENERATED, DO NOT MODIFY */ + + #include "example-qapi-visit.h" + + void visit_type_UserDefOne(Visitor *m, UserDefOne ** obj, const char *name, Error **errp) + { + visit_start_struct(m, (void **)obj, "UserDefOne", name, sizeof(UserDefOne), errp); + visit_type_int(m, (obj && *obj) ? &(*obj)->integer : NULL, "integer", errp); + visit_type_str(m, (obj && *obj) ? &(*obj)->string : NULL, "string", errp); + visit_end_struct(m, errp); + } + + void visit_type_UserDefOneList(Visitor *m, UserDefOneList ** obj, const char *name, Error **errp) + { + GenericList *i; + + visit_start_list(m, name, errp); + + for (i = visit_next_list(m, (GenericList **)obj, errp); i; i = visit_next_list(m, &i, errp)) { + UserDefOneList *native_i = (UserDefOneList *)i; + visit_type_UserDefOne(m, &native_i->value, NULL, errp); + } + + visit_end_list(m, errp); + } + mdroth@illuin:~/w/qemu2.git$ cat qapi-generated/example-qapi-visit.h + /* THIS FILE IS AUTOMATICALLY GENERATED, DO NOT MODIFY */ + + #ifndef QAPI_GENERATED_EXAMPLE_QAPI_VISIT + #define QAPI_GENERATED_EXAMPLE_QAPI_VISIT + + #include "qapi/qapi-visit-core.h" + #include "example-qapi-types.h" + + void visit_type_UserDefOne(Visitor *m, UserDefOne ** obj, const char *name, Error **errp); + void visit_type_UserDefOneList(Visitor *m, UserDefOneList ** obj, const char *name, Error **errp); + + #endif + mdroth@illuin:~/w/qemu2.git$ + + +=== scripts/qapi-commands.py === + +Used to generate the marshaling/dispatch functions for the commands defined +in the schema. The following files are generated: + +$(prefix)qmp-marshal.c: command marshal/dispatch functions for each + QMP command defined in the schema. Functions + generated by qapi-visit.py are used to + convert QObjects recieved from the wire into + function parameters, and uses the same + visitor functions to convert native C return + values to QObjects from transmission back + over the wire. + +$(prefix)qmp-commands.h: Function prototypes for the QMP commands + specified in the schema. + +Example: + + mdroth@illuin:~/w/qemu2.git$ cat qapi-generated/example-qmp-marshal.c + /* THIS FILE IS AUTOMATICALLY GENERATED, DO NOT MODIFY */ + + #include "qemu-objects.h" + #include "qapi/qmp-core.h" + #include "qapi/qapi-visit-core.h" + #include "qapi/qmp-output-visitor.h" + #include "qapi/qmp-input-visitor.h" + #include "qapi/qapi-dealloc-visitor.h" + #include "example-qapi-types.h" + #include "example-qapi-visit.h" + + #include "example-qmp-commands.h" + static void qmp_marshal_output_my_command(UserDefOne * ret_in, QObject **ret_out, Error **errp) + { + QapiDeallocVisitor *md = qapi_dealloc_visitor_new(); + QmpOutputVisitor *mo = qmp_output_visitor_new(); + Visitor *v; + + v = qmp_output_get_visitor(mo); + visit_type_UserDefOne(v, &ret_in, "unused", errp); + v = qapi_dealloc_get_visitor(md); + visit_type_UserDefOne(v, &ret_in, "unused", errp); + qapi_dealloc_visitor_cleanup(md); + + + *ret_out = qmp_output_get_qobject(mo); + } + + static void qmp_marshal_input_my_command(QmpState *qmp__sess, QDict *args, QObject **ret, Error **errp) + { + UserDefOne * retval = NULL; + QmpInputVisitor *mi; + QapiDeallocVisitor *md; + Visitor *v; + UserDefOne * arg1 = NULL; + + mi = qmp_input_visitor_new(QOBJECT(args)); + v = qmp_input_get_visitor(mi); + visit_type_UserDefOne(v, &arg1, "arg1", errp); + + if (error_is_set(errp)) { + goto out; + } + retval = qmp_my_command(arg1, errp); + qmp_marshal_output_my_command(retval, ret, errp); + + out: + md = qapi_dealloc_visitor_new(); + v = qapi_dealloc_get_visitor(md); + visit_type_UserDefOne(v, &arg1, "arg1", errp); + qapi_dealloc_visitor_cleanup(md); + return; + } + + static void qmp_init_marshal(void) + { + qmp_register_command("my-command", qmp_marshal_input_my_command); + } + + qapi_init(qmp_init_marshal); + mdroth@illuin:~/w/qemu2.git$ cat qapi-generated/example-qmp-commands.h + /* THIS FILE IS AUTOMATICALLY GENERATED, DO NOT MODIFY */ + + #ifndef QAPI_GENERATED_EXAMPLE_QMP_COMMANDS + #define QAPI_GENERATED_EXAMPLE_QMP_COMMANDS + + #include "example-qapi-types.h" + #include "error.h" + + UserDefOne * qmp_my_command(UserDefOne * arg1, Error **errp); + + #endif + mdroth@illuin:~/w/qemu2.git$ From ac32c7807640f04682ea17bca17c22f8b9264d62 Mon Sep 17 00:00:00 2001 From: Michael Roth Date: Tue, 19 Jul 2011 15:41:52 -0500 Subject: [PATCH 192/209] qerror: add QERR_JSON_PARSE_ERROR to qerror.c Missing from previous addition of error to qerror.h. Needed for qerror_format() and friends. Signed-off-by: Michael Roth Signed-off-by: Luiz Capitulino --- qerror.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/qerror.c b/qerror.c index d7fcd93cad..c92adfcb13 100644 --- a/qerror.c +++ b/qerror.c @@ -140,6 +140,11 @@ static const QErrorStringTable qerror_table[] = { .error_fmt = QERR_JSON_PARSING, .desc = "Invalid JSON syntax", }, + { + .error_fmt = QERR_JSON_PARSE_ERROR, + .desc = "JSON parse error, %(message)", + + }, { .error_fmt = QERR_KVM_MISSING_CAP, .desc = "Using KVM without %(capability), %(feature) unavailable", From 13a286d57bb5f50dbceea1fc45060a69bcb23fd1 Mon Sep 17 00:00:00 2001 From: Michael Roth Date: Tue, 19 Jul 2011 15:41:53 -0500 Subject: [PATCH 193/209] guest agent: command state class Signed-off-by: Michael Roth Signed-off-by: Luiz Capitulino --- Makefile | 4 +- configure | 1 + qga/guest-agent-command-state.c | 73 +++++++++++++++++++++++++++++++++ qga/guest-agent-core.h | 25 +++++++++++ 4 files changed, 102 insertions(+), 1 deletion(-) create mode 100644 qga/guest-agent-command-state.c create mode 100644 qga/guest-agent-core.h diff --git a/Makefile b/Makefile index d7c8567c4b..b8cdf0ed2a 100644 --- a/Makefile +++ b/Makefile @@ -182,6 +182,8 @@ test-visitor: test-visitor.o qfloat.o qint.o qdict.o qstring.o qlist.o qbool.o $ test-qmp-commands.o: $(addprefix $(qapi-dir)/, test-qapi-types.c test-qapi-types.h test-qapi-visit.c test-qapi-visit.h test-qmp-marshal.c test-qmp-commands.h) $(qapi-obj-y) test-qmp-commands: test-qmp-commands.o qfloat.o qint.o qdict.o qstring.o qlist.o qbool.o $(qapi-obj-y) error.o osdep.o qemu-malloc.o $(oslib-obj-y) qjson.o json-streamer.o json-lexer.o json-parser.o qerror.o qemu-error.o qemu-tool.o $(qapi-dir)/test-qapi-visit.o $(qapi-dir)/test-qapi-types.o $(qapi-dir)/test-qmp-marshal.o module.o +QGALIB=qga/guest-agent-command-state.o + QEMULIBS=libhw32 libhw64 libuser libdis libdis-user clean: @@ -190,7 +192,7 @@ clean: rm -f qemu-options.def rm -f *.o *.d *.a *.lo $(TOOLS) TAGS cscope.* *.pod *~ */*~ rm -Rf .libs - rm -f slirp/*.o slirp/*.d audio/*.o audio/*.d block/*.o block/*.d net/*.o net/*.d fsdev/*.o fsdev/*.d ui/*.o ui/*.d qapi/*.o qapi/*.d + rm -f slirp/*.o slirp/*.d audio/*.o audio/*.d block/*.o block/*.d net/*.o net/*.d fsdev/*.o fsdev/*.d ui/*.o ui/*.d qapi/*.o qapi/*.d qga/*.o qga/*.d rm -f qemu-img-cmds.h rm -f trace.c trace.h trace.c-timestamp trace.h-timestamp rm -f trace-dtrace.dtrace trace-dtrace.dtrace-timestamp diff --git a/configure b/configure index ad1e1e134d..796f706a08 100755 --- a/configure +++ b/configure @@ -3487,6 +3487,7 @@ DIRS="$DIRS pc-bios/spapr-rtas" DIRS="$DIRS roms/seabios roms/vgabios" DIRS="$DIRS fsdev ui" DIRS="$DIRS qapi" +DIRS="$DIRS qga" FILES="Makefile tests/Makefile" FILES="$FILES tests/cris/Makefile tests/cris/.gdbinit" FILES="$FILES pc-bios/optionrom/Makefile pc-bios/keymaps" diff --git a/qga/guest-agent-command-state.c b/qga/guest-agent-command-state.c new file mode 100644 index 0000000000..bc6e0bd4a8 --- /dev/null +++ b/qga/guest-agent-command-state.c @@ -0,0 +1,73 @@ +/* + * QEMU Guest Agent command state interfaces + * + * Copyright IBM Corp. 2011 + * + * Authors: + * Michael Roth + * + * This work is licensed under the terms of the GNU GPL, version 2 or later. + * See the COPYING file in the top-level directory. + */ +#include +#include "qga/guest-agent-core.h" + +struct GACommandState { + GSList *groups; +}; + +typedef struct GACommandGroup { + void (*init)(void); + void (*cleanup)(void); +} GACommandGroup; + +/* handle init/cleanup for stateful guest commands */ + +void ga_command_state_add(GACommandState *cs, + void (*init)(void), + void (*cleanup)(void)) +{ + GACommandGroup *cg = qemu_mallocz(sizeof(GACommandGroup)); + cg->init = init; + cg->cleanup = cleanup; + cs->groups = g_slist_append(cs->groups, cg); +} + +static void ga_command_group_init(gpointer opaque, gpointer unused) +{ + GACommandGroup *cg = opaque; + + g_assert(cg); + if (cg->init) { + cg->init(); + } +} + +void ga_command_state_init_all(GACommandState *cs) +{ + g_assert(cs); + g_slist_foreach(cs->groups, ga_command_group_init, NULL); +} + +static void ga_command_group_cleanup(gpointer opaque, gpointer unused) +{ + GACommandGroup *cg = opaque; + + g_assert(cg); + if (cg->cleanup) { + cg->cleanup(); + } +} + +void ga_command_state_cleanup_all(GACommandState *cs) +{ + g_assert(cs); + g_slist_foreach(cs->groups, ga_command_group_cleanup, NULL); +} + +GACommandState *ga_command_state_new(void) +{ + GACommandState *cs = qemu_mallocz(sizeof(GACommandState)); + cs->groups = NULL; + return cs; +} diff --git a/qga/guest-agent-core.h b/qga/guest-agent-core.h new file mode 100644 index 0000000000..688f1205b5 --- /dev/null +++ b/qga/guest-agent-core.h @@ -0,0 +1,25 @@ +/* + * QEMU Guest Agent core declarations + * + * Copyright IBM Corp. 2011 + * + * Authors: + * Adam Litke + * Michael Roth + * + * This work is licensed under the terms of the GNU GPL, version 2 or later. + * See the COPYING file in the top-level directory. + */ +#include "qapi/qmp-core.h" +#include "qemu-common.h" + +#define QGA_VERSION "1.0" + +typedef struct GACommandState GACommandState; + +void ga_command_state_add(GACommandState *cs, + void (*init)(void), + void (*cleanup)(void)); +void ga_command_state_init_all(GACommandState *cs); +void ga_command_state_cleanup_all(GACommandState *cs); +GACommandState *ga_command_state_new(void); From 48ff7a625b3611d075d8798585df86455bb2d1fd Mon Sep 17 00:00:00 2001 From: Michael Roth Date: Wed, 20 Jul 2011 15:19:37 -0500 Subject: [PATCH 194/209] guest agent: qemu-ga daemon This is the actual guest daemon, it listens for requests over a virtio-serial/isa-serial/unix socket channel and routes them through to dispatch routines, and writes the results back to the channel in a manner similar to QMP. A shorthand invocation: qemu-ga -d Is equivalent to: qemu-ga -m virtio-serial -p /dev/virtio-ports/org.qemu.guest_agent.0 \ -f /var/run/qemu-ga.pid -d Signed-off-by: Michael Roth Signed-off-by: Luiz Capitulino --- Makefile | 8 +- configure | 1 + qemu-ga.c | 650 +++++++++++++++++++++++++++++++++++++++++ qga/guest-agent-core.h | 4 + 4 files changed, 660 insertions(+), 3 deletions(-) create mode 100644 qemu-ga.c diff --git a/Makefile b/Makefile index b8cdf0ed2a..0d2e33dfb6 100644 --- a/Makefile +++ b/Makefile @@ -140,7 +140,7 @@ endif ###################################################################### qemu-img.o: qemu-img-cmds.h -qemu-img.o qemu-tool.o qemu-nbd.o qemu-io.o cmd.o: $(GENERATED_HEADERS) +qemu-img.o qemu-tool.o qemu-nbd.o qemu-io.o cmd.o qemu-ga.o: $(GENERATED_HEADERS) qemu-img$(EXESUF): qemu-img.o qemu-tool.o qemu-error.o $(oslib-obj-y) $(trace-obj-y) $(block-obj-y) $(qobject-obj-y) $(version-obj-y) qemu-timer-common.o @@ -184,13 +184,15 @@ test-qmp-commands: test-qmp-commands.o qfloat.o qint.o qdict.o qstring.o qlist.o QGALIB=qga/guest-agent-command-state.o +qemu-ga$(EXESUF): qemu-ga.o $(QGALIB) qemu-tool.o qemu-error.o error.o $(oslib-obj-y) $(trace-obj-y) $(block-obj-y) $(qobject-obj-y) $(version-obj-y) $(qapi-obj-y) qemu-timer-common.o qemu-sockets.o module.o qapi/qmp-dispatch.o qapi/qmp-registry.o + QEMULIBS=libhw32 libhw64 libuser libdis libdis-user clean: # avoid old build problems by removing potentially incorrect old files rm -f config.mak op-i386.h opc-i386.h gen-op-i386.h op-arm.h opc-arm.h gen-op-arm.h rm -f qemu-options.def - rm -f *.o *.d *.a *.lo $(TOOLS) TAGS cscope.* *.pod *~ */*~ + rm -f *.o *.d *.a *.lo $(TOOLS) qemu-ga TAGS cscope.* *.pod *~ */*~ rm -Rf .libs rm -f slirp/*.o slirp/*.d audio/*.o audio/*.d block/*.o block/*.d net/*.o net/*.d fsdev/*.o fsdev/*.d ui/*.o ui/*.d qapi/*.o qapi/*.d qga/*.o qga/*.d rm -f qemu-img-cmds.h @@ -386,4 +388,4 @@ tarbin: $(mandir)/man8/qemu-nbd.8 # Include automatically generated dependency files --include $(wildcard *.d audio/*.d slirp/*.d block/*.d net/*.d ui/*.d qapi/*.d) +-include $(wildcard *.d audio/*.d slirp/*.d block/*.d net/*.d ui/*.d qapi/*.d qga/*.d) diff --git a/configure b/configure index 796f706a08..f9be70993a 100755 --- a/configure +++ b/configure @@ -2532,6 +2532,7 @@ if test "$softmmu" = yes ; then tools="qemu-img\$(EXESUF) qemu-io\$(EXESUF) $tools" if [ "$linux" = "yes" -o "$bsd" = "yes" -o "$solaris" = "yes" ] ; then tools="qemu-nbd\$(EXESUF) $tools" + tools="qemu-ga\$(EXESUF) $tools" if [ "$check_utests" = "yes" ]; then tools="check-qint check-qstring check-qdict check-qlist $tools" tools="check-qfloat check-qjson $tools" diff --git a/qemu-ga.c b/qemu-ga.c new file mode 100644 index 0000000000..1f3585c51e --- /dev/null +++ b/qemu-ga.c @@ -0,0 +1,650 @@ +/* + * QEMU Guest Agent + * + * Copyright IBM Corp. 2011 + * + * Authors: + * Adam Litke + * Michael Roth + * + * This work is licensed under the terms of the GNU GPL, version 2 or later. + * See the COPYING file in the top-level directory. + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include "qemu_socket.h" +#include "json-streamer.h" +#include "json-parser.h" +#include "qint.h" +#include "qjson.h" +#include "qga/guest-agent-core.h" +#include "module.h" +#include "signal.h" +#include "qerror.h" +#include "error_int.h" + +#define QGA_VIRTIO_PATH_DEFAULT "/dev/virtio-ports/org.qemu.guest_agent.0" +#define QGA_PIDFILE_DEFAULT "/var/run/qemu-ga.pid" +#define QGA_BAUDRATE_DEFAULT B38400 /* for isa-serial channels */ +#define QGA_TIMEOUT_DEFAULT 30*1000 /* ms */ + +struct GAState { + JSONMessageParser parser; + GMainLoop *main_loop; + GSocket *conn_sock; + GIOChannel *conn_channel; + GSocket *listen_sock; + GIOChannel *listen_channel; + const char *path; + const char *method; + bool virtio; /* fastpath to check for virtio to deal with poll() quirks */ + GACommandState *command_state; + GLogLevelFlags log_level; + FILE *log_file; + bool logging_enabled; +}; + +static struct GAState *ga_state; + +static void quit_handler(int sig) +{ + g_debug("recieved signal num %d, quitting", sig); + + if (g_main_loop_is_running(ga_state->main_loop)) { + g_main_loop_quit(ga_state->main_loop); + } +} + +static void register_signal_handlers(void) +{ + struct sigaction sigact; + int ret; + + memset(&sigact, 0, sizeof(struct sigaction)); + sigact.sa_handler = quit_handler; + + ret = sigaction(SIGINT, &sigact, NULL); + if (ret == -1) { + g_error("error configuring signal handler: %s", strerror(errno)); + exit(EXIT_FAILURE); + } + ret = sigaction(SIGTERM, &sigact, NULL); + if (ret == -1) { + g_error("error configuring signal handler: %s", strerror(errno)); + } +} + +static void usage(const char *cmd) +{ + printf( +"Usage: %s -c \n" +"QEMU Guest Agent %s\n" +"\n" +" -m, --method transport method: one of unix-listen, virtio-serial, or\n" +" isa-serial (virtio-serial is the default)\n" +" -p, --path device/socket path (%s is the default for virtio-serial)\n" +" -l, --logfile set logfile path, logs to stderr by default\n" +" -f, --pidfile specify pidfile (default is %s)\n" +" -v, --verbose log extra debugging information\n" +" -V, --version print version information and exit\n" +" -d, --daemonize become a daemon\n" +" -h, --help display this help and exit\n" +"\n" +"Report bugs to \n" + , cmd, QGA_VERSION, QGA_VIRTIO_PATH_DEFAULT, QGA_PIDFILE_DEFAULT); +} + +static void conn_channel_close(GAState *s); + +static const char *ga_log_level_str(GLogLevelFlags level) +{ + switch (level & G_LOG_LEVEL_MASK) { + case G_LOG_LEVEL_ERROR: + return "error"; + case G_LOG_LEVEL_CRITICAL: + return "critical"; + case G_LOG_LEVEL_WARNING: + return "warning"; + case G_LOG_LEVEL_MESSAGE: + return "message"; + case G_LOG_LEVEL_INFO: + return "info"; + case G_LOG_LEVEL_DEBUG: + return "debug"; + default: + return "user"; + } +} + +bool ga_logging_enabled(GAState *s) +{ + return s->logging_enabled; +} + +void ga_disable_logging(GAState *s) +{ + s->logging_enabled = false; +} + +void ga_enable_logging(GAState *s) +{ + s->logging_enabled = true; +} + +static void ga_log(const gchar *domain, GLogLevelFlags level, + const gchar *msg, gpointer opaque) +{ + GAState *s = opaque; + GTimeVal time; + const char *level_str = ga_log_level_str(level); + + if (!ga_logging_enabled(s)) { + return; + } + + level &= G_LOG_LEVEL_MASK; + if (g_strcmp0(domain, "syslog") == 0) { + syslog(LOG_INFO, "%s: %s", level_str, msg); + } else if (level & s->log_level) { + g_get_current_time(&time); + fprintf(s->log_file, + "%lu.%lu: %s: %s\n", time.tv_sec, time.tv_usec, level_str, msg); + fflush(s->log_file); + } +} + +static void become_daemon(const char *pidfile) +{ + pid_t pid, sid; + int pidfd; + char *pidstr = NULL; + + pid = fork(); + if (pid < 0) { + exit(EXIT_FAILURE); + } + if (pid > 0) { + exit(EXIT_SUCCESS); + } + + pidfd = open(pidfile, O_CREAT|O_WRONLY|O_EXCL, S_IRUSR|S_IWUSR); + if (pidfd == -1) { + g_critical("Cannot create pid file, %s", strerror(errno)); + exit(EXIT_FAILURE); + } + + if (asprintf(&pidstr, "%d", getpid()) == -1) { + g_critical("Cannot allocate memory"); + goto fail; + } + if (write(pidfd, pidstr, strlen(pidstr)) != strlen(pidstr)) { + free(pidstr); + g_critical("Failed to write pid file"); + goto fail; + } + + umask(0); + sid = setsid(); + if (sid < 0) { + goto fail; + } + if ((chdir("/")) < 0) { + goto fail; + } + + close(STDIN_FILENO); + close(STDOUT_FILENO); + close(STDERR_FILENO); + free(pidstr); + return; + +fail: + unlink(pidfile); + g_critical("failed to daemonize"); + exit(EXIT_FAILURE); +} + +static int conn_channel_send_buf(GIOChannel *channel, const char *buf, + gsize count) +{ + GError *err = NULL; + gsize written = 0; + GIOStatus status; + + while (count) { + status = g_io_channel_write_chars(channel, buf, count, &written, &err); + g_debug("sending data, count: %d", (int)count); + if (err != NULL) { + g_warning("error sending newline: %s", err->message); + return err->code; + } + if (status == G_IO_STATUS_ERROR || status == G_IO_STATUS_EOF) { + return -EPIPE; + } + + if (status == G_IO_STATUS_NORMAL) { + count -= written; + } + } + + return 0; +} + +static int conn_channel_send_payload(GIOChannel *channel, QObject *payload) +{ + int ret = 0; + const char *buf; + QString *payload_qstr; + GError *err = NULL; + + g_assert(payload && channel); + + payload_qstr = qobject_to_json(payload); + if (!payload_qstr) { + return -EINVAL; + } + + qstring_append_chr(payload_qstr, '\n'); + buf = qstring_get_str(payload_qstr); + ret = conn_channel_send_buf(channel, buf, strlen(buf)); + if (ret) { + goto out_free; + } + + g_io_channel_flush(channel, &err); + if (err != NULL) { + g_warning("error flushing payload: %s", err->message); + ret = err->code; + goto out_free; + } + +out_free: + QDECREF(payload_qstr); + if (err) { + g_error_free(err); + } + return ret; +} + +static void process_command(GAState *s, QDict *req) +{ + QObject *rsp = NULL; + int ret; + + g_assert(req); + g_debug("processing command"); + rsp = qmp_dispatch(QOBJECT(req)); + if (rsp) { + ret = conn_channel_send_payload(s->conn_channel, rsp); + if (ret) { + g_warning("error sending payload: %s", strerror(ret)); + } + qobject_decref(rsp); + } else { + g_warning("error getting response"); + } +} + +/* handle requests/control events coming in over the channel */ +static void process_event(JSONMessageParser *parser, QList *tokens) +{ + GAState *s = container_of(parser, GAState, parser); + QObject *obj; + QDict *qdict; + Error *err = NULL; + int ret; + + g_assert(s && parser); + + g_debug("process_event: called"); + obj = json_parser_parse_err(tokens, NULL, &err); + if (err || !obj || qobject_type(obj) != QTYPE_QDICT) { + qobject_decref(obj); + qdict = qdict_new(); + if (!err) { + g_warning("failed to parse event: unknown error"); + error_set(&err, QERR_JSON_PARSING); + } else { + g_warning("failed to parse event: %s", error_get_pretty(err)); + } + qdict_put_obj(qdict, "error", error_get_qobject(err)); + error_free(err); + } else { + qdict = qobject_to_qdict(obj); + } + + g_assert(qdict); + + /* handle host->guest commands */ + if (qdict_haskey(qdict, "execute")) { + process_command(s, qdict); + } else { + if (!qdict_haskey(qdict, "error")) { + QDECREF(qdict); + qdict = qdict_new(); + g_warning("unrecognized payload format"); + error_set(&err, QERR_UNSUPPORTED); + qdict_put_obj(qdict, "error", error_get_qobject(err)); + error_free(err); + } + ret = conn_channel_send_payload(s->conn_channel, QOBJECT(qdict)); + if (ret) { + g_warning("error sending payload: %s", strerror(ret)); + } + } + + QDECREF(qdict); +} + +static gboolean conn_channel_read(GIOChannel *channel, GIOCondition condition, + gpointer data) +{ + GAState *s = data; + gchar buf[1024]; + gsize count; + GError *err = NULL; + memset(buf, 0, 1024); + GIOStatus status = g_io_channel_read_chars(channel, buf, 1024, + &count, &err); + if (err != NULL) { + g_warning("error reading channel: %s", err->message); + conn_channel_close(s); + g_error_free(err); + return false; + } + switch (status) { + case G_IO_STATUS_ERROR: + g_warning("problem"); + return false; + case G_IO_STATUS_NORMAL: + g_debug("read data, count: %d, data: %s", (int)count, buf); + json_message_parser_feed(&s->parser, (char *)buf, (int)count); + case G_IO_STATUS_AGAIN: + /* virtio causes us to spin here when no process is attached to + * host-side chardev. sleep a bit to mitigate this + */ + if (s->virtio) { + usleep(100*1000); + } + return true; + case G_IO_STATUS_EOF: + g_debug("received EOF"); + conn_channel_close(s); + if (s->virtio) { + return true; + } + return false; + default: + g_warning("unknown channel read status, closing"); + conn_channel_close(s); + return false; + } + return true; +} + +static int conn_channel_add(GAState *s, int fd) +{ + GIOChannel *conn_channel; + GError *err = NULL; + + g_assert(s && !s->conn_channel); + conn_channel = g_io_channel_unix_new(fd); + g_assert(conn_channel); + g_io_channel_set_encoding(conn_channel, NULL, &err); + if (err != NULL) { + g_warning("error setting channel encoding to binary"); + g_error_free(err); + return -1; + } + g_io_add_watch(conn_channel, G_IO_IN | G_IO_HUP, + conn_channel_read, s); + s->conn_channel = conn_channel; + return 0; +} + +static gboolean listen_channel_accept(GIOChannel *channel, + GIOCondition condition, gpointer data) +{ + GAState *s = data; + GError *err = NULL; + g_assert(channel != NULL); + int ret; + bool accepted = false; + + s->conn_sock = g_socket_accept(s->listen_sock, NULL, &err); + if (err != NULL) { + g_warning("error converting fd to gsocket: %s", err->message); + g_error_free(err); + goto out; + } + ret = conn_channel_add(s, g_socket_get_fd(s->conn_sock)); + if (ret) { + g_warning("error setting up connection"); + goto out; + } + accepted = true; + +out: + /* only accept 1 connection at a time */ + return !accepted; +} + +/* start polling for readable events on listen fd, new==true + * indicates we should use the existing s->listen_channel + */ +static int listen_channel_add(GAState *s, int listen_fd, bool new) +{ + GError *err = NULL; + + if (new) { + s->listen_channel = g_io_channel_unix_new(listen_fd); + if (s->listen_sock) { + g_object_unref(s->listen_sock); + } + s->listen_sock = g_socket_new_from_fd(listen_fd, &err); + if (err != NULL) { + g_warning("error converting fd to gsocket: %s", err->message); + g_error_free(err); + return -1; + } + } + g_io_add_watch(s->listen_channel, G_IO_IN, + listen_channel_accept, s); + return 0; +} + +/* cleanup state for closed connection/session, start accepting new + * connections if we're in listening mode + */ +static void conn_channel_close(GAState *s) +{ + if (strcmp(s->method, "unix-listen") == 0) { + g_io_channel_shutdown(s->conn_channel, true, NULL); + g_object_unref(s->conn_sock); + s->conn_sock = NULL; + listen_channel_add(s, 0, false); + } else if (strcmp(s->method, "virtio-serial") == 0) { + /* we spin on EOF for virtio-serial, so back off a bit. also, + * dont close the connection in this case, it'll resume normal + * operation when another process connects to host chardev + */ + usleep(100*1000); + goto out_noclose; + } + g_io_channel_unref(s->conn_channel); + s->conn_channel = NULL; +out_noclose: + return; +} + +static void init_guest_agent(GAState *s) +{ + struct termios tio; + int ret, fd; + + if (s->method == NULL) { + /* try virtio-serial as our default */ + s->method = "virtio-serial"; + } + + if (s->path == NULL) { + if (strcmp(s->method, "virtio-serial") != 0) { + g_critical("must specify a path for this channel"); + exit(EXIT_FAILURE); + } + /* try the default path for the virtio-serial port */ + s->path = QGA_VIRTIO_PATH_DEFAULT; + } + + if (strcmp(s->method, "virtio-serial") == 0) { + s->virtio = true; + fd = qemu_open(s->path, O_RDWR | O_NONBLOCK | O_ASYNC); + if (fd == -1) { + g_critical("error opening channel: %s", strerror(errno)); + exit(EXIT_FAILURE); + } + ret = conn_channel_add(s, fd); + if (ret) { + g_critical("error adding channel to main loop"); + exit(EXIT_FAILURE); + } + } else if (strcmp(s->method, "isa-serial") == 0) { + fd = qemu_open(s->path, O_RDWR | O_NOCTTY); + if (fd == -1) { + g_critical("error opening channel: %s", strerror(errno)); + exit(EXIT_FAILURE); + } + tcgetattr(fd, &tio); + /* set up serial port for non-canonical, dumb byte streaming */ + tio.c_iflag &= ~(IGNBRK | BRKINT | IGNPAR | PARMRK | INPCK | ISTRIP | + INLCR | IGNCR | ICRNL | IXON | IXOFF | IXANY | + IMAXBEL); + tio.c_oflag = 0; + tio.c_lflag = 0; + tio.c_cflag |= QGA_BAUDRATE_DEFAULT; + /* 1 available byte min or reads will block (we'll set non-blocking + * elsewhere, else we have to deal with read()=0 instead) + */ + tio.c_cc[VMIN] = 1; + tio.c_cc[VTIME] = 0; + /* flush everything waiting for read/xmit, it's garbage at this point */ + tcflush(fd, TCIFLUSH); + tcsetattr(fd, TCSANOW, &tio); + ret = conn_channel_add(s, fd); + if (ret) { + g_error("error adding channel to main loop"); + } + } else if (strcmp(s->method, "unix-listen") == 0) { + fd = unix_listen(s->path, NULL, strlen(s->path)); + if (fd == -1) { + g_critical("error opening path: %s", strerror(errno)); + exit(EXIT_FAILURE); + } + ret = listen_channel_add(s, fd, true); + if (ret) { + g_critical("error binding/listening to specified socket"); + exit(EXIT_FAILURE); + } + } else { + g_critical("unsupported channel method/type: %s", s->method); + exit(EXIT_FAILURE); + } + + json_message_parser_init(&s->parser, process_event); + s->main_loop = g_main_loop_new(NULL, false); +} + +int main(int argc, char **argv) +{ + const char *sopt = "hVvdm:p:l:f:"; + const char *method = NULL, *path = NULL, *pidfile = QGA_PIDFILE_DEFAULT; + const struct option lopt[] = { + { "help", 0, NULL, 'h' }, + { "version", 0, NULL, 'V' }, + { "logfile", 0, NULL, 'l' }, + { "pidfile", 0, NULL, 'f' }, + { "verbose", 0, NULL, 'v' }, + { "method", 0, NULL, 'm' }, + { "path", 0, NULL, 'p' }, + { "daemonize", 0, NULL, 'd' }, + { NULL, 0, NULL, 0 } + }; + int opt_ind = 0, ch, daemonize = 0; + GLogLevelFlags log_level = G_LOG_LEVEL_ERROR | G_LOG_LEVEL_CRITICAL; + FILE *log_file = stderr; + GAState *s; + + while ((ch = getopt_long(argc, argv, sopt, lopt, &opt_ind)) != -1) { + switch (ch) { + case 'm': + method = optarg; + break; + case 'p': + path = optarg; + break; + case 'l': + log_file = fopen(optarg, "a"); + if (!log_file) { + g_critical("unable to open specified log file: %s", + strerror(errno)); + return EXIT_FAILURE; + } + break; + case 'f': + pidfile = optarg; + break; + case 'v': + /* enable all log levels */ + log_level = G_LOG_LEVEL_MASK; + break; + case 'V': + printf("QEMU Guest Agent %s\n", QGA_VERSION); + return 0; + case 'd': + daemonize = 1; + break; + case 'h': + usage(argv[0]); + return 0; + case '?': + g_print("Unknown option, try '%s --help' for more information.\n", + argv[0]); + return EXIT_FAILURE; + } + } + + if (daemonize) { + g_debug("starting daemon"); + become_daemon(pidfile); + } + + g_type_init(); + g_thread_init(NULL); + + s = qemu_mallocz(sizeof(GAState)); + s->conn_channel = NULL; + s->path = path; + s->method = method; + s->log_file = log_file; + s->log_level = log_level; + g_log_set_default_handler(ga_log, s); + g_log_set_fatal_mask(NULL, G_LOG_LEVEL_ERROR); + s->logging_enabled = true; + ga_state = s; + + module_call_init(MODULE_INIT_QAPI); + init_guest_agent(ga_state); + register_signal_handlers(); + + g_main_loop_run(ga_state->main_loop); + + unlink(pidfile); + + return 0; +} diff --git a/qga/guest-agent-core.h b/qga/guest-agent-core.h index 688f1205b5..66d1729689 100644 --- a/qga/guest-agent-core.h +++ b/qga/guest-agent-core.h @@ -15,6 +15,7 @@ #define QGA_VERSION "1.0" +typedef struct GAState GAState; typedef struct GACommandState GACommandState; void ga_command_state_add(GACommandState *cs, @@ -23,3 +24,6 @@ void ga_command_state_add(GACommandState *cs, void ga_command_state_init_all(GACommandState *cs); void ga_command_state_cleanup_all(GACommandState *cs); GACommandState *ga_command_state_new(void); +bool ga_logging_enabled(GAState *s); +void ga_disable_logging(GAState *s); +void ga_enable_logging(GAState *s); From e3d4d25206a13ca48936e4357a53591997ce6d57 Mon Sep 17 00:00:00 2001 From: Michael Roth Date: Tue, 19 Jul 2011 15:41:55 -0500 Subject: [PATCH 195/209] guest agent: add guest agent RPCs/commands This adds the initial set of QMP/QAPI commands provided by the guest agent: guest-sync guest-ping guest-info guest-shutdown guest-file-open guest-file-read guest-file-write guest-file-seek guest-file-flush guest-file-close guest-fsfreeze-freeze guest-fsfreeze-thaw guest-fsfreeze-status The input/output specification for these commands are documented in the schema. Example usage: host: qemu -device virtio-serial \ -chardev socket,path=/tmp/vs0.sock,server,nowait,id=qga0 \ -device virtserialport,chardev=qga0,name=org.qemu.quest_agent.0 ... echo "{'execute':'guest-info'}" | socat stdio unix-connect:/tmp/qga0.sock guest: qemu-ga -m virtio-serial -p /dev/virtio-ports/org.qemu.guest_agent.0 \ -p /var/run/qemu-guest-agent.pid -d Signed-off-by: Michael Roth Signed-off-by: Luiz Capitulino --- Makefile | 16 +- qapi-schema-guest.json | 217 ++++++++++++++++ qemu-ga.c | 4 + qerror.c | 8 + qerror.h | 6 + qga/guest-agent-commands.c | 518 +++++++++++++++++++++++++++++++++++++ qga/guest-agent-core.h | 2 + 7 files changed, 768 insertions(+), 3 deletions(-) create mode 100644 qapi-schema-guest.json create mode 100644 qga/guest-agent-commands.c diff --git a/Makefile b/Makefile index 0d2e33dfb6..f3a03ad87e 100644 --- a/Makefile +++ b/Makefile @@ -164,7 +164,7 @@ check-qjson: check-qjson.o qfloat.o qint.o qdict.o qstring.o qlist.o qbool.o qjs $(qapi-obj-y): $(GENERATED_HEADERS) qapi-dir := qapi-generated -test-visitor.o test-qmp-commands.o: QEMU_CFLAGS += -I $(qapi-dir) +test-visitor.o test-qmp-commands.o qemu-ga$(EXESUF): QEMU_CFLAGS += -I $(qapi-dir) $(qapi-dir)/test-qapi-types.c: $(qapi-dir)/test-qapi-types.h $(qapi-dir)/test-qapi-types.h: $(SRC_PATH)/qapi-schema-test.json $(SRC_PATH)/scripts/qapi-types.py @@ -176,15 +176,25 @@ $(qapi-dir)/test-qmp-commands.h: $(qapi-dir)/test-qmp-marshal.c $(qapi-dir)/test-qmp-marshal.c: $(SRC_PATH)/qapi-schema-test.json $(SRC_PATH)/scripts/qapi-commands.py $(call quiet-command,python $(SRC_PATH)/scripts/qapi-commands.py -o "$(qapi-dir)" -p "test-" < $<, " GEN $@") +$(qapi-dir)/qga-qapi-types.c: $(qapi-dir)/qga-qapi-types.h +$(qapi-dir)/qga-qapi-types.h: $(SRC_PATH)/qapi-schema-guest.json $(SRC_PATH)/scripts/qapi-types.py + $(call quiet-command,python $(SRC_PATH)/scripts/qapi-types.py -o "$(qapi-dir)" -p "qga-" < $<, " GEN $@") +$(qapi-dir)/qga-qapi-visit.c: $(qapi-dir)/qga-qapi-visit.h +$(qapi-dir)/qga-qapi-visit.h: $(SRC_PATH)/qapi-schema-guest.json $(SRC_PATH)/scripts/qapi-visit.py + $(call quiet-command,python $(SRC_PATH)/scripts/qapi-visit.py -o "$(qapi-dir)" -p "qga-" < $<, " GEN $@") +$(qapi-dir)/qga-qmp-marshal.c: $(SRC_PATH)/qapi-schema-guest.json $(SRC_PATH)/scripts/qapi-commands.py + $(call quiet-command,python $(SRC_PATH)/scripts/qapi-commands.py -o "$(qapi-dir)" -p "qga-" < $<, " GEN $@") + test-visitor.o: $(addprefix $(qapi-dir)/, test-qapi-types.c test-qapi-types.h test-qapi-visit.c test-qapi-visit.h) $(qapi-obj-y) test-visitor: test-visitor.o qfloat.o qint.o qdict.o qstring.o qlist.o qbool.o $(qapi-obj-y) error.o osdep.o qemu-malloc.o $(oslib-obj-y) qjson.o json-streamer.o json-lexer.o json-parser.o qerror.o qemu-error.o qemu-tool.o $(qapi-dir)/test-qapi-visit.o $(qapi-dir)/test-qapi-types.o test-qmp-commands.o: $(addprefix $(qapi-dir)/, test-qapi-types.c test-qapi-types.h test-qapi-visit.c test-qapi-visit.h test-qmp-marshal.c test-qmp-commands.h) $(qapi-obj-y) test-qmp-commands: test-qmp-commands.o qfloat.o qint.o qdict.o qstring.o qlist.o qbool.o $(qapi-obj-y) error.o osdep.o qemu-malloc.o $(oslib-obj-y) qjson.o json-streamer.o json-lexer.o json-parser.o qerror.o qemu-error.o qemu-tool.o $(qapi-dir)/test-qapi-visit.o $(qapi-dir)/test-qapi-types.o $(qapi-dir)/test-qmp-marshal.o module.o -QGALIB=qga/guest-agent-command-state.o +QGALIB=qga/guest-agent-command-state.o qga/guest-agent-commands.o -qemu-ga$(EXESUF): qemu-ga.o $(QGALIB) qemu-tool.o qemu-error.o error.o $(oslib-obj-y) $(trace-obj-y) $(block-obj-y) $(qobject-obj-y) $(version-obj-y) $(qapi-obj-y) qemu-timer-common.o qemu-sockets.o module.o qapi/qmp-dispatch.o qapi/qmp-registry.o +qemu-ga.o: $(addprefix $(qapi-dir)/, qga-qapi-types.c qga-qapi-types.h qga-qapi-visit.c qga-qmp-marshal.c) $(qapi-obj-y) +qemu-ga$(EXESUF): qemu-ga.o $(QGALIB) qemu-tool.o qemu-error.o error.o $(oslib-obj-y) $(trace-obj-y) $(block-obj-y) $(qobject-obj-y) $(version-obj-y) $(qapi-obj-y) qemu-timer-common.o qemu-sockets.o module.o qapi/qmp-dispatch.o qapi/qmp-registry.o $(qapi-dir)/qga-qapi-visit.o $(qapi-dir)/qga-qapi-types.o $(qapi-dir)/qga-qmp-marshal.o QEMULIBS=libhw32 libhw64 libuser libdis libdis-user diff --git a/qapi-schema-guest.json b/qapi-schema-guest.json new file mode 100644 index 0000000000..fde5971e87 --- /dev/null +++ b/qapi-schema-guest.json @@ -0,0 +1,217 @@ +# *-*- Mode: Python -*-* + +## +# @guest-sync: +# +# Echo back a unique integer value +# +# This is used by clients talking to the guest agent over the +# wire to ensure the stream is in sync and doesn't contain stale +# data from previous client. All guest agent responses should be +# ignored until the provided unique integer value is returned, +# and it is up to the client to handle stale whole or +# partially-delivered JSON text in such a way that this response +# can be obtained. +# +# Such clients should also preceed this command +# with a 0xFF byte to make such the guest agent flushes any +# partially read JSON data from a previous session. +# +# @id: randomly generated 64-bit integer +# +# Returns: The unique integer id passed in by the client +# +# Since: 0.15.0 +## +{ 'command': 'guest-sync' + 'data': { 'id': 'int' }, + 'returns': 'int' } + +## +# @guest-ping: +# +# Ping the guest agent, a non-error return implies success +# +# Since: 0.15.0 +## +{ 'command': 'guest-ping' } + +## +# @guest-info: +# +# Get some information about the guest agent. +# +# Since: 0.15.0 +## +{ 'type': 'GuestAgentInfo', 'data': {'version': 'str'} } +{ 'command': 'guest-info', + 'returns': 'GuestAgentInfo' } + +## +# @guest-shutdown: +# +# Initiate guest-activated shutdown. Note: this is an asynchronous +# shutdown request, with no guaruntee of successful shutdown. Errors +# will be logged to guest's syslog. +# +# @mode: #optional "halt", "powerdown" (default), or "reboot" +# +# Returns: Nothing on success +# +# Since: 0.15.0 +## +{ 'command': 'guest-shutdown', 'data': { '*mode': 'str' } } + +## +# @guest-file-open: +# +# Open a file in the guest and retrieve a file handle for it +# +# @filepath: Full path to the file in the guest to open. +# +# @mode: #optional open mode, as per fopen(), "r" is the default. +# +# Returns: Guest file handle on success. +# +# Since: 0.15.0 +## +{ 'command': 'guest-file-open', + 'data': { 'path': 'str', '*mode': 'str' }, + 'returns': 'int' } + +## +# @guest-file-close: +# +# Close an open file in the guest +# +# @handle: filehandle returned by guest-file-open +# +# Returns: Nothing on success. +# +# Since: 0.15.0 +## +{ 'command': 'guest-file-close', + 'data': { 'handle': 'int' } } + +## +# @guest-file-read: +# +# Read from an open file in the guest. Data will be base64-encoded +# +# @handle: filehandle returned by guest-file-open +# +# @count: #optional maximum number of bytes to read (default is 4KB) +# +# Returns: GuestFileRead on success. Note: count is number of bytes read +# *before* base64 encoding bytes read. +# +# Since: 0.15.0 +## +{ 'type': 'GuestFileRead', + 'data': { 'count': 'int', 'buf-b64': 'str', 'eof': 'bool' } } + +{ 'command': 'guest-file-read', + 'data': { 'handle': 'int', '*count': 'int' }, + 'returns': 'GuestFileRead' } + +## +# @guest-file-write: +# +# Write to an open file in the guest. +# +# @handle: filehandle returned by guest-file-open +# +# @buf-b64: base64-encoded string representing data to be written +# +# @count: #optional bytes to write (actual bytes, after base64-decode), +# default is all content in buf-b64 buffer after base64 decoding +# +# Returns: GuestFileWrite on success. Note: count is the number of bytes +# base64-decoded bytes written +# +# Since: 0.15.0 +## +{ 'type': 'GuestFileWrite', + 'data': { 'count': 'int', 'eof': 'bool' } } +{ 'command': 'guest-file-write', + 'data': { 'handle': 'int', 'buf-b64': 'str', '*count': 'int' }, + 'returns': 'GuestFileWrite' } + +## +# @guest-file-seek: +# +# Seek to a position in the file, as with fseek(), and return the +# current file position afterward. Also encapsulates ftell()'s +# functionality, just Set offset=0, whence=SEEK_CUR. +# +# @handle: filehandle returned by guest-file-open +# +# @offset: bytes to skip over in the file stream +# +# @whence: SEEK_SET, SEEK_CUR, or SEEK_END, as with fseek() +# +# Returns: GuestFileSeek on success. +# +# Since: 0.15.0 +## +{ 'type': 'GuestFileSeek', + 'data': { 'position': 'int', 'eof': 'bool' } } + +{ 'command': 'guest-file-seek', + 'data': { 'handle': 'int', 'offset': 'int', 'whence': 'int' }, + 'returns': 'GuestFileSeek' } + +## +# @guest-file-flush: +# +# Write file changes bufferred in userspace to disk/kernel buffers +# +# @handle: filehandle returned by guest-file-open +# +# Returns: Nothing on success. +# +# Since: 0.15.0 +## +{ 'command': 'guest-file-flush', + 'data': { 'handle': 'int' } } + +## +# @guest-fsfreeze-status: +# +# Get guest fsfreeze state. error state indicates failure to thaw 1 or more +# previously frozen filesystems, or failure to open a previously cached +# filesytem (filesystem unmounted/directory changes, etc). +# +# Returns: GuestFsfreezeStatus ("thawed", "frozen", etc., as defined below) +# +# Since: 0.15.0 +## +{ 'enum': 'GuestFsfreezeStatus', + 'data': [ 'thawed', 'frozen', 'error' ] } +{ 'command': 'guest-fsfreeze-status', + 'returns': 'GuestFsfreezeStatus' } + +## +# @guest-fsfreeze-freeze: +# +# Sync and freeze all non-network guest filesystems +# +# Returns: Number of file systems frozen on success +# +# Since: 0.15.0 +## +{ 'command': 'guest-fsfreeze-freeze', + 'returns': 'int' } + +## +# @guest-fsfreeze-thaw: +# +# Unfreeze frozen guest fileystems +# +# Returns: Number of file systems thawed +# If error, -1 (unknown error) or -errno +# +# Since: 0.15.0 +## +{ 'command': 'guest-fsfreeze-thaw', + 'returns': 'int' } diff --git a/qemu-ga.c b/qemu-ga.c index 1f3585c51e..6e2f61fe3c 100644 --- a/qemu-ga.c +++ b/qemu-ga.c @@ -636,6 +636,9 @@ int main(int argc, char **argv) g_log_set_default_handler(ga_log, s); g_log_set_fatal_mask(NULL, G_LOG_LEVEL_ERROR); s->logging_enabled = true; + s->command_state = ga_command_state_new(); + ga_command_state_init(s, s->command_state); + ga_command_state_init_all(s->command_state); ga_state = s; module_call_init(MODULE_INIT_QAPI); @@ -644,6 +647,7 @@ int main(int argc, char **argv) g_main_loop_run(ga_state->main_loop); + ga_command_state_cleanup_all(ga_state->command_state); unlink(pidfile); return 0; diff --git a/qerror.c b/qerror.c index c92adfcb13..229d0d63e3 100644 --- a/qerror.c +++ b/qerror.c @@ -218,6 +218,14 @@ static const QErrorStringTable qerror_table[] = { .error_fmt = QERR_VNC_SERVER_FAILED, .desc = "Could not start VNC server on %(target)", }, + { + .error_fmt = QERR_QGA_LOGGING_FAILED, + .desc = "Guest agent failed to log non-optional log statement", + }, + { + .error_fmt = QERR_QGA_COMMAND_FAILED, + .desc = "Guest agent command failed, error was '%(message)'", + }, {} }; diff --git a/qerror.h b/qerror.h index 9a9fa5b7eb..7ec0fc12d7 100644 --- a/qerror.h +++ b/qerror.h @@ -184,4 +184,10 @@ QError *qobject_to_qerror(const QObject *obj); #define QERR_FEATURE_DISABLED \ "{ 'class': 'FeatureDisabled', 'data': { 'name': %s } }" +#define QERR_QGA_LOGGING_FAILED \ + "{ 'class': 'QgaLoggingFailed', 'data': {} }" + +#define QERR_QGA_COMMAND_FAILED \ + "{ 'class': 'QgaCommandFailed', 'data': { 'message': %s } }" + #endif /* QERROR_H */ diff --git a/qga/guest-agent-commands.c b/qga/guest-agent-commands.c new file mode 100644 index 0000000000..8c0d67ee28 --- /dev/null +++ b/qga/guest-agent-commands.c @@ -0,0 +1,518 @@ +/* + * QEMU Guest Agent commands + * + * Copyright IBM Corp. 2011 + * + * Authors: + * Michael Roth + * + * This work is licensed under the terms of the GNU GPL, version 2 or later. + * See the COPYING file in the top-level directory. + */ + +#include +#include +#include +#include +#include +#include "qga/guest-agent-core.h" +#include "qga-qmp-commands.h" +#include "qerror.h" +#include "qemu-queue.h" + +static GAState *ga_state; + +static void disable_logging(void) +{ + ga_disable_logging(ga_state); +} + +static void enable_logging(void) +{ + ga_enable_logging(ga_state); +} + +/* Note: in some situations, like with the fsfreeze, logging may be + * temporarilly disabled. if it is necessary that a command be able + * to log for accounting purposes, check ga_logging_enabled() beforehand, + * and use the QERR_QGA_LOGGING_DISABLED to generate an error + */ +static void slog(const char *fmt, ...) +{ + va_list ap; + + va_start(ap, fmt); + g_logv("syslog", G_LOG_LEVEL_INFO, fmt, ap); + va_end(ap); +} + +int64_t qmp_guest_sync(int64_t id, Error **errp) +{ + return id; +} + +void qmp_guest_ping(Error **err) +{ + slog("guest-ping called"); +} + +struct GuestAgentInfo *qmp_guest_info(Error **err) +{ + GuestAgentInfo *info = qemu_mallocz(sizeof(GuestAgentInfo)); + + info->version = g_strdup(QGA_VERSION); + + return info; +} + +void qmp_guest_shutdown(bool has_mode, const char *mode, Error **err) +{ + int ret; + const char *shutdown_flag; + + slog("guest-shutdown called, mode: %s", mode); + if (!has_mode || strcmp(mode, "powerdown") == 0) { + shutdown_flag = "-P"; + } else if (strcmp(mode, "halt") == 0) { + shutdown_flag = "-H"; + } else if (strcmp(mode, "reboot") == 0) { + shutdown_flag = "-r"; + } else { + error_set(err, QERR_INVALID_PARAMETER_VALUE, "mode", + "halt|powerdown|reboot"); + return; + } + + ret = fork(); + if (ret == 0) { + /* child, start the shutdown */ + setsid(); + fclose(stdin); + fclose(stdout); + fclose(stderr); + + ret = execl("/sbin/shutdown", "shutdown", shutdown_flag, "+0", + "hypervisor initiated shutdown", (char*)NULL); + if (ret) { + slog("guest-shutdown failed: %s", strerror(errno)); + } + exit(!!ret); + } else if (ret < 0) { + error_set(err, QERR_UNDEFINED_ERROR); + } +} + +typedef struct GuestFileHandle { + uint64_t id; + FILE *fh; + QTAILQ_ENTRY(GuestFileHandle) next; +} GuestFileHandle; + +static struct { + QTAILQ_HEAD(, GuestFileHandle) filehandles; +} guest_file_state; + +static void guest_file_handle_add(FILE *fh) +{ + GuestFileHandle *gfh; + + gfh = qemu_mallocz(sizeof(GuestFileHandle)); + gfh->id = fileno(fh); + gfh->fh = fh; + QTAILQ_INSERT_TAIL(&guest_file_state.filehandles, gfh, next); +} + +static GuestFileHandle *guest_file_handle_find(int64_t id) +{ + GuestFileHandle *gfh; + + QTAILQ_FOREACH(gfh, &guest_file_state.filehandles, next) + { + if (gfh->id == id) { + return gfh; + } + } + + return NULL; +} + +int64_t qmp_guest_file_open(const char *path, bool has_mode, const char *mode, Error **err) +{ + FILE *fh; + int fd; + int64_t ret = -1; + + if (!has_mode) { + mode = "r"; + } + slog("guest-file-open called, filepath: %s, mode: %s", path, mode); + fh = fopen(path, mode); + if (!fh) { + error_set(err, QERR_OPEN_FILE_FAILED, path); + return -1; + } + + /* set fd non-blocking to avoid common use cases (like reading from a + * named pipe) from hanging the agent + */ + fd = fileno(fh); + ret = fcntl(fd, F_GETFL); + ret = fcntl(fd, F_SETFL, ret | O_NONBLOCK); + if (ret == -1) { + error_set(err, QERR_QGA_COMMAND_FAILED, "fcntl() failed"); + fclose(fh); + return -1; + } + + guest_file_handle_add(fh); + slog("guest-file-open, handle: %d", fd); + return fd; +} + +void qmp_guest_file_close(int64_t handle, Error **err) +{ + GuestFileHandle *gfh = guest_file_handle_find(handle); + int ret; + + slog("guest-file-close called, handle: %ld", handle); + if (!gfh) { + error_set(err, QERR_FD_NOT_FOUND, "handle"); + return; + } + + ret = fclose(gfh->fh); + if (ret == -1) { + error_set(err, QERR_QGA_COMMAND_FAILED, "fclose() failed"); + return; + } + + QTAILQ_REMOVE(&guest_file_state.filehandles, gfh, next); + qemu_free(gfh); +} + +struct GuestFileRead *qmp_guest_file_read(int64_t handle, bool has_count, + int64_t count, Error **err) +{ + GuestFileHandle *gfh = guest_file_handle_find(handle); + GuestFileRead *read_data = NULL; + guchar *buf; + FILE *fh; + size_t read_count; + + if (!gfh) { + error_set(err, QERR_FD_NOT_FOUND, "handle"); + return NULL; + } + + if (!has_count) { + count = QGA_READ_COUNT_DEFAULT; + } else if (count < 0) { + error_set(err, QERR_INVALID_PARAMETER, "count"); + return NULL; + } + + fh = gfh->fh; + buf = qemu_mallocz(count+1); + read_count = fread(buf, 1, count, fh); + if (ferror(fh)) { + slog("guest-file-read failed, handle: %ld", handle); + error_set(err, QERR_QGA_COMMAND_FAILED, "fread() failed"); + } else { + buf[read_count] = 0; + read_data = qemu_mallocz(sizeof(GuestFileRead)); + read_data->count = read_count; + read_data->eof = feof(fh); + if (read_count) { + read_data->buf_b64 = g_base64_encode(buf, read_count); + } + } + qemu_free(buf); + clearerr(fh); + + return read_data; +} + +GuestFileWrite *qmp_guest_file_write(int64_t handle, const char *buf_b64, + bool has_count, int64_t count, Error **err) +{ + GuestFileWrite *write_data = NULL; + guchar *buf; + gsize buf_len; + int write_count; + GuestFileHandle *gfh = guest_file_handle_find(handle); + FILE *fh; + + if (!gfh) { + error_set(err, QERR_FD_NOT_FOUND, "handle"); + return NULL; + } + + fh = gfh->fh; + buf = g_base64_decode(buf_b64, &buf_len); + + if (!has_count) { + count = buf_len; + } else if (count < 0 || count > buf_len) { + qemu_free(buf); + error_set(err, QERR_INVALID_PARAMETER, "count"); + return NULL; + } + + write_count = fwrite(buf, 1, count, fh); + if (ferror(fh)) { + slog("guest-file-write failed, handle: %ld", handle); + error_set(err, QERR_QGA_COMMAND_FAILED, "fwrite() error"); + } else { + write_data = qemu_mallocz(sizeof(GuestFileWrite)); + write_data->count = write_count; + write_data->eof = feof(fh); + } + qemu_free(buf); + clearerr(fh); + + return write_data; +} + +struct GuestFileSeek *qmp_guest_file_seek(int64_t handle, int64_t offset, + int64_t whence, Error **err) +{ + GuestFileHandle *gfh = guest_file_handle_find(handle); + GuestFileSeek *seek_data = NULL; + FILE *fh; + int ret; + + if (!gfh) { + error_set(err, QERR_FD_NOT_FOUND, "handle"); + return NULL; + } + + fh = gfh->fh; + ret = fseek(fh, offset, whence); + if (ret == -1) { + error_set(err, QERR_QGA_COMMAND_FAILED, strerror(errno)); + } else { + seek_data = qemu_mallocz(sizeof(GuestFileRead)); + seek_data->position = ftell(fh); + seek_data->eof = feof(fh); + } + clearerr(fh); + + return seek_data; +} + +void qmp_guest_file_flush(int64_t handle, Error **err) +{ + GuestFileHandle *gfh = guest_file_handle_find(handle); + FILE *fh; + int ret; + + if (!gfh) { + error_set(err, QERR_FD_NOT_FOUND, "handle"); + return; + } + + fh = gfh->fh; + ret = fflush(fh); + if (ret == EOF) { + error_set(err, QERR_QGA_COMMAND_FAILED, strerror(errno)); + } +} + +static void guest_file_init(void) +{ + QTAILQ_INIT(&guest_file_state.filehandles); +} + +typedef struct GuestFsfreezeMount { + char *dirname; + char *devtype; + QTAILQ_ENTRY(GuestFsfreezeMount) next; +} GuestFsfreezeMount; + +struct { + GuestFsfreezeStatus status; + QTAILQ_HEAD(, GuestFsfreezeMount) mount_list; +} guest_fsfreeze_state; + +/* + * Walk the mount table and build a list of local file systems + */ +static int guest_fsfreeze_build_mount_list(void) +{ + struct mntent *ment; + GuestFsfreezeMount *mount, *temp; + char const *mtab = MOUNTED; + FILE *fp; + + QTAILQ_FOREACH_SAFE(mount, &guest_fsfreeze_state.mount_list, next, temp) { + QTAILQ_REMOVE(&guest_fsfreeze_state.mount_list, mount, next); + qemu_free(mount->dirname); + qemu_free(mount->devtype); + qemu_free(mount); + } + + fp = setmntent(mtab, "r"); + if (!fp) { + g_warning("fsfreeze: unable to read mtab"); + return -1; + } + + while ((ment = getmntent(fp))) { + /* + * An entry which device name doesn't start with a '/' is + * either a dummy file system or a network file system. + * Add special handling for smbfs and cifs as is done by + * coreutils as well. + */ + if ((ment->mnt_fsname[0] != '/') || + (strcmp(ment->mnt_type, "smbfs") == 0) || + (strcmp(ment->mnt_type, "cifs") == 0)) { + continue; + } + + mount = qemu_mallocz(sizeof(GuestFsfreezeMount)); + mount->dirname = qemu_strdup(ment->mnt_dir); + mount->devtype = qemu_strdup(ment->mnt_type); + + QTAILQ_INSERT_TAIL(&guest_fsfreeze_state.mount_list, mount, next); + } + + endmntent(fp); + + return 0; +} + +/* + * Return status of freeze/thaw + */ +GuestFsfreezeStatus qmp_guest_fsfreeze_status(Error **err) +{ + return guest_fsfreeze_state.status; +} + +/* + * Walk list of mounted file systems in the guest, and freeze the ones which + * are real local file systems. + */ +int64_t qmp_guest_fsfreeze_freeze(Error **err) +{ + int ret = 0, i = 0; + struct GuestFsfreezeMount *mount, *temp; + int fd; + char err_msg[512]; + + slog("guest-fsfreeze called"); + + if (guest_fsfreeze_state.status == GUEST_FSFREEZE_STATUS_FROZEN) { + return 0; + } + + ret = guest_fsfreeze_build_mount_list(); + if (ret < 0) { + return ret; + } + + /* cannot risk guest agent blocking itself on a write in this state */ + disable_logging(); + + QTAILQ_FOREACH_SAFE(mount, &guest_fsfreeze_state.mount_list, next, temp) { + fd = qemu_open(mount->dirname, O_RDONLY); + if (fd == -1) { + sprintf(err_msg, "failed to open %s, %s", mount->dirname, strerror(errno)); + error_set(err, QERR_QGA_COMMAND_FAILED, err_msg); + goto error; + } + + /* we try to cull filesytems we know won't work in advance, but other + * filesytems may not implement fsfreeze for less obvious reasons. + * these will report EOPNOTSUPP, so we simply ignore them. when + * thawing, these filesystems will return an EINVAL instead, due to + * not being in a frozen state. Other filesystem-specific + * errors may result in EINVAL, however, so the user should check the + * number * of filesystems returned here against those returned by the + * thaw operation to determine whether everything completed + * successfully + */ + ret = ioctl(fd, FIFREEZE); + if (ret < 0 && errno != EOPNOTSUPP) { + sprintf(err_msg, "failed to freeze %s, %s", mount->dirname, strerror(errno)); + error_set(err, QERR_QGA_COMMAND_FAILED, err_msg); + close(fd); + goto error; + } + close(fd); + + i++; + } + + guest_fsfreeze_state.status = GUEST_FSFREEZE_STATUS_FROZEN; + return i; + +error: + if (i > 0) { + qmp_guest_fsfreeze_thaw(NULL); + } + return 0; +} + +/* + * Walk list of frozen file systems in the guest, and thaw them. + */ +int64_t qmp_guest_fsfreeze_thaw(Error **err) +{ + int ret; + GuestFsfreezeMount *mount, *temp; + int fd, i = 0; + bool has_error = false; + + QTAILQ_FOREACH_SAFE(mount, &guest_fsfreeze_state.mount_list, next, temp) { + fd = qemu_open(mount->dirname, O_RDONLY); + if (fd == -1) { + has_error = true; + continue; + } + ret = ioctl(fd, FITHAW); + if (ret < 0 && errno != EOPNOTSUPP && errno != EINVAL) { + has_error = true; + close(fd); + continue; + } + close(fd); + i++; + } + + if (has_error) { + guest_fsfreeze_state.status = GUEST_FSFREEZE_STATUS_ERROR; + } else { + guest_fsfreeze_state.status = GUEST_FSFREEZE_STATUS_THAWED; + } + enable_logging(); + return i; +} + +static void guest_fsfreeze_init(void) +{ + guest_fsfreeze_state.status = GUEST_FSFREEZE_STATUS_THAWED; + QTAILQ_INIT(&guest_fsfreeze_state.mount_list); +} + +static void guest_fsfreeze_cleanup(void) +{ + int64_t ret; + Error *err = NULL; + + if (guest_fsfreeze_state.status == GUEST_FSFREEZE_STATUS_FROZEN) { + ret = qmp_guest_fsfreeze_thaw(&err); + if (ret < 0 || err) { + slog("failed to clean up frozen filesystems"); + } + } +} + +/* register init/cleanup routines for stateful command groups */ +void ga_command_state_init(GAState *s, GACommandState *cs) +{ + ga_state = s; + ga_command_state_add(cs, guest_fsfreeze_init, guest_fsfreeze_cleanup); + ga_command_state_add(cs, guest_file_init, NULL); +} diff --git a/qga/guest-agent-core.h b/qga/guest-agent-core.h index 66d1729689..e42b91d364 100644 --- a/qga/guest-agent-core.h +++ b/qga/guest-agent-core.h @@ -14,10 +14,12 @@ #include "qemu-common.h" #define QGA_VERSION "1.0" +#define QGA_READ_COUNT_DEFAULT 4 << 10 typedef struct GAState GAState; typedef struct GACommandState GACommandState; +void ga_command_state_init(GAState *s, GACommandState *cs); void ga_command_state_add(GACommandState *cs, void (*init)(void), void (*cleanup)(void)); From 06e12b6503d08bbc27f256b9f5d7d211420b3c41 Mon Sep 17 00:00:00 2001 From: Tsuneo Saito Date: Fri, 22 Jul 2011 00:16:27 +0900 Subject: [PATCH 196/209] SPARC64: TTE bits cleanup Add macros for TTE bits and modify to use macros instead of magic numbers. Signed-off-by: Tsuneo Saito Signed-off-by: Blue Swirl --- target-sparc/cpu.h | 7 +++++++ target-sparc/helper.c | 35 +++++++++++++++++++---------------- 2 files changed, 26 insertions(+), 16 deletions(-) diff --git a/target-sparc/cpu.h b/target-sparc/cpu.h index 0084b67625..b2160e9f34 100644 --- a/target-sparc/cpu.h +++ b/target-sparc/cpu.h @@ -292,16 +292,23 @@ enum { #define TTE_VALID_BIT (1ULL << 63) #define TTE_USED_BIT (1ULL << 41) #define TTE_LOCKED_BIT (1ULL << 6) +#define TTE_PRIV_BIT (1ULL << 2) +#define TTE_W_OK_BIT (1ULL << 1) #define TTE_GLOBAL_BIT (1ULL << 0) #define TTE_IS_VALID(tte) ((tte) & TTE_VALID_BIT) #define TTE_IS_USED(tte) ((tte) & TTE_USED_BIT) #define TTE_IS_LOCKED(tte) ((tte) & TTE_LOCKED_BIT) +#define TTE_IS_PRIV(tte) ((tte) & TTE_PRIV_BIT) +#define TTE_IS_W_OK(tte) ((tte) & TTE_W_OK_BIT) #define TTE_IS_GLOBAL(tte) ((tte) & TTE_GLOBAL_BIT) #define TTE_SET_USED(tte) ((tte) |= TTE_USED_BIT) #define TTE_SET_UNUSED(tte) ((tte) &= ~TTE_USED_BIT) +#define TTE_PGSIZE(tte) (((tte) >> 61) & 3ULL) +#define TTE_PA(tte) ((tte) & 0x1ffffffe000ULL) + typedef struct SparcTLBEntry { uint64_t tag; uint64_t tte; diff --git a/target-sparc/helper.c b/target-sparc/helper.c index 7eea1acbd5..0a4cfc5bfa 100644 --- a/target-sparc/helper.c +++ b/target-sparc/helper.c @@ -378,7 +378,7 @@ static inline int ultrasparc_tag_match(SparcTLBEntry *tlb, { uint64_t mask; - switch ((tlb->tte >> 61) & 3) { + switch (TTE_PGSIZE(tlb->tte)) { default: case 0x0: // 8k mask = 0xffffffffffffe000ULL; @@ -445,14 +445,14 @@ static int get_physical_address_data(CPUState *env, uint8_t fault_type = 0; // access ok? - if ((env->dtlb[i].tte & 0x4) && is_user) { + if (TTE_IS_PRIV(env->dtlb[i].tte) && is_user) { fault_type |= 1; /* privilege violation */ env->exception_index = TT_DFAULT; DPRINTF_MMU("DFAULT at %" PRIx64 " context %" PRIx64 " mmu_idx=%d tl=%d\n", address, context, mmu_idx, env->tl); - } else if (!(env->dtlb[i].tte & 0x2) && (rw == 1)) { + } else if (!TTE_IS_W_OK(env->dtlb[i].tte) && (rw == 1)) { env->exception_index = TT_DPROT; DPRINTF_MMU("DPROT at %" PRIx64 " context %" PRIx64 @@ -460,8 +460,9 @@ static int get_physical_address_data(CPUState *env, address, context, mmu_idx, env->tl); } else { *prot = PAGE_READ; - if (env->dtlb[i].tte & 0x2) + if (TTE_IS_W_OK(env->dtlb[i].tte)) { *prot |= PAGE_WRITE; + } TTE_SET_USED(env->dtlb[i].tte); @@ -522,7 +523,7 @@ static int get_physical_address_code(CPUState *env, if (ultrasparc_tag_match(&env->itlb[i], address, context, physical)) { // access ok? - if ((env->itlb[i].tte & 0x4) && is_user) { + if (TTE_IS_PRIV(env->itlb[i].tte) && is_user) { if (env->immu.sfsr) /* Fault status register */ env->immu.sfsr = 2; /* overflow (not read before another fault) */ @@ -632,7 +633,7 @@ void dump_mmu(FILE *f, fprintf_function cpu_fprintf, CPUState *env) } else { (*cpu_fprintf)(f, "DMMU dump\n"); for (i = 0; i < 64; i++) { - switch ((env->dtlb[i].tte >> 61) & 3) { + switch (TTE_PGSIZE(env->dtlb[i].tte)) { default: case 0x0: mask = " 8k"; @@ -647,16 +648,17 @@ void dump_mmu(FILE *f, fprintf_function cpu_fprintf, CPUState *env) mask = " 4M"; break; } - if ((env->dtlb[i].tte & 0x8000000000000000ULL) != 0) { + if (TTE_IS_VALID(env->dtlb[i].tte)) { (*cpu_fprintf)(f, "[%02u] VA: %" PRIx64 ", PA: %" PRIx64 ", %s, %s, %s, %s, ctx %" PRId64 " %s\n", i, env->dtlb[i].tag & (uint64_t)~0x1fffULL, - env->dtlb[i].tte & (uint64_t)0x1ffffffe000ULL, + TTE_PA(env->dtlb[i].tte), mask, - env->dtlb[i].tte & 0x4? "priv": "user", - env->dtlb[i].tte & 0x2? "RW": "RO", - env->dtlb[i].tte & 0x40? "locked": "unlocked", + TTE_IS_PRIV(env->dtlb[i].tte) ? "priv" : "user", + TTE_IS_W_OK(env->dtlb[i].tte) ? "RW" : "RO", + TTE_IS_LOCKED(env->dtlb[i].tte) ? + "locked" : "unlocked", env->dtlb[i].tag & (uint64_t)0x1fffULL, TTE_IS_GLOBAL(env->dtlb[i].tte)? "global" : "local"); @@ -668,7 +670,7 @@ void dump_mmu(FILE *f, fprintf_function cpu_fprintf, CPUState *env) } else { (*cpu_fprintf)(f, "IMMU dump\n"); for (i = 0; i < 64; i++) { - switch ((env->itlb[i].tte >> 61) & 3) { + switch (TTE_PGSIZE(env->itlb[i].tte)) { default: case 0x0: mask = " 8k"; @@ -683,15 +685,16 @@ void dump_mmu(FILE *f, fprintf_function cpu_fprintf, CPUState *env) mask = " 4M"; break; } - if ((env->itlb[i].tte & 0x8000000000000000ULL) != 0) { + if (TTE_IS_VALID(env->itlb[i].tte)) { (*cpu_fprintf)(f, "[%02u] VA: %" PRIx64 ", PA: %" PRIx64 ", %s, %s, %s, ctx %" PRId64 " %s\n", i, env->itlb[i].tag & (uint64_t)~0x1fffULL, - env->itlb[i].tte & (uint64_t)0x1ffffffe000ULL, + TTE_PA(env->itlb[i].tte), mask, - env->itlb[i].tte & 0x4? "priv": "user", - env->itlb[i].tte & 0x40? "locked": "unlocked", + TTE_IS_PRIV(env->itlb[i].tte) ? "priv" : "user", + TTE_IS_LOCKED(env->itlb[i].tte) ? + "locked" : "unlocked", env->itlb[i].tag & (uint64_t)0x1fffULL, TTE_IS_GLOBAL(env->itlb[i].tte)? "global" : "local"); From ccc76c24efdb06b895b8ff3d0a932c905ff483d4 Mon Sep 17 00:00:00 2001 From: Tsuneo Saito Date: Fri, 22 Jul 2011 00:16:28 +0900 Subject: [PATCH 197/209] SPARC64: SFSR cleanup and fix Add macros for SFSR fields and use macros instead of magic numbers. Also fix the update of the register fields on MMU faults. Signed-off-by: Tsuneo Saito Signed-off-by: Blue Swirl --- target-sparc/cpu.h | 22 ++++++++++++++++++ target-sparc/helper.c | 52 +++++++++++++++++++++++++++++++++---------- 2 files changed, 62 insertions(+), 12 deletions(-) diff --git a/target-sparc/cpu.h b/target-sparc/cpu.h index b2160e9f34..348858e21b 100644 --- a/target-sparc/cpu.h +++ b/target-sparc/cpu.h @@ -309,6 +309,28 @@ enum { #define TTE_PGSIZE(tte) (((tte) >> 61) & 3ULL) #define TTE_PA(tte) ((tte) & 0x1ffffffe000ULL) +#define SFSR_NF_BIT (1ULL << 24) /* JPS1 NoFault */ +#define SFSR_TM_BIT (1ULL << 15) /* JPS1 TLB Miss */ +#define SFSR_FT_VA_IMMU_BIT (1ULL << 13) /* USIIi VA out of range (IMMU) */ +#define SFSR_FT_VA_DMMU_BIT (1ULL << 12) /* USIIi VA out of range (DMMU) */ +#define SFSR_FT_NFO_BIT (1ULL << 11) /* NFO page access */ +#define SFSR_FT_ILL_BIT (1ULL << 10) /* illegal LDA/STA ASI */ +#define SFSR_FT_ATOMIC_BIT (1ULL << 9) /* atomic op on noncacheable area */ +#define SFSR_FT_NF_E_BIT (1ULL << 8) /* NF access on side effect area */ +#define SFSR_FT_PRIV_BIT (1ULL << 7) /* privilege violation */ +#define SFSR_PR_BIT (1ULL << 3) /* privilege mode */ +#define SFSR_WRITE_BIT (1ULL << 2) /* write access mode */ +#define SFSR_OW_BIT (1ULL << 1) /* status overwritten */ +#define SFSR_VALID_BIT (1ULL << 0) /* status valid */ + +#define SFSR_ASI_SHIFT 16 /* 23:16 ASI value */ +#define SFSR_ASI_MASK (0xffULL << SFSR_ASI_SHIFT) +#define SFSR_CT_PRIMARY (0ULL << 4) /* 5:4 context type */ +#define SFSR_CT_SECONDARY (1ULL << 4) +#define SFSR_CT_NUCLEUS (2ULL << 4) +#define SFSR_CT_NOTRANS (3ULL << 4) +#define SFSR_CT_MASK (3ULL << 4) + typedef struct SparcTLBEntry { uint64_t tag; uint64_t tte; diff --git a/target-sparc/helper.c b/target-sparc/helper.c index 0a4cfc5bfa..f9b7fe2a68 100644 --- a/target-sparc/helper.c +++ b/target-sparc/helper.c @@ -413,6 +413,7 @@ static int get_physical_address_data(CPUState *env, { unsigned int i; uint64_t context; + uint64_t sfsr = 0; int is_user = (mmu_idx == MMU_USER_IDX || mmu_idx == MMU_USER_SECONDARY_IDX); @@ -427,26 +428,32 @@ static int get_physical_address_data(CPUState *env, case MMU_USER_IDX: case MMU_KERNEL_IDX: context = env->dmmu.mmu_primary_context & 0x1fff; + sfsr |= SFSR_CT_PRIMARY; break; case MMU_USER_SECONDARY_IDX: case MMU_KERNEL_SECONDARY_IDX: context = env->dmmu.mmu_secondary_context & 0x1fff; + sfsr |= SFSR_CT_SECONDARY; break; case MMU_NUCLEUS_IDX: + sfsr |= SFSR_CT_NUCLEUS; + /* FALLTHRU */ default: context = 0; break; } + if (rw == 1) { + sfsr |= SFSR_WRITE_BIT; + } + for (i = 0; i < 64; i++) { // ctx match, vaddr match, valid? if (ultrasparc_tag_match(&env->dtlb[i], address, context, physical)) { - uint8_t fault_type = 0; - // access ok? if (TTE_IS_PRIV(env->dtlb[i].tte) && is_user) { - fault_type |= 1; /* privilege violation */ + sfsr |= SFSR_FT_PRIV_BIT; /* privilege violation */ env->exception_index = TT_DFAULT; DPRINTF_MMU("DFAULT at %" PRIx64 " context %" PRIx64 @@ -469,13 +476,17 @@ static int get_physical_address_data(CPUState *env, return 0; } - if (env->dmmu.sfsr & 1) /* Fault status register */ - env->dmmu.sfsr = 2; /* overflow (not read before - another fault) */ + if (env->dmmu.sfsr & SFSR_VALID_BIT) { /* Fault status register */ + sfsr |= SFSR_OW_BIT; /* overflow (not read before + another fault) */ + } - env->dmmu.sfsr |= (is_user << 3) | ((rw == 1) << 2) | 1; + if (env->pstate & PS_PRIV) { + sfsr |= SFSR_PR_BIT; + } - env->dmmu.sfsr |= (fault_type << 7); + /* FIXME: ASI field in SFSR must be set */ + env->dmmu.sfsr = sfsr | SFSR_VALID_BIT; env->dmmu.sfar = address; /* Fault address register */ @@ -488,6 +499,11 @@ static int get_physical_address_data(CPUState *env, DPRINTF_MMU("DMISS at %" PRIx64 " context %" PRIx64 "\n", address, context); + /* + * On MMU misses: + * - UltraSPARC IIi: SFSR and SFAR unmodified + * - JPS1: SFAR updated and some fields of SFSR updated + */ env->dmmu.tag_access = (address & ~0x1fffULL) | context; env->exception_index = TT_DMISS; return 1; @@ -524,10 +540,22 @@ static int get_physical_address_code(CPUState *env, address, context, physical)) { // access ok? if (TTE_IS_PRIV(env->itlb[i].tte) && is_user) { - if (env->immu.sfsr) /* Fault status register */ - env->immu.sfsr = 2; /* overflow (not read before - another fault) */ - env->immu.sfsr |= (is_user << 3) | 1; + /* Fault status register */ + if (env->immu.sfsr & SFSR_VALID_BIT) { + env->immu.sfsr = SFSR_OW_BIT; /* overflow (not read before + another fault) */ + } else { + env->immu.sfsr = 0; + } + if (env->pstate & PS_PRIV) { + env->immu.sfsr |= SFSR_PR_BIT; + } + if (env->tl > 0) { + env->immu.sfsr |= SFSR_CT_NUCLEUS; + } + + /* FIXME: ASI field in SFSR must be set */ + env->immu.sfsr |= SFSR_FT_PRIV_BIT | SFSR_VALID_BIT; env->exception_index = TT_TFAULT; env->immu.tag_access = (address & ~0x1fffULL) | context; From 321365ab1734cb151090fc0b5a7bef3b97807115 Mon Sep 17 00:00:00 2001 From: Tsuneo Saito Date: Fri, 22 Jul 2011 00:16:29 +0900 Subject: [PATCH 198/209] SPARC64: introduce a convenience function for getting physical addresses Introduce cpu_sparc_get_phys_page() to be used as a help for splitting cpu_get_phys_page_debug() from cpu_get_phys_page_nofault(). Signed-off-by: Tsuneo Saito Signed-off-by: Blue Swirl --- target-sparc/helper.c | 20 ++++++++++++++------ 1 file changed, 14 insertions(+), 6 deletions(-) diff --git a/target-sparc/helper.c b/target-sparc/helper.c index f9b7fe2a68..9acbcae8c6 100644 --- a/target-sparc/helper.c +++ b/target-sparc/helper.c @@ -736,18 +736,26 @@ void dump_mmu(FILE *f, fprintf_function cpu_fprintf, CPUState *env) #if !defined(CONFIG_USER_ONLY) +static int cpu_sparc_get_phys_page(CPUState *env, target_phys_addr_t *phys, + target_ulong addr, int rw, int mmu_idx) +{ + target_ulong page_size; + int prot, access_index; + + return get_physical_address(env, phys, &prot, &access_index, addr, rw, + mmu_idx, &page_size); +} + target_phys_addr_t cpu_get_phys_page_nofault(CPUState *env, target_ulong addr, int mmu_idx) { target_phys_addr_t phys_addr; - target_ulong page_size; - int prot, access_index; - if (get_physical_address(env, &phys_addr, &prot, &access_index, addr, 2, - mmu_idx, &page_size) != 0) - if (get_physical_address(env, &phys_addr, &prot, &access_index, addr, - 0, mmu_idx, &page_size) != 0) + if (cpu_sparc_get_phys_page(env, &phys_addr, addr, 2, mmu_idx) != 0) { + if (cpu_sparc_get_phys_page(env, &phys_addr, addr, 0, mmu_idx) != 0) { return -1; + } + } if (cpu_get_physical_page_desc(phys_addr) == IO_MEM_UNASSIGNED) return -1; return phys_addr; From b64b64361413808bee8e6213095e2e3a18b3358f Mon Sep 17 00:00:00 2001 From: Tsuneo Saito Date: Fri, 22 Jul 2011 00:16:30 +0900 Subject: [PATCH 199/209] SPARC64: split cpu_get_phys_page_debug() from cpu_get_phys_page_nofault() This patch makes cpu_get_phys_page_debug() independent from cpu_get_phys_page_nofault() in advance of implementing nonfaulting load. This also modifies cpu_get_phys_page_nofault() to be compiled only on TARGET_SPARC64 because it is not required on SPARC32. Signed-off-by: Tsuneo Saito Signed-off-by: Blue Swirl --- target-sparc/cpu.h | 2 ++ target-sparc/helper.c | 15 ++++++++++++++- 2 files changed, 16 insertions(+), 1 deletion(-) diff --git a/target-sparc/cpu.h b/target-sparc/cpu.h index 348858e21b..f4eeff5b17 100644 --- a/target-sparc/cpu.h +++ b/target-sparc/cpu.h @@ -541,9 +541,11 @@ static inline int tlb_compare_context(const SparcTLBEntry *tlb, #if !defined(CONFIG_USER_ONLY) void cpu_unassigned_access(CPUState *env1, target_phys_addr_t addr, int is_write, int is_exec, int is_asi, int size); +#if defined(TARGET_SPARC64) target_phys_addr_t cpu_get_phys_page_nofault(CPUState *env, target_ulong addr, int mmu_idx); +#endif #endif int cpu_sparc_signal_handler(int host_signum, void *pinfo, void *puc); diff --git a/target-sparc/helper.c b/target-sparc/helper.c index 9acbcae8c6..cb8d706f78 100644 --- a/target-sparc/helper.c +++ b/target-sparc/helper.c @@ -746,6 +746,7 @@ static int cpu_sparc_get_phys_page(CPUState *env, target_phys_addr_t *phys, mmu_idx, &page_size); } +#if defined(TARGET_SPARC64) target_phys_addr_t cpu_get_phys_page_nofault(CPUState *env, target_ulong addr, int mmu_idx) { @@ -760,10 +761,22 @@ target_phys_addr_t cpu_get_phys_page_nofault(CPUState *env, target_ulong addr, return -1; return phys_addr; } +#endif target_phys_addr_t cpu_get_phys_page_debug(CPUState *env, target_ulong addr) { - return cpu_get_phys_page_nofault(env, addr, cpu_mmu_index(env)); + target_phys_addr_t phys_addr; + int mmu_idx = cpu_mmu_index(env); + + if (cpu_sparc_get_phys_page(env, &phys_addr, addr, 2, mmu_idx) != 0) { + if (cpu_sparc_get_phys_page(env, &phys_addr, addr, 0, mmu_idx) != 0) { + return -1; + } + } + if (cpu_get_physical_page_desc(phys_addr) == IO_MEM_UNASSIGNED) { + return -1; + } + return phys_addr; } #endif From 103dcbe581524ba777ccee1ca8ef7c3838c4d4de Mon Sep 17 00:00:00 2001 From: Tsuneo Saito Date: Fri, 22 Jul 2011 00:16:31 +0900 Subject: [PATCH 200/209] SPARC64: fix fault status overwritten on nonfaulting load cpu_get_phys_page_nofault() calls get_physical_address() twice, that results in overwriting the fault status in the SFSR. We need this change in order for nonfaulting loads to raising MMU faults as normal loads do. Also removed the call to cpu_get_physical_page_desc() since we are going to modify nonfaulting loads raising MMU faults. Signed-off-by: Tsuneo Saito Signed-off-by: Blue Swirl --- target-sparc/helper.c | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/target-sparc/helper.c b/target-sparc/helper.c index cb8d706f78..b6e62a78c2 100644 --- a/target-sparc/helper.c +++ b/target-sparc/helper.c @@ -752,13 +752,9 @@ target_phys_addr_t cpu_get_phys_page_nofault(CPUState *env, target_ulong addr, { target_phys_addr_t phys_addr; - if (cpu_sparc_get_phys_page(env, &phys_addr, addr, 2, mmu_idx) != 0) { - if (cpu_sparc_get_phys_page(env, &phys_addr, addr, 0, mmu_idx) != 0) { - return -1; - } - } - if (cpu_get_physical_page_desc(phys_addr) == IO_MEM_UNASSIGNED) + if (cpu_sparc_get_phys_page(env, &phys_addr, addr, 0, mmu_idx) != 0) { return -1; + } return phys_addr; } #endif From b7785d2072164fa8576767853af9ed517508ee57 Mon Sep 17 00:00:00 2001 From: Tsuneo Saito Date: Fri, 22 Jul 2011 00:16:32 +0900 Subject: [PATCH 201/209] SPARC64: implement MMU miss traps on nonfaulting loads Nonfaulting loads should raise fast_data_access_MMU_miss traps as normal loads do. It is up to the guest OS kernel that detect MMU misses on nonfaulting load instructions and make them complete without signaling. Signed-off-by: Tsuneo Saito Signed-off-by: Blue Swirl --- target-sparc/op_helper.c | 40 ++++++++++++++++++++++------------------ 1 file changed, 22 insertions(+), 18 deletions(-) diff --git a/target-sparc/op_helper.c b/target-sparc/op_helper.c index 3b7f9cab5d..8962e38219 100644 --- a/target-sparc/op_helper.c +++ b/target-sparc/op_helper.c @@ -2567,24 +2567,30 @@ uint64_t helper_ld_asi(target_ulong addr, int asi, int size, int sign) helper_check_align(addr, size - 1); addr = asi_address_mask(env, asi, addr); - switch (asi) { - case 0x82: // Primary no-fault - case 0x8a: // Primary no-fault LE - case 0x83: // Secondary no-fault - case 0x8b: // Secondary no-fault LE - { - /* secondary space access has lowest asi bit equal to 1 */ - int access_mmu_idx = ( asi & 1 ) ? MMU_KERNEL_IDX - : MMU_KERNEL_SECONDARY_IDX; + /* process nonfaulting loads first */ + if ((asi & 0xf6) == 0x82) { + int mmu_idx; - if (cpu_get_phys_page_nofault(env, addr, access_mmu_idx) == -1ULL) { -#ifdef DEBUG_ASI - dump_asi("read ", last_addr, asi, size, ret); -#endif - return 0; - } + /* secondary space access has lowest asi bit equal to 1 */ + if (env->pstate & PS_PRIV) { + mmu_idx = (asi & 1) ? MMU_KERNEL_SECONDARY_IDX : MMU_KERNEL_IDX; + } else { + mmu_idx = (asi & 1) ? MMU_USER_SECONDARY_IDX : MMU_USER_IDX; } - // Fall through + + if (cpu_get_phys_page_nofault(env, addr, mmu_idx) == -1ULL) { +#ifdef DEBUG_ASI + dump_asi("read ", last_addr, asi, size, ret); +#endif + /* env->exception_index is set in get_physical_address_data(). */ + raise_exception(env->exception_index); + } + + /* convert nonfaulting load ASIs to normal load ASIs */ + asi &= ~0x02; + } + + switch (asi) { case 0x10: // As if user primary case 0x11: // As if user secondary case 0x18: // As if user primary LE @@ -2862,8 +2868,6 @@ uint64_t helper_ld_asi(target_ulong addr, int asi, int size, int sign) case 0x1d: // Bypass, non-cacheable LE case 0x88: // Primary LE case 0x89: // Secondary LE - case 0x8a: // Primary no-fault LE - case 0x8b: // Secondary no-fault LE switch(size) { case 2: ret = bswap16(ret); From d1afc48b7cfdb4490f322d5d82a2aae6d545ec06 Mon Sep 17 00:00:00 2001 From: Tsuneo Saito Date: Fri, 22 Jul 2011 00:16:33 +0900 Subject: [PATCH 202/209] SPARC64: implement addtional MMU faults related to nonfaulting load This patch implements MMU faults caused by TTE.NFO and TTE.E: - access other than nonfaulting load to a page marked NFO should raise data_access_exception - nonfaulting load to a page marked with E bit should raise data_access_exception To distinguish nonfaulting loads, this patch extends (abuses?) the rw argument of get_physical_address_data(). rw is set to 4 on nonfaulting loads. Signed-off-by: Tsuneo Saito Signed-off-by: Blue Swirl --- target-sparc/cpu.h | 4 ++++ target-sparc/helper.c | 29 ++++++++++++++++++++++++++--- 2 files changed, 30 insertions(+), 3 deletions(-) diff --git a/target-sparc/cpu.h b/target-sparc/cpu.h index f4eeff5b17..a51863cf07 100644 --- a/target-sparc/cpu.h +++ b/target-sparc/cpu.h @@ -290,15 +290,19 @@ enum { #endif #define TTE_VALID_BIT (1ULL << 63) +#define TTE_NFO_BIT (1ULL << 60) #define TTE_USED_BIT (1ULL << 41) #define TTE_LOCKED_BIT (1ULL << 6) +#define TTE_SIDEEFFECT_BIT (1ULL << 3) #define TTE_PRIV_BIT (1ULL << 2) #define TTE_W_OK_BIT (1ULL << 1) #define TTE_GLOBAL_BIT (1ULL << 0) #define TTE_IS_VALID(tte) ((tte) & TTE_VALID_BIT) +#define TTE_IS_NFO(tte) ((tte) & TTE_NFO_BIT) #define TTE_IS_USED(tte) ((tte) & TTE_USED_BIT) #define TTE_IS_LOCKED(tte) ((tte) & TTE_LOCKED_BIT) +#define TTE_IS_SIDEEFFECT(tte) ((tte) & TTE_SIDEEFFECT_BIT) #define TTE_IS_PRIV(tte) ((tte) & TTE_PRIV_BIT) #define TTE_IS_W_OK(tte) ((tte) & TTE_W_OK_BIT) #define TTE_IS_GLOBAL(tte) ((tte) & TTE_GLOBAL_BIT) diff --git a/target-sparc/helper.c b/target-sparc/helper.c index b6e62a78c2..acc07f5cba 100644 --- a/target-sparc/helper.c +++ b/target-sparc/helper.c @@ -445,27 +445,50 @@ static int get_physical_address_data(CPUState *env, if (rw == 1) { sfsr |= SFSR_WRITE_BIT; + } else if (rw == 4) { + sfsr |= SFSR_NF_BIT; } for (i = 0; i < 64; i++) { // ctx match, vaddr match, valid? if (ultrasparc_tag_match(&env->dtlb[i], address, context, physical)) { + int do_fault = 0; // access ok? + /* multiple bits in SFSR.FT may be set on TT_DFAULT */ if (TTE_IS_PRIV(env->dtlb[i].tte) && is_user) { + do_fault = 1; sfsr |= SFSR_FT_PRIV_BIT; /* privilege violation */ - env->exception_index = TT_DFAULT; DPRINTF_MMU("DFAULT at %" PRIx64 " context %" PRIx64 " mmu_idx=%d tl=%d\n", address, context, mmu_idx, env->tl); + } + if (rw == 4) { + if (TTE_IS_SIDEEFFECT(env->dtlb[i].tte)) { + do_fault = 1; + sfsr |= SFSR_FT_NF_E_BIT; + } + } else { + if (TTE_IS_NFO(env->dtlb[i].tte)) { + do_fault = 1; + sfsr |= SFSR_FT_NFO_BIT; + } + } + + if (do_fault) { + /* faults above are reported with TT_DFAULT. */ + env->exception_index = TT_DFAULT; } else if (!TTE_IS_W_OK(env->dtlb[i].tte) && (rw == 1)) { + do_fault = 1; env->exception_index = TT_DPROT; DPRINTF_MMU("DPROT at %" PRIx64 " context %" PRIx64 " mmu_idx=%d tl=%d\n", address, context, mmu_idx, env->tl); - } else { + } + + if (!do_fault) { *prot = PAGE_READ; if (TTE_IS_W_OK(env->dtlb[i].tte)) { *prot |= PAGE_WRITE; @@ -752,7 +775,7 @@ target_phys_addr_t cpu_get_phys_page_nofault(CPUState *env, target_ulong addr, { target_phys_addr_t phys_addr; - if (cpu_sparc_get_phys_page(env, &phys_addr, addr, 0, mmu_idx) != 0) { + if (cpu_sparc_get_phys_page(env, &phys_addr, addr, 4, mmu_idx) != 0) { return -1; } return phys_addr; From e7e73892a66dd3850eb723ee2ef9ef3f4569529f Mon Sep 17 00:00:00 2001 From: Gerd Hoffmann Date: Fri, 8 Jul 2011 13:19:01 +0200 Subject: [PATCH 203/209] usb-hid: fixup changed tracking. Remove leftover calls to usb_hid_changed(). Take care to update the changed flag after delivering a event via GET_REPORT like we do when sending events via interrupt endpoint. Signed-off-by: Gerd Hoffmann --- hw/usb-hid.c | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/hw/usb-hid.c b/hw/usb-hid.c index d711b5c0be..b812da2a6a 100644 --- a/hw/usb-hid.c +++ b/hw/usb-hid.c @@ -531,18 +531,15 @@ static void usb_keyboard_process_keycode(USBHIDState *hs) case 0xe0: if (s->modifiers & (1 << 9)) { s->modifiers ^= 3 << 8; - usb_hid_changed(hs); return; } case 0xe1 ... 0xe7: if (keycode & (1 << 7)) { s->modifiers &= ~(1 << (hid_code & 0x0f)); - usb_hid_changed(hs); return; } case 0xe8 ... 0xef: s->modifiers |= 1 << (hid_code & 0x0f); - usb_hid_changed(hs); return; } @@ -769,10 +766,12 @@ static int usb_hid_handle_control(USBDevice *dev, USBPacket *p, } break; case GET_REPORT: - if (s->kind == USB_MOUSE || s->kind == USB_TABLET) + if (s->kind == USB_MOUSE || s->kind == USB_TABLET) { ret = usb_pointer_poll(s, data, length); - else if (s->kind == USB_KEYBOARD) + } else if (s->kind == USB_KEYBOARD) { ret = usb_keyboard_poll(s, data, length); + } + s->changed = s->n > 0; break; case SET_REPORT: if (s->kind == USB_KEYBOARD) From 0070f095d9aa216ebe4c06c43d0072275f23c8af Mon Sep 17 00:00:00 2001 From: Gerd Hoffmann Date: Fri, 8 Jul 2011 17:28:11 +0200 Subject: [PATCH 204/209] usb-uhci: fix irq handling on error. Spec on UHCI_STS_USBERR: "If the TD on which the error interrupt occurred also had its IOC bit set, both this bit and Bit 0 are set." Make UHCI emulation do that. Signed-off-by: Gerd Hoffmann --- hw/usb-uhci.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/hw/usb-uhci.c b/hw/usb-uhci.c index 2ef4c5b747..da74c57c62 100644 --- a/hw/usb-uhci.c +++ b/hw/usb-uhci.c @@ -730,6 +730,9 @@ out: td->ctrl |= TD_CTRL_STALL; td->ctrl &= ~TD_CTRL_ACTIVE; s->status |= UHCI_STS_USBERR; + if (td->ctrl & TD_CTRL_IOC) { + *int_mask |= 0x01; + } uhci_update_irq(s); return 1; @@ -737,6 +740,9 @@ out: td->ctrl |= TD_CTRL_BABBLE | TD_CTRL_STALL; td->ctrl &= ~TD_CTRL_ACTIVE; s->status |= UHCI_STS_USBERR; + if (td->ctrl & TD_CTRL_IOC) { + *int_mask |= 0x01; + } uhci_update_irq(s); /* frame interrupted */ return -1; From 2e97f03ed7719d95e739c4050459b5026ef3cc6c Mon Sep 17 00:00:00 2001 From: Gerd Hoffmann Date: Wed, 20 Jul 2011 15:44:01 +0200 Subject: [PATCH 205/209] usb-ehci: trace: rename "next" to "nxt". "next" is reserved in systemtap thus using this as a trace parameter name causes trouble when trying to trace with systemtap. Signed-off-by: Gerd Hoffmann --- trace-events | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/trace-events b/trace-events index 99a4a2b144..713f042081 100644 --- a/trace-events +++ b/trace-events @@ -216,13 +216,13 @@ disable usb_ehci_mmio_writel(uint32_t addr, const char *str, uint32_t val) "wr m disable usb_ehci_mmio_change(uint32_t addr, const char *str, uint32_t new, uint32_t old) "ch mmio %04x [%s] = %x (old: %x)" disable usb_ehci_usbsts(const char *sts, int state) "usbsts %s %d" disable usb_ehci_state(const char *schedule, const char *state) "%s schedule %s" -disable usb_ehci_qh_ptrs(void *q, uint32_t addr, uint32_t next, uint32_t c_qtd, uint32_t n_qtd, uint32_t a_qtd) "q %p - QH @ %08x: next %08x qtds %08x,%08x,%08x" +disable usb_ehci_qh_ptrs(void *q, uint32_t addr, uint32_t nxt, uint32_t c_qtd, uint32_t n_qtd, uint32_t a_qtd) "q %p - QH @ %08x: next %08x qtds %08x,%08x,%08x" disable usb_ehci_qh_fields(uint32_t addr, int rl, int mplen, int eps, int ep, int devaddr) "QH @ %08x - rl %d, mplen %d, eps %d, ep %d, dev %d" disable usb_ehci_qh_bits(uint32_t addr, int c, int h, int dtc, int i) "QH @ %08x - c %d, h %d, dtc %d, i %d" -disable usb_ehci_qtd_ptrs(void *q, uint32_t addr, uint32_t next, uint32_t altnext) "q %p - QTD @ %08x: next %08x altnext %08x" +disable usb_ehci_qtd_ptrs(void *q, uint32_t addr, uint32_t nxt, uint32_t altnext) "q %p - QTD @ %08x: next %08x altnext %08x" disable usb_ehci_qtd_fields(uint32_t addr, int tbytes, int cpage, int cerr, int pid) "QTD @ %08x - tbytes %d, cpage %d, cerr %d, pid %d" disable usb_ehci_qtd_bits(uint32_t addr, int ioc, int active, int halt, int babble, int xacterr) "QTD @ %08x - ioc %d, active %d, halt %d, babble %d, xacterr %d" -disable usb_ehci_itd(uint32_t addr, uint32_t next, uint32_t mplen, uint32_t mult, uint32_t ep, uint32_t devaddr) "ITD @ %08x: next %08x - mplen %d, mult %d, ep %d, dev %d" +disable usb_ehci_itd(uint32_t addr, uint32_t nxt, uint32_t mplen, uint32_t mult, uint32_t ep, uint32_t devaddr) "ITD @ %08x: next %08x - mplen %d, mult %d, ep %d, dev %d" disable usb_ehci_port_attach(uint32_t port, const char *device) "attach port #%d - %s" disable usb_ehci_port_detach(uint32_t port) "detach port #%d" disable usb_ehci_port_reset(uint32_t port, int enable) "reset port #%d - %d" From 69354a83346da2d4d8eb9cda18b639794566c902 Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Tue, 19 Jul 2011 11:04:10 +0200 Subject: [PATCH 206/209] USB: add usb network redirection support This patch adds support for a usb-redir device, which takes a chardev as a communication channel to an actual usbdevice using the usbredir protocol. Compiling the usb-redir device requires usbredir-0.3 to be installed for the usbredir protocol parser, usbredir-0.3 also contains a server for redirecting usb traffic from an actual usb device. You can get the 0.3 release of usbredir here: http://people.fedoraproject.org/~jwrdegoede/usbredir-0.3.tar.bz2 (getting a more formal site for it is a WIP) Example usage: 1) Start usbredirserver for a usb device: sudo usbredirserver 045e:0772 2) Start qemu with usb2 support + a chardev talking to usbredirserver + a usb-redir device using this chardev: qemu ... \ -readconfig docs/ich9-ehci-uhci.cfg \ -chardev socket,id=usbredirchardev,host=localhost,port=4000 \ -device usb-redir,chardev=usbredirchardev,id=usbredirdev Signed-off-by: Hans de Goede Signed-off-by: Gerd Hoffmann --- Makefile.objs | 1 + configure | 28 ++ usb-redir.c | 1218 +++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 1247 insertions(+) create mode 100644 usb-redir.c diff --git a/Makefile.objs b/Makefile.objs index c43ed05c89..a82974bf9a 100644 --- a/Makefile.objs +++ b/Makefile.objs @@ -205,6 +205,7 @@ hw-obj-$(CONFIG_HPET) += hpet.o hw-obj-$(CONFIG_APPLESMC) += applesmc.o hw-obj-$(CONFIG_SMARTCARD) += usb-ccid.o ccid-card-passthru.o hw-obj-$(CONFIG_SMARTCARD_NSS) += ccid-card-emulated.o +hw-obj-$(CONFIG_USB_REDIR) += usb-redir.o # PPC devices hw-obj-$(CONFIG_OPENPIC) += openpic.o diff --git a/configure b/configure index e57efb179c..3a76bb8b4b 100755 --- a/configure +++ b/configure @@ -177,6 +177,7 @@ spice="" rbd="" smartcard="" smartcard_nss="" +usb_redir="" opengl="" # parse CC options first @@ -743,6 +744,10 @@ for opt do ;; --enable-smartcard-nss) smartcard_nss="yes" ;; + --disable-usb-redir) usb_redir="no" + ;; + --enable-usb-redir) usb_redir="yes" + ;; *) echo "ERROR: unknown option $opt"; show_help="yes" ;; esac @@ -1018,6 +1023,8 @@ echo " --disable-smartcard disable smartcard support" echo " --enable-smartcard enable smartcard support" echo " --disable-smartcard-nss disable smartcard nss support" echo " --enable-smartcard-nss enable smartcard nss support" +echo " --disable-usb-redir disable usb network redirection support" +echo " --enable-usb-redir enable usb network redirection support" echo "" echo "NOTE: The object files are built at the place where configure is launched" exit 1 @@ -2371,6 +2378,22 @@ if test "$smartcard" = "no" ; then smartcard_nss="no" fi +# check for usbredirparser for usb network redirection support +if test "$usb_redir" != "no" ; then + if $pkg_config libusbredirparser >/dev/null 2>&1 ; then + usb_redir="yes" + usb_redir_cflags=$($pkg_config --cflags libusbredirparser 2>/dev/null) + usb_redir_libs=$($pkg_config --libs libusbredirparser 2>/dev/null) + QEMU_CFLAGS="$QEMU_CFLAGS $usb_redir_cflags" + LIBS="$LIBS $usb_redir_libs" + else + if test "$usb_redir" = "yes"; then + feature_not_found "usb-redir" + fi + usb_redir="no" + fi +fi + ########################################## ########################################## @@ -2617,6 +2640,7 @@ echo "spice support $spice" echo "rbd support $rbd" echo "xfsctl support $xfs" echo "nss used $smartcard_nss" +echo "usb net redir $usb_redir" echo "OpenGL support $opengl" if test $sdl_too_old = "yes"; then @@ -2910,6 +2934,10 @@ if test "$smartcard_nss" = "yes" ; then echo "CONFIG_SMARTCARD_NSS=y" >> $config_host_mak fi +if test "$usb_redir" = "yes" ; then + echo "CONFIG_USB_REDIR=y" >> $config_host_mak +fi + if test "$opengl" = "yes" ; then echo "CONFIG_OPENGL=y" >> $config_host_mak fi diff --git a/usb-redir.c b/usb-redir.c new file mode 100644 index 0000000000..e2129931a0 --- /dev/null +++ b/usb-redir.c @@ -0,0 +1,1218 @@ +/* + * USB redirector usb-guest + * + * Copyright (c) 2011 Red Hat, Inc. + * + * Red Hat Authors: + * Hans de Goede + * + * 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-timer.h" +#include "monitor.h" +#include "sysemu.h" + +#include +#include +#include +#include + +#include "hw/usb.h" + +#define MAX_ENDPOINTS 32 +#define EP2I(ep_address) (((ep_address & 0x80) >> 3) | (ep_address & 0x0f)) +#define I2EP(i) (((i & 0x10) << 3) | (i & 0x0f)) + +typedef struct AsyncURB AsyncURB; +typedef struct USBRedirDevice USBRedirDevice; + +/* Struct to hold buffered packets (iso or int input packets) */ +struct buf_packet { + uint8_t *data; + int len; + int status; + QTAILQ_ENTRY(buf_packet)next; +}; + +struct endp_data { + uint8_t type; + uint8_t interval; + uint8_t interface; /* bInterfaceNumber this ep belongs to */ + uint8_t iso_started; + uint8_t iso_error; /* For reporting iso errors to the HC */ + uint8_t interrupt_started; + uint8_t interrupt_error; + QTAILQ_HEAD(, buf_packet) bufpq; +}; + +struct USBRedirDevice { + USBDevice dev; + /* Properties */ + CharDriverState *cs; + uint8_t debug; + /* Data passed from chardev the fd_read cb to the usbredirparser read cb */ + const uint8_t *read_buf; + int read_buf_size; + /* For async handling of open/close */ + QEMUBH *open_close_bh; + /* To delay the usb attach in case of quick chardev close + open */ + QEMUTimer *attach_timer; + int64_t next_attach_time; + struct usbredirparser *parser; + struct endp_data endpoint[MAX_ENDPOINTS]; + uint32_t packet_id; + QTAILQ_HEAD(, AsyncURB) asyncq; +}; + +struct AsyncURB { + USBRedirDevice *dev; + USBPacket *packet; + uint32_t packet_id; + int get; + union { + struct usb_redir_control_packet_header control_packet; + struct usb_redir_bulk_packet_header bulk_packet; + struct usb_redir_interrupt_packet_header interrupt_packet; + }; + QTAILQ_ENTRY(AsyncURB)next; +}; + +static void usbredir_device_connect(void *priv, + struct usb_redir_device_connect_header *device_connect); +static void usbredir_device_disconnect(void *priv); +static void usbredir_interface_info(void *priv, + struct usb_redir_interface_info_header *interface_info); +static void usbredir_ep_info(void *priv, + struct usb_redir_ep_info_header *ep_info); +static void usbredir_configuration_status(void *priv, uint32_t id, + struct usb_redir_configuration_status_header *configuration_status); +static void usbredir_alt_setting_status(void *priv, uint32_t id, + struct usb_redir_alt_setting_status_header *alt_setting_status); +static void usbredir_iso_stream_status(void *priv, uint32_t id, + struct usb_redir_iso_stream_status_header *iso_stream_status); +static void usbredir_interrupt_receiving_status(void *priv, uint32_t id, + struct usb_redir_interrupt_receiving_status_header + *interrupt_receiving_status); +static void usbredir_bulk_streams_status(void *priv, uint32_t id, + struct usb_redir_bulk_streams_status_header *bulk_streams_status); +static void usbredir_control_packet(void *priv, uint32_t id, + struct usb_redir_control_packet_header *control_packet, + uint8_t *data, int data_len); +static void usbredir_bulk_packet(void *priv, uint32_t id, + struct usb_redir_bulk_packet_header *bulk_packet, + uint8_t *data, int data_len); +static void usbredir_iso_packet(void *priv, uint32_t id, + struct usb_redir_iso_packet_header *iso_packet, + uint8_t *data, int data_len); +static void usbredir_interrupt_packet(void *priv, uint32_t id, + struct usb_redir_interrupt_packet_header *interrupt_header, + uint8_t *data, int data_len); + +static int usbredir_handle_status(USBRedirDevice *dev, + int status, int actual_len); + +#define VERSION "qemu usb-redir guest " QEMU_VERSION + +/* + * Logging stuff + */ + +#define ERROR(...) \ + do { \ + if (dev->debug >= usbredirparser_error) { \ + error_report("usb-redir error: " __VA_ARGS__); \ + } \ + } while (0) +#define WARNING(...) \ + do { \ + if (dev->debug >= usbredirparser_warning) { \ + error_report("usb-redir warning: " __VA_ARGS__); \ + } \ + } while (0) +#define INFO(...) \ + do { \ + if (dev->debug >= usbredirparser_info) { \ + error_report("usb-redir: " __VA_ARGS__); \ + } \ + } while (0) +#define DPRINTF(...) \ + do { \ + if (dev->debug >= usbredirparser_debug) { \ + error_report("usb-redir: " __VA_ARGS__); \ + } \ + } while (0) +#define DPRINTF2(...) \ + do { \ + if (dev->debug >= usbredirparser_debug_data) { \ + error_report("usb-redir: " __VA_ARGS__); \ + } \ + } while (0) + +static void usbredir_log(void *priv, int level, const char *msg) +{ + USBRedirDevice *dev = priv; + + if (dev->debug < level) { + return; + } + + error_report("%s\n", msg); +} + +static void usbredir_log_data(USBRedirDevice *dev, const char *desc, + const uint8_t *data, int len) +{ + int i, j, n; + + if (dev->debug < usbredirparser_debug_data) { + return; + } + + for (i = 0; i < len; i += j) { + char buf[128]; + + n = sprintf(buf, "%s", desc); + for (j = 0; j < 8 && i + j < len; j++) { + n += sprintf(buf + n, " %02X", data[i + j]); + } + error_report("%s\n", buf); + } +} + +/* + * usbredirparser io functions + */ + +static int usbredir_read(void *priv, uint8_t *data, int count) +{ + USBRedirDevice *dev = priv; + + if (dev->read_buf_size < count) { + count = dev->read_buf_size; + } + + memcpy(data, dev->read_buf, count); + + dev->read_buf_size -= count; + if (dev->read_buf_size) { + dev->read_buf += count; + } else { + dev->read_buf = NULL; + } + + return count; +} + +static int usbredir_write(void *priv, uint8_t *data, int count) +{ + USBRedirDevice *dev = priv; + + return qemu_chr_write(dev->cs, data, count); +} + +/* + * Async and buffered packets helpers + */ + +static AsyncURB *async_alloc(USBRedirDevice *dev, USBPacket *p) +{ + AsyncURB *aurb = (AsyncURB *) qemu_mallocz(sizeof(AsyncURB)); + aurb->dev = dev; + aurb->packet = p; + aurb->packet_id = dev->packet_id; + QTAILQ_INSERT_TAIL(&dev->asyncq, aurb, next); + dev->packet_id++; + + return aurb; +} + +static void async_free(USBRedirDevice *dev, AsyncURB *aurb) +{ + QTAILQ_REMOVE(&dev->asyncq, aurb, next); + qemu_free(aurb); +} + +static AsyncURB *async_find(USBRedirDevice *dev, uint32_t packet_id) +{ + AsyncURB *aurb; + + QTAILQ_FOREACH(aurb, &dev->asyncq, next) { + if (aurb->packet_id == packet_id) { + return aurb; + } + } + ERROR("could not find async urb for packet_id %u\n", packet_id); + return NULL; +} + +static void usbredir_cancel_packet(USBDevice *udev, USBPacket *p) +{ + USBRedirDevice *dev = DO_UPCAST(USBRedirDevice, dev, udev); + AsyncURB *aurb; + + QTAILQ_FOREACH(aurb, &dev->asyncq, next) { + if (p != aurb->packet) { + continue; + } + + DPRINTF("async cancel id %u\n", aurb->packet_id); + usbredirparser_send_cancel_data_packet(dev->parser, aurb->packet_id); + usbredirparser_do_write(dev->parser); + + /* Mark it as dead */ + aurb->packet = NULL; + break; + } +} + +static struct buf_packet *bufp_alloc(USBRedirDevice *dev, + uint8_t *data, int len, int status, uint8_t ep) +{ + struct buf_packet *bufp = qemu_malloc(sizeof(struct buf_packet)); + bufp->data = data; + bufp->len = len; + bufp->status = status; + QTAILQ_INSERT_TAIL(&dev->endpoint[EP2I(ep)].bufpq, bufp, next); + return bufp; +} + +static void bufp_free(USBRedirDevice *dev, struct buf_packet *bufp, + uint8_t ep) +{ + QTAILQ_REMOVE(&dev->endpoint[EP2I(ep)].bufpq, bufp, next); + free(bufp->data); + qemu_free(bufp); +} + +static void usbredir_free_bufpq(USBRedirDevice *dev, uint8_t ep) +{ + struct buf_packet *buf, *buf_next; + + QTAILQ_FOREACH_SAFE(buf, &dev->endpoint[EP2I(ep)].bufpq, next, buf_next) { + bufp_free(dev, buf, ep); + } +} + +/* + * USBDevice callbacks + */ + +static void usbredir_handle_reset(USBDevice *udev) +{ + USBRedirDevice *dev = DO_UPCAST(USBRedirDevice, dev, udev); + + DPRINTF("reset device\n"); + usbredirparser_send_reset(dev->parser); + usbredirparser_do_write(dev->parser); +} + +static int usbredir_handle_iso_data(USBRedirDevice *dev, USBPacket *p, + uint8_t ep) +{ + int status, len; + + if (!dev->endpoint[EP2I(ep)].iso_started && + !dev->endpoint[EP2I(ep)].iso_error) { + struct usb_redir_start_iso_stream_header start_iso = { + .endpoint = ep, + /* TODO maybe do something with these depending on ep interval? */ + .pkts_per_urb = 32, + .no_urbs = 3, + }; + /* No id, we look at the ep when receiving a status back */ + usbredirparser_send_start_iso_stream(dev->parser, 0, &start_iso); + usbredirparser_do_write(dev->parser); + DPRINTF("iso stream started ep %02X\n", ep); + dev->endpoint[EP2I(ep)].iso_started = 1; + } + + if (ep & USB_DIR_IN) { + struct buf_packet *isop; + + isop = QTAILQ_FIRST(&dev->endpoint[EP2I(ep)].bufpq); + if (isop == NULL) { + DPRINTF2("iso-token-in ep %02X, no isop\n", ep); + /* Check iso_error for stream errors, otherwise its an underrun */ + status = dev->endpoint[EP2I(ep)].iso_error; + dev->endpoint[EP2I(ep)].iso_error = 0; + return usbredir_handle_status(dev, status, 0); + } + DPRINTF2("iso-token-in ep %02X status %d len %d\n", ep, isop->status, + isop->len); + + status = isop->status; + if (status != usb_redir_success) { + bufp_free(dev, isop, ep); + return usbredir_handle_status(dev, status, 0); + } + + len = isop->len; + if (len > p->len) { + ERROR("received iso data is larger then packet ep %02X\n", ep); + bufp_free(dev, isop, ep); + return USB_RET_NAK; + } + memcpy(p->data, isop->data, len); + bufp_free(dev, isop, ep); + return len; + } else { + /* If the stream was not started because of a pending error don't + send the packet to the usb-host */ + if (dev->endpoint[EP2I(ep)].iso_started) { + struct usb_redir_iso_packet_header iso_packet = { + .endpoint = ep, + .length = p->len + }; + /* No id, we look at the ep when receiving a status back */ + usbredirparser_send_iso_packet(dev->parser, 0, &iso_packet, + p->data, p->len); + usbredirparser_do_write(dev->parser); + } + status = dev->endpoint[EP2I(ep)].iso_error; + dev->endpoint[EP2I(ep)].iso_error = 0; + DPRINTF2("iso-token-out ep %02X status %d len %d\n", ep, status, + p->len); + return usbredir_handle_status(dev, status, p->len); + } +} + +static void usbredir_stop_iso_stream(USBRedirDevice *dev, uint8_t ep) +{ + struct usb_redir_stop_iso_stream_header stop_iso_stream = { + .endpoint = ep + }; + if (dev->endpoint[EP2I(ep)].iso_started) { + usbredirparser_send_stop_iso_stream(dev->parser, 0, &stop_iso_stream); + DPRINTF("iso stream stopped ep %02X\n", ep); + dev->endpoint[EP2I(ep)].iso_started = 0; + } + usbredir_free_bufpq(dev, ep); +} + +static int usbredir_handle_bulk_data(USBRedirDevice *dev, USBPacket *p, + uint8_t ep) +{ + AsyncURB *aurb = async_alloc(dev, p); + struct usb_redir_bulk_packet_header bulk_packet; + + DPRINTF("bulk-out ep %02X len %d id %u\n", ep, p->len, aurb->packet_id); + + bulk_packet.endpoint = ep; + bulk_packet.length = p->len; + bulk_packet.stream_id = 0; + aurb->bulk_packet = bulk_packet; + + if (ep & USB_DIR_IN) { + usbredirparser_send_bulk_packet(dev->parser, aurb->packet_id, + &bulk_packet, NULL, 0); + } else { + usbredir_log_data(dev, "bulk data out:", p->data, p->len); + usbredirparser_send_bulk_packet(dev->parser, aurb->packet_id, + &bulk_packet, p->data, p->len); + } + usbredirparser_do_write(dev->parser); + return USB_RET_ASYNC; +} + +static int usbredir_handle_interrupt_data(USBRedirDevice *dev, + USBPacket *p, uint8_t ep) +{ + if (ep & USB_DIR_IN) { + /* Input interrupt endpoint, buffered packet input */ + struct buf_packet *intp; + int status, len; + + if (!dev->endpoint[EP2I(ep)].interrupt_started && + !dev->endpoint[EP2I(ep)].interrupt_error) { + struct usb_redir_start_interrupt_receiving_header start_int = { + .endpoint = ep, + }; + /* No id, we look at the ep when receiving a status back */ + usbredirparser_send_start_interrupt_receiving(dev->parser, 0, + &start_int); + usbredirparser_do_write(dev->parser); + DPRINTF("interrupt recv started ep %02X\n", ep); + dev->endpoint[EP2I(ep)].interrupt_started = 1; + } + + intp = QTAILQ_FIRST(&dev->endpoint[EP2I(ep)].bufpq); + if (intp == NULL) { + DPRINTF2("interrupt-token-in ep %02X, no intp\n", ep); + /* Check interrupt_error for stream errors */ + status = dev->endpoint[EP2I(ep)].interrupt_error; + dev->endpoint[EP2I(ep)].interrupt_error = 0; + return usbredir_handle_status(dev, status, 0); + } + DPRINTF("interrupt-token-in ep %02X status %d len %d\n", ep, + intp->status, intp->len); + + status = intp->status; + if (status != usb_redir_success) { + bufp_free(dev, intp, ep); + return usbredir_handle_status(dev, status, 0); + } + + len = intp->len; + if (len > p->len) { + ERROR("received int data is larger then packet ep %02X\n", ep); + bufp_free(dev, intp, ep); + return USB_RET_NAK; + } + memcpy(p->data, intp->data, len); + bufp_free(dev, intp, ep); + return len; + } else { + /* Output interrupt endpoint, normal async operation */ + AsyncURB *aurb = async_alloc(dev, p); + struct usb_redir_interrupt_packet_header interrupt_packet; + + DPRINTF("interrupt-out ep %02X len %d id %u\n", ep, p->len, + aurb->packet_id); + + interrupt_packet.endpoint = ep; + interrupt_packet.length = p->len; + aurb->interrupt_packet = interrupt_packet; + + usbredir_log_data(dev, "interrupt data out:", p->data, p->len); + usbredirparser_send_interrupt_packet(dev->parser, aurb->packet_id, + &interrupt_packet, p->data, p->len); + usbredirparser_do_write(dev->parser); + return USB_RET_ASYNC; + } +} + +static void usbredir_stop_interrupt_receiving(USBRedirDevice *dev, + uint8_t ep) +{ + struct usb_redir_stop_interrupt_receiving_header stop_interrupt_recv = { + .endpoint = ep + }; + if (dev->endpoint[EP2I(ep)].interrupt_started) { + usbredirparser_send_stop_interrupt_receiving(dev->parser, 0, + &stop_interrupt_recv); + DPRINTF("interrupt recv stopped ep %02X\n", ep); + dev->endpoint[EP2I(ep)].interrupt_started = 0; + } + usbredir_free_bufpq(dev, ep); +} + +static int usbredir_handle_data(USBDevice *udev, USBPacket *p) +{ + USBRedirDevice *dev = DO_UPCAST(USBRedirDevice, dev, udev); + uint8_t ep; + + ep = p->devep; + if (p->pid == USB_TOKEN_IN) { + ep |= USB_DIR_IN; + } + + switch (dev->endpoint[EP2I(ep)].type) { + case USB_ENDPOINT_XFER_CONTROL: + ERROR("handle_data called for control transfer on ep %02X\n", ep); + return USB_RET_NAK; + case USB_ENDPOINT_XFER_ISOC: + return usbredir_handle_iso_data(dev, p, ep); + case USB_ENDPOINT_XFER_BULK: + return usbredir_handle_bulk_data(dev, p, ep);; + case USB_ENDPOINT_XFER_INT: + return usbredir_handle_interrupt_data(dev, p, ep);; + default: + ERROR("handle_data ep %02X has unknown type %d\n", ep, + dev->endpoint[EP2I(ep)].type); + return USB_RET_NAK; + } +} + +static int usbredir_set_config(USBRedirDevice *dev, USBPacket *p, + int config) +{ + struct usb_redir_set_configuration_header set_config; + AsyncURB *aurb = async_alloc(dev, p); + int i; + + DPRINTF("set config %d id %u\n", config, aurb->packet_id); + + for (i = 0; i < MAX_ENDPOINTS; i++) { + switch (dev->endpoint[i].type) { + case USB_ENDPOINT_XFER_ISOC: + usbredir_stop_iso_stream(dev, I2EP(i)); + break; + case USB_ENDPOINT_XFER_INT: + if (i & 0x10) { + usbredir_stop_interrupt_receiving(dev, I2EP(i)); + } + break; + } + usbredir_free_bufpq(dev, I2EP(i)); + } + + set_config.configuration = config; + usbredirparser_send_set_configuration(dev->parser, aurb->packet_id, + &set_config); + usbredirparser_do_write(dev->parser); + return USB_RET_ASYNC; +} + +static int usbredir_get_config(USBRedirDevice *dev, USBPacket *p) +{ + AsyncURB *aurb = async_alloc(dev, p); + + DPRINTF("get config id %u\n", aurb->packet_id); + + aurb->get = 1; + usbredirparser_send_get_configuration(dev->parser, aurb->packet_id); + usbredirparser_do_write(dev->parser); + return USB_RET_ASYNC; +} + +static int usbredir_set_interface(USBRedirDevice *dev, USBPacket *p, + int interface, int alt) +{ + struct usb_redir_set_alt_setting_header set_alt; + AsyncURB *aurb = async_alloc(dev, p); + int i; + + DPRINTF("set interface %d alt %d id %u\n", interface, alt, + aurb->packet_id); + + for (i = 0; i < MAX_ENDPOINTS; i++) { + if (dev->endpoint[i].interface == interface) { + switch (dev->endpoint[i].type) { + case USB_ENDPOINT_XFER_ISOC: + usbredir_stop_iso_stream(dev, I2EP(i)); + break; + case USB_ENDPOINT_XFER_INT: + if (i & 0x10) { + usbredir_stop_interrupt_receiving(dev, I2EP(i)); + } + break; + } + usbredir_free_bufpq(dev, I2EP(i)); + } + } + + set_alt.interface = interface; + set_alt.alt = alt; + usbredirparser_send_set_alt_setting(dev->parser, aurb->packet_id, + &set_alt); + usbredirparser_do_write(dev->parser); + return USB_RET_ASYNC; +} + +static int usbredir_get_interface(USBRedirDevice *dev, USBPacket *p, + int interface) +{ + struct usb_redir_get_alt_setting_header get_alt; + AsyncURB *aurb = async_alloc(dev, p); + + DPRINTF("get interface %d id %u\n", interface, aurb->packet_id); + + get_alt.interface = interface; + aurb->get = 1; + usbredirparser_send_get_alt_setting(dev->parser, aurb->packet_id, + &get_alt); + usbredirparser_do_write(dev->parser); + return USB_RET_ASYNC; +} + +static int usbredir_handle_control(USBDevice *udev, USBPacket *p, + int request, int value, int index, int length, uint8_t *data) +{ + USBRedirDevice *dev = DO_UPCAST(USBRedirDevice, dev, udev); + struct usb_redir_control_packet_header control_packet; + AsyncURB *aurb; + + /* Special cases for certain standard device requests */ + switch (request) { + case DeviceOutRequest | USB_REQ_SET_ADDRESS: + DPRINTF("set address %d\n", value); + dev->dev.addr = value; + return 0; + case DeviceOutRequest | USB_REQ_SET_CONFIGURATION: + return usbredir_set_config(dev, p, value & 0xff); + case DeviceRequest | USB_REQ_GET_CONFIGURATION: + return usbredir_get_config(dev, p); + case InterfaceOutRequest | USB_REQ_SET_INTERFACE: + return usbredir_set_interface(dev, p, index, value); + case InterfaceRequest | USB_REQ_GET_INTERFACE: + return usbredir_get_interface(dev, p, index); + } + + /* "Normal" ctrl requests */ + aurb = async_alloc(dev, p); + + /* Note request is (bRequestType << 8) | bRequest */ + DPRINTF("ctrl-out type 0x%x req 0x%x val 0x%x index %d len %d id %u\n", + request >> 8, request & 0xff, value, index, length, + aurb->packet_id); + + control_packet.request = request & 0xFF; + control_packet.requesttype = request >> 8; + control_packet.endpoint = control_packet.requesttype & USB_DIR_IN; + control_packet.value = value; + control_packet.index = index; + control_packet.length = length; + aurb->control_packet = control_packet; + + if (control_packet.requesttype & USB_DIR_IN) { + usbredirparser_send_control_packet(dev->parser, aurb->packet_id, + &control_packet, NULL, 0); + } else { + usbredir_log_data(dev, "ctrl data out:", data, length); + usbredirparser_send_control_packet(dev->parser, aurb->packet_id, + &control_packet, data, length); + } + usbredirparser_do_write(dev->parser); + return USB_RET_ASYNC; +} + +/* + * Close events can be triggered by usbredirparser_do_write which gets called + * from within the USBDevice data / control packet callbacks and doing a + * usb_detach from within these callbacks is not a good idea. + * + * So we use a bh handler to take care of close events. We also handle + * open events from this callback to make sure that a close directly followed + * by an open gets handled in the right order. + */ +static void usbredir_open_close_bh(void *opaque) +{ + USBRedirDevice *dev = opaque; + + usbredir_device_disconnect(dev); + + if (dev->parser) { + usbredirparser_destroy(dev->parser); + dev->parser = NULL; + } + + if (dev->cs->opened) { + dev->parser = qemu_oom_check(usbredirparser_create()); + dev->parser->priv = dev; + dev->parser->log_func = usbredir_log; + dev->parser->read_func = usbredir_read; + dev->parser->write_func = usbredir_write; + dev->parser->device_connect_func = usbredir_device_connect; + dev->parser->device_disconnect_func = usbredir_device_disconnect; + dev->parser->interface_info_func = usbredir_interface_info; + dev->parser->ep_info_func = usbredir_ep_info; + dev->parser->configuration_status_func = usbredir_configuration_status; + dev->parser->alt_setting_status_func = usbredir_alt_setting_status; + dev->parser->iso_stream_status_func = usbredir_iso_stream_status; + dev->parser->interrupt_receiving_status_func = + usbredir_interrupt_receiving_status; + dev->parser->bulk_streams_status_func = usbredir_bulk_streams_status; + dev->parser->control_packet_func = usbredir_control_packet; + dev->parser->bulk_packet_func = usbredir_bulk_packet; + dev->parser->iso_packet_func = usbredir_iso_packet; + dev->parser->interrupt_packet_func = usbredir_interrupt_packet; + dev->read_buf = NULL; + dev->read_buf_size = 0; + usbredirparser_init(dev->parser, VERSION, NULL, 0, 0); + usbredirparser_do_write(dev->parser); + } +} + +static void usbredir_do_attach(void *opaque) +{ + USBRedirDevice *dev = opaque; + + usb_device_attach(&dev->dev); +} + +/* + * chardev callbacks + */ + +static int usbredir_chardev_can_read(void *opaque) +{ + USBRedirDevice *dev = opaque; + + if (dev->parser) { + /* usbredir_parser_do_read will consume *all* data we give it */ + return 1024 * 1024; + } else { + /* usbredir_open_close_bh hasn't handled the open event yet */ + return 0; + } +} + +static void usbredir_chardev_read(void *opaque, const uint8_t *buf, int size) +{ + USBRedirDevice *dev = opaque; + + /* No recursion allowed! */ + assert(dev->read_buf == NULL); + + dev->read_buf = buf; + dev->read_buf_size = size; + + usbredirparser_do_read(dev->parser); + /* Send any acks, etc. which may be queued now */ + usbredirparser_do_write(dev->parser); +} + +static void usbredir_chardev_event(void *opaque, int event) +{ + USBRedirDevice *dev = opaque; + + switch (event) { + case CHR_EVENT_OPENED: + case CHR_EVENT_CLOSED: + qemu_bh_schedule(dev->open_close_bh); + break; + } +} + +/* + * init + destroy + */ + +static int usbredir_initfn(USBDevice *udev) +{ + USBRedirDevice *dev = DO_UPCAST(USBRedirDevice, dev, udev); + int i; + + if (dev->cs == NULL) { + qerror_report(QERR_MISSING_PARAMETER, "chardev"); + return -1; + } + + dev->open_close_bh = qemu_bh_new(usbredir_open_close_bh, dev); + dev->attach_timer = qemu_new_timer_ms(vm_clock, usbredir_do_attach, dev); + + QTAILQ_INIT(&dev->asyncq); + for (i = 0; i < MAX_ENDPOINTS; i++) { + QTAILQ_INIT(&dev->endpoint[i].bufpq); + } + + /* We'll do the attach once we receive the speed from the usb-host */ + udev->auto_attach = 0; + + qemu_chr_add_handlers(dev->cs, usbredir_chardev_can_read, + usbredir_chardev_read, usbredir_chardev_event, dev); + + return 0; +} + +static void usbredir_cleanup_device_queues(USBRedirDevice *dev) +{ + AsyncURB *aurb, *next_aurb; + int i; + + QTAILQ_FOREACH_SAFE(aurb, &dev->asyncq, next, next_aurb) { + async_free(dev, aurb); + } + for (i = 0; i < MAX_ENDPOINTS; i++) { + usbredir_free_bufpq(dev, I2EP(i)); + } +} + +static void usbredir_handle_destroy(USBDevice *udev) +{ + USBRedirDevice *dev = DO_UPCAST(USBRedirDevice, dev, udev); + + qemu_chr_close(dev->cs); + /* Note must be done after qemu_chr_close, as that causes a close event */ + qemu_bh_delete(dev->open_close_bh); + + qemu_del_timer(dev->attach_timer); + qemu_free_timer(dev->attach_timer); + + usbredir_cleanup_device_queues(dev); + + if (dev->parser) { + usbredirparser_destroy(dev->parser); + } +} + +/* + * usbredirparser packet complete callbacks + */ + +static int usbredir_handle_status(USBRedirDevice *dev, + int status, int actual_len) +{ + switch (status) { + case usb_redir_success: + return actual_len; + case usb_redir_stall: + return USB_RET_STALL; + case usb_redir_cancelled: + WARNING("returning cancelled packet to HC?\n"); + case usb_redir_inval: + case usb_redir_ioerror: + case usb_redir_timeout: + default: + return USB_RET_NAK; + } +} + +static void usbredir_device_connect(void *priv, + struct usb_redir_device_connect_header *device_connect) +{ + USBRedirDevice *dev = priv; + + switch (device_connect->speed) { + case usb_redir_speed_low: + DPRINTF("attaching low speed device\n"); + dev->dev.speed = USB_SPEED_LOW; + break; + case usb_redir_speed_full: + DPRINTF("attaching full speed device\n"); + dev->dev.speed = USB_SPEED_FULL; + break; + case usb_redir_speed_high: + DPRINTF("attaching high speed device\n"); + dev->dev.speed = USB_SPEED_HIGH; + break; + case usb_redir_speed_super: + DPRINTF("attaching super speed device\n"); + dev->dev.speed = USB_SPEED_SUPER; + break; + default: + DPRINTF("attaching unknown speed device, assuming full speed\n"); + dev->dev.speed = USB_SPEED_FULL; + } + dev->dev.speedmask = (1 << dev->dev.speed); + qemu_mod_timer(dev->attach_timer, dev->next_attach_time); +} + +static void usbredir_device_disconnect(void *priv) +{ + USBRedirDevice *dev = priv; + + /* Stop any pending attaches */ + qemu_del_timer(dev->attach_timer); + + if (dev->dev.attached) { + usb_device_detach(&dev->dev); + usbredir_cleanup_device_queues(dev); + /* + * Delay next usb device attach to give the guest a chance to see + * see the detach / attach in case of quick close / open succession + */ + dev->next_attach_time = qemu_get_clock_ms(vm_clock) + 200; + } +} + +static void usbredir_interface_info(void *priv, + struct usb_redir_interface_info_header *interface_info) +{ + /* The intention is to allow specifying acceptable interface classes + for redirection on the cmdline and in the future verify this here, + and disconnect (or never connect) the device if a not accepted + interface class is detected */ +} + +static void usbredir_ep_info(void *priv, + struct usb_redir_ep_info_header *ep_info) +{ + USBRedirDevice *dev = priv; + int i; + + for (i = 0; i < MAX_ENDPOINTS; i++) { + dev->endpoint[i].type = ep_info->type[i]; + dev->endpoint[i].interval = ep_info->interval[i]; + dev->endpoint[i].interface = ep_info->interface[i]; + if (dev->endpoint[i].type != usb_redir_type_invalid) { + DPRINTF("ep: %02X type: %d interface: %d\n", I2EP(i), + dev->endpoint[i].type, dev->endpoint[i].interface); + } + } +} + +static void usbredir_configuration_status(void *priv, uint32_t id, + struct usb_redir_configuration_status_header *config_status) +{ + USBRedirDevice *dev = priv; + AsyncURB *aurb; + int len = 0; + + DPRINTF("set config status %d config %d id %u\n", config_status->status, + config_status->configuration, id); + + aurb = async_find(dev, id); + if (!aurb) { + return; + } + if (aurb->packet) { + if (aurb->get) { + dev->dev.data_buf[0] = config_status->configuration; + len = 1; + } + aurb->packet->len = + usbredir_handle_status(dev, config_status->status, len); + usb_generic_async_ctrl_complete(&dev->dev, aurb->packet); + } + async_free(dev, aurb); +} + +static void usbredir_alt_setting_status(void *priv, uint32_t id, + struct usb_redir_alt_setting_status_header *alt_setting_status) +{ + USBRedirDevice *dev = priv; + AsyncURB *aurb; + int len = 0; + + DPRINTF("alt status %d intf %d alt %d id: %u\n", + alt_setting_status->status, + alt_setting_status->interface, + alt_setting_status->alt, id); + + aurb = async_find(dev, id); + if (!aurb) { + return; + } + if (aurb->packet) { + if (aurb->get) { + dev->dev.data_buf[0] = alt_setting_status->alt; + len = 1; + } + aurb->packet->len = + usbredir_handle_status(dev, alt_setting_status->status, len); + usb_generic_async_ctrl_complete(&dev->dev, aurb->packet); + } + async_free(dev, aurb); +} + +static void usbredir_iso_stream_status(void *priv, uint32_t id, + struct usb_redir_iso_stream_status_header *iso_stream_status) +{ + USBRedirDevice *dev = priv; + uint8_t ep = iso_stream_status->endpoint; + + DPRINTF("iso status %d ep %02X id %u\n", iso_stream_status->status, + ep, id); + + dev->endpoint[EP2I(ep)].iso_error = iso_stream_status->status; + if (iso_stream_status->status == usb_redir_stall) { + DPRINTF("iso stream stopped by peer ep %02X\n", ep); + dev->endpoint[EP2I(ep)].iso_started = 0; + } +} + +static void usbredir_interrupt_receiving_status(void *priv, uint32_t id, + struct usb_redir_interrupt_receiving_status_header + *interrupt_receiving_status) +{ + USBRedirDevice *dev = priv; + uint8_t ep = interrupt_receiving_status->endpoint; + + DPRINTF("interrupt recv status %d ep %02X id %u\n", + interrupt_receiving_status->status, ep, id); + + dev->endpoint[EP2I(ep)].interrupt_error = + interrupt_receiving_status->status; + if (interrupt_receiving_status->status == usb_redir_stall) { + DPRINTF("interrupt receiving stopped by peer ep %02X\n", ep); + dev->endpoint[EP2I(ep)].interrupt_started = 0; + } +} + +static void usbredir_bulk_streams_status(void *priv, uint32_t id, + struct usb_redir_bulk_streams_status_header *bulk_streams_status) +{ +} + +static void usbredir_control_packet(void *priv, uint32_t id, + struct usb_redir_control_packet_header *control_packet, + uint8_t *data, int data_len) +{ + USBRedirDevice *dev = priv; + int len = control_packet->length; + AsyncURB *aurb; + + DPRINTF("ctrl-in status %d len %d id %u\n", control_packet->status, + len, id); + + aurb = async_find(dev, id); + if (!aurb) { + free(data); + return; + } + + aurb->control_packet.status = control_packet->status; + aurb->control_packet.length = control_packet->length; + if (memcmp(&aurb->control_packet, control_packet, + sizeof(*control_packet))) { + ERROR("return control packet mismatch, please report this!\n"); + len = USB_RET_NAK; + } + + if (aurb->packet) { + len = usbredir_handle_status(dev, control_packet->status, len); + if (len > 0) { + usbredir_log_data(dev, "ctrl data in:", data, data_len); + if (data_len <= sizeof(dev->dev.data_buf)) { + memcpy(dev->dev.data_buf, data, data_len); + } else { + ERROR("ctrl buffer too small (%d > %zu)\n", + data_len, sizeof(dev->dev.data_buf)); + len = USB_RET_STALL; + } + } + aurb->packet->len = len; + usb_generic_async_ctrl_complete(&dev->dev, aurb->packet); + } + async_free(dev, aurb); + free(data); +} + +static void usbredir_bulk_packet(void *priv, uint32_t id, + struct usb_redir_bulk_packet_header *bulk_packet, + uint8_t *data, int data_len) +{ + USBRedirDevice *dev = priv; + uint8_t ep = bulk_packet->endpoint; + int len = bulk_packet->length; + AsyncURB *aurb; + + DPRINTF("bulk-in status %d ep %02X len %d id %u\n", bulk_packet->status, + ep, len, id); + + aurb = async_find(dev, id); + if (!aurb) { + free(data); + return; + } + + if (aurb->bulk_packet.endpoint != bulk_packet->endpoint || + aurb->bulk_packet.stream_id != bulk_packet->stream_id) { + ERROR("return bulk packet mismatch, please report this!\n"); + len = USB_RET_NAK; + } + + if (aurb->packet) { + len = usbredir_handle_status(dev, bulk_packet->status, len); + if (len > 0) { + usbredir_log_data(dev, "bulk data in:", data, data_len); + if (data_len <= aurb->packet->len) { + memcpy(aurb->packet->data, data, data_len); + } else { + ERROR("bulk buffer too small (%d > %d)\n", data_len, + aurb->packet->len); + len = USB_RET_STALL; + } + } + aurb->packet->len = len; + usb_packet_complete(&dev->dev, aurb->packet); + } + async_free(dev, aurb); + free(data); +} + +static void usbredir_iso_packet(void *priv, uint32_t id, + struct usb_redir_iso_packet_header *iso_packet, + uint8_t *data, int data_len) +{ + USBRedirDevice *dev = priv; + uint8_t ep = iso_packet->endpoint; + + DPRINTF2("iso-in status %d ep %02X len %d id %u\n", iso_packet->status, ep, + data_len, id); + + if (dev->endpoint[EP2I(ep)].type != USB_ENDPOINT_XFER_ISOC) { + ERROR("received iso packet for non iso endpoint %02X\n", ep); + free(data); + return; + } + + if (dev->endpoint[EP2I(ep)].iso_started == 0) { + DPRINTF("received iso packet for non started stream ep %02X\n", ep); + free(data); + return; + } + + /* bufp_alloc also adds the packet to the ep queue */ + bufp_alloc(dev, data, data_len, iso_packet->status, ep); +} + +static void usbredir_interrupt_packet(void *priv, uint32_t id, + struct usb_redir_interrupt_packet_header *interrupt_packet, + uint8_t *data, int data_len) +{ + USBRedirDevice *dev = priv; + uint8_t ep = interrupt_packet->endpoint; + + DPRINTF("interrupt-in status %d ep %02X len %d id %u\n", + interrupt_packet->status, ep, data_len, id); + + if (dev->endpoint[EP2I(ep)].type != USB_ENDPOINT_XFER_INT) { + ERROR("received int packet for non interrupt endpoint %02X\n", ep); + free(data); + return; + } + + if (ep & USB_DIR_IN) { + if (dev->endpoint[EP2I(ep)].interrupt_started == 0) { + DPRINTF("received int packet while not started ep %02X\n", ep); + free(data); + return; + } + + /* bufp_alloc also adds the packet to the ep queue */ + bufp_alloc(dev, data, data_len, interrupt_packet->status, ep); + } else { + int len = interrupt_packet->length; + + AsyncURB *aurb = async_find(dev, id); + if (!aurb) { + return; + } + + if (aurb->interrupt_packet.endpoint != interrupt_packet->endpoint) { + ERROR("return int packet mismatch, please report this!\n"); + len = USB_RET_NAK; + } + + if (aurb->packet) { + aurb->packet->len = usbredir_handle_status(dev, + interrupt_packet->status, len); + usb_packet_complete(&dev->dev, aurb->packet); + } + async_free(dev, aurb); + } +} + +static struct USBDeviceInfo usbredir_dev_info = { + .product_desc = "USB Redirection Device", + .qdev.name = "usb-redir", + .qdev.size = sizeof(USBRedirDevice), + .init = usbredir_initfn, + .handle_destroy = usbredir_handle_destroy, + .handle_packet = usb_generic_handle_packet, + .cancel_packet = usbredir_cancel_packet, + .handle_reset = usbredir_handle_reset, + .handle_data = usbredir_handle_data, + .handle_control = usbredir_handle_control, + .qdev.props = (Property[]) { + DEFINE_PROP_CHR("chardev", USBRedirDevice, cs), + DEFINE_PROP_UINT8("debug", USBRedirDevice, debug, 0), + DEFINE_PROP_END_OF_LIST(), + }, +}; + +static void usbredir_register_devices(void) +{ + usb_qdev_register(&usbredir_dev_info); +} +device_init(usbredir_register_devices); From 3b8b030a280f3b496602dc7d2ee5c68e480ec10b Mon Sep 17 00:00:00 2001 From: Stefan Weil Date: Fri, 22 Jul 2011 07:44:27 +0200 Subject: [PATCH 207/209] target-sparc: Fix compiler errors (format strings) This change is needed because commit 06e12b65 now uses an unsigned long long value (uint64_t && unsigned long long => unsigned long long). Cc: Tsuneo Saito Cc: Blue Swirl Signed-off-by: Stefan Weil Signed-off-by: Blue Swirl --- target-sparc/helper.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/target-sparc/helper.c b/target-sparc/helper.c index acc07f5cba..efab885b83 100644 --- a/target-sparc/helper.c +++ b/target-sparc/helper.c @@ -700,7 +700,7 @@ void dump_mmu(FILE *f, fprintf_function cpu_fprintf, CPUState *env) break; } if (TTE_IS_VALID(env->dtlb[i].tte)) { - (*cpu_fprintf)(f, "[%02u] VA: %" PRIx64 ", PA: %" PRIx64 + (*cpu_fprintf)(f, "[%02u] VA: %" PRIx64 ", PA: %llx" ", %s, %s, %s, %s, ctx %" PRId64 " %s\n", i, env->dtlb[i].tag & (uint64_t)~0x1fffULL, @@ -737,7 +737,7 @@ void dump_mmu(FILE *f, fprintf_function cpu_fprintf, CPUState *env) break; } if (TTE_IS_VALID(env->itlb[i].tte)) { - (*cpu_fprintf)(f, "[%02u] VA: %" PRIx64 ", PA: %" PRIx64 + (*cpu_fprintf)(f, "[%02u] VA: %" PRIx64 ", PA: %llx" ", %s, %s, %s, ctx %" PRId64 " %s\n", i, env->itlb[i].tag & (uint64_t)~0x1fffULL, From fd2a9d2fc719d53caeb37c28440a0e9d34c1b563 Mon Sep 17 00:00:00 2001 From: Avi Kivity Date: Wed, 20 Jul 2011 12:14:06 +0300 Subject: [PATCH 208/209] xen: fix xen-mapcache build on non-Xen capable targets Signed-off-by: Avi Kivity Signed-off-by: Blue Swirl --- xen-mapcache.h | 32 ++++++++++++++++++++++++++++++++ 1 file changed, 32 insertions(+) diff --git a/xen-mapcache.h b/xen-mapcache.h index 606b8afc52..da874ca23d 100644 --- a/xen-mapcache.h +++ b/xen-mapcache.h @@ -9,6 +9,10 @@ #ifndef XEN_MAPCACHE_H #define XEN_MAPCACHE_H +#include + +#ifdef CONFIG_XEN + void xen_map_cache_init(void); uint8_t *xen_map_cache(target_phys_addr_t phys_addr, target_phys_addr_t size, uint8_t lock); @@ -16,4 +20,32 @@ ram_addr_t xen_ram_addr_from_mapcache(void *ptr); void xen_invalidate_map_cache_entry(uint8_t *buffer); void xen_invalidate_map_cache(void); +#else + +static inline void xen_map_cache_init(void) +{ +} + +static inline uint8_t *xen_map_cache(target_phys_addr_t phys_addr, + target_phys_addr_t size, + uint8_t lock) +{ + abort(); +} + +static inline ram_addr_t xen_ram_addr_from_mapcache(void *ptr) +{ + abort(); +} + +static inline void xen_invalidate_map_cache_entry(uint8_t *buffer) +{ +} + +static inline void xen_invalidate_map_cache(void) +{ +} + +#endif + #endif /* !XEN_MAPCACHE_H */ From 7006b9cff356233c3db0150ec55fd999c49dfae3 Mon Sep 17 00:00:00 2001 From: Anthony Liguori Date: Fri, 22 Jul 2011 14:14:17 -0500 Subject: [PATCH 209/209] guest-agent: fix build with OpenBSD FS-Freeze only works with Linux. Signed-off-by: Anthony Liguori --- qga/guest-agent-commands.c | 63 +++++++++++++++++++++++++++++++------- 1 file changed, 52 insertions(+), 11 deletions(-) diff --git a/qga/guest-agent-commands.c b/qga/guest-agent-commands.c index 8c0d67ee28..e215bd3c5f 100644 --- a/qga/guest-agent-commands.c +++ b/qga/guest-agent-commands.c @@ -10,11 +10,17 @@ * See the COPYING file in the top-level directory. */ +#if defined(__linux__) +#define CONFIG_FSFREEZE +#endif + #include +#if defined(CONFIG_FSFREEZE) #include +#include +#endif #include #include -#include #include "qga/guest-agent-core.h" #include "qga-qmp-commands.h" #include "qerror.h" @@ -22,16 +28,6 @@ static GAState *ga_state; -static void disable_logging(void) -{ - ga_disable_logging(ga_state); -} - -static void enable_logging(void) -{ - ga_enable_logging(ga_state); -} - /* Note: in some situations, like with the fsfreeze, logging may be * temporarilly disabled. if it is necessary that a command be able * to log for accounting purposes, check ga_logging_enabled() beforehand, @@ -323,6 +319,17 @@ static void guest_file_init(void) QTAILQ_INIT(&guest_file_state.filehandles); } +#if defined(CONFIG_FSFREEZE) +static void disable_logging(void) +{ + ga_disable_logging(ga_state); +} + +static void enable_logging(void) +{ + ga_enable_logging(ga_state); +} + typedef struct GuestFsfreezeMount { char *dirname; char *devtype; @@ -508,11 +515,45 @@ static void guest_fsfreeze_cleanup(void) } } } +#else +/* + * Return status of freeze/thaw + */ +GuestFsfreezeStatus qmp_guest_fsfreeze_status(Error **err) +{ + error_set(err, QERR_COMMAND_NOT_FOUND, "guest_fsfreeze_status"); + + return 0; +} + +/* + * Walk list of mounted file systems in the guest, and freeze the ones which + * are real local file systems. + */ +int64_t qmp_guest_fsfreeze_freeze(Error **err) +{ + error_set(err, QERR_COMMAND_NOT_FOUND, "guest_fsfreeze_freeze"); + + return 0; +} + +/* + * Walk list of frozen file systems in the guest, and thaw them. + */ +int64_t qmp_guest_fsfreeze_thaw(Error **err) +{ + error_set(err, QERR_COMMAND_NOT_FOUND, "guest_fsfreeze_thaw"); + + return 0; +} +#endif /* register init/cleanup routines for stateful command groups */ void ga_command_state_init(GAState *s, GACommandState *cs) { ga_state = s; +#if defined(CONFIG_FSFREEZE) ga_command_state_add(cs, guest_fsfreeze_init, guest_fsfreeze_cleanup); +#endif ga_command_state_add(cs, guest_file_init, NULL); }