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=Hfk3=9k!3=9l{3=9kc3}C>R%fP^{Gtp3Z<8B|8 zdS(U&1{MYeMkxjc21W)31||@lo|B)Hn4?=-l9`iPl9^iUkZNFHU;x$)QV%kWEv-05 zC$}Kk!qmh>H@&o^7%a=c0FvimU|{fN-~)+(?F5N|j8%e~54NKqInyE4V6qr%psWD{ z!w-;R7-naHnDc;vfzf$$3oAb(W8mZ|Y?~RQCmS#-Z_Z}dX5`|m9Sg8>5r1H%pm2KEvL1_liV1_nU}1_l9;AOizqJOcyU9te9-k&IB+Ff%YP zaDmvq0nQetCMGVyAPxfqNL+-0fq@Mn4rWzUKvaO_S)hIenFZr3FfcHH_>74;nTf@z z#URr_a&im|3_AM7`N<$l98wJ+K)*P(C^Ip~!Nk-69EuQoL26BKt2IMb%g4aLV547< zoC!A5!T=`61ae1mK`Gb)3=ng1xfkR{#(@0t)S>`q9kBZ#jsdwFq{lJ2pcJGA#`gtr zkjxBZU|<0G8z!HO#xG@HXaH$}$v2_#`$3XO<}E_wZ-DYaega{}+|=CsqDrtR$V_$y z1_nnoF%AX>2ADfR3c>o&gC8V;WG^V)g5nNjCdl6)KOyrSi}OKV1E&pcP}(TY%q_@C z)lGtiGDsf}0|UbWkhu>)1SA)L#KG0w9GTF_2xba0Kx|`YtgrFlaF_Fo41aBnDD*7pe&) z26q!Q#lh5o+yo0365ZqmH69#>Ap1ymQ$c33g9#}2fP4l?*C5tAXc&Ra1jQpr4C-D+ z5DoGVhz9xFmjR>-gdzGtd5A4Jqo_b9zaTYFw;&T7%MdwEP})KjhPj;)R6c;h0c17^ zGh${#m|6xEr#W^~;1)$=~6y*@VCTHXqr{;ky0{csjfq~H%cUcdw7eID^FlTOlN@@;6a(-?> zVo7FFPAY?PVoFL;YH_h{a%xUaF@th(W>qSb4Jj)O7$CGELqT$;0Yge^S!Qx-d`V?N zDnn6fIzx(Xa$<5us%}nZUaBrgGeahZ2uux%G?-J8nVXuFSe&YxR+L&;nwpnf$&gf9 zjKoXM$xqHk;uj_6rKc7%fE=rvnU|7U!H}6(l3G+$T2NAqVCohm7NzEuAPFSr=am%Y z=j5aoF(|`zK>eAMnO9n&Q;?CEmy(l;q$fAAKsPtBI2%<6>_D(H7$6#pOH%SnOLPko zOESRmGD?hwz-S1JhQMeDjE2By2#kinXb6mkz-S1JhQMeDjE2By2#kinXb6mkz-S1J zhQMeDjE2By2#kinXb6mkz-S1JhQMeDjE2By2#kinXb6mkz-S1JhQMeDjE2Cl4gmnn CJ`#BV diff --git a/pc-bios/mpc8544ds.dts b/pc-bios/mpc8544ds.dts index 872152df93..fd792d5f88 100644 --- a/pc-bios/mpc8544ds.dts +++ b/pc-bios/mpc8544ds.dts @@ -82,6 +82,12 @@ compatible = "chrp,open-pic"; device_type = "open-pic"; }; + + global-utilities@e0000 { //global utilities block + compatible = "fsl,mpc8544-guts"; + reg = <0xe0000 0x1000>; + 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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

G`P`tsz082UN!N4fv!NA-I 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 DEy#;R literal 729876 zcmb<-^>JflVq|~=CI%4(1_s9e9~wYHCJfw)Eg%Mi34;Oy6N3f=7Xv3qo`Hd(;{OK* z1`uZ8R#?KoecgtEfq|WYfq?@o&#fr)|Ns9#Ak4tPzyigrU^xZ`h6E_~{{R1vO~U_= z7RjwFHJQxI{2EvkJs21ypkgjaVk{Pl3<3>|3??-k%*z@W87yk}KPogZGT78`g82?L zj9{9f!I6QZA(4Tnp^-tLVIhM^!$Ag#hKCF?jf@NmjfxB^jgAZ&jfo68jg1Tj4T=mb zJ`4;gH5p9HY#RQ5v}oyhP}2!^hXUB&G$K40lqzUs^pG&t`~Sa*L&Bjna%7F6^RgNR zhGij&40bgNj4Cw-PRlG5|8JPU$iPq|=(KDCBLm9>M+S}wi3~gw8W{v8EMyRwaF9V_ z!b1j`iHr;i6BQX$COR@`OiX0Znb^o+P{Y9l(qmG?z@Rn(ls7oRe483ZFzsNW`2U0V z|35Qe-tqqbR}jV?2zp@r|9cJ0uipRv45EHYRuMzJ5pCj7;HHFU0YO0x* zB_t~_)KoJst0{C^2C9c)`E$6_-v7Uk==k^k|80c(|LX|%|CbT&|IZ`b|DQ&<|3BjO zzxV%-g~PcH^#1<=um6X$-$8!!{{Mc2`~Tet_y5}w?*BI<-2bmfxc^^`aR0v?(f$Y3 zfundd1V%$(Gz3ONU^E0qLttcu0QUL6e+Pa*=RbciGI0EGWDxj~$RP2fkwM|dLI#Z= z2N?{0JY=x=$;jaFQ<1^rrz1na&qRiZpN$L&KNm7|{5;4o;pan!8NV1A7W`6VSn2+JN`U54e=ss+faX$vFftU>a563X!N^bnnoIq`$j|_qONFjafUj8qtxb3V(!;^L z>;*^<|3`%vAU&L5egkN2!V5+Qh8KGgL85~|J zGI+dnWC(bf$Pn?eks;ybLWYi)2N@>3e8@246(hrfSBeZPUO6&sc$LVo<5eTWfmaI| zPP{tEaN*TMh8wRL86Lb=WO(t~k>SJZM1~))8yOj1FJ$C+eUMS$^+QI97m5rVgK2(c zn8ZJPeacXYe+vc%h8hM&6`O?rA1sm?7%UnX7%UbjcZ4iuuxL2Uz|ioRfu)g|fum8G zfoH-I28Ibw7+5BU|9}qz zqmBmylc|mJ{|`2b|Nn<5GKkd}I4`qMY+&$UNSx@wkm(Cr`vY3{1IlMp6#sviz{ns{ zvx0e>i1+dwT3>-BbOv@%TGO*Y*{QqpxqQKD5!oV=4<$u8h6$Xx) zM&*hLjto2#7#Rd=JeihFaAXjvabQrJ;K(3R;|S)?YR=^;bLWJZRZ$%+gGlN}jKCMPmfOm1YTnY@spVe&zSmdOtpIy@M*R5mPRP-r;F zpwjS=L8Fn8L8noX!JyHR!K5*f!J@H|!KQH`gG1v%2A9T%3?5C43_eYY3;|7!3?WU4 z3=vI@3^7d$84{WfGNd#;WXNb{WXNe&WGHBMWGHD)WT)O0W}YiMNPXi#Ke0EI0GTP$FZ zuSwRZsA*ORsA32C`m@7u(n09rp)lO|nJ!yu7kk<6gr#PI(ID7}Ei8O+yM97BkK z(hJBeTZIn{mYS>r77Pr`8#oylpnRbYO$LSzP6mb$1_!+wMdk_!yFm`YKEPL_%v=F7 zV`SB!qrM#tfzc2c4S~@R7!85Z5Eu=C(GVC7fzc2c4S~@R7!85Z5Eu=C(GVC7fzc2c z4S~@R7!85Z5Eu=C(GVC7fzc2c4S~@R7!85Z5Eu=C(GVC7fzc2c4S~@R7!85Z5Eu=C z(GVC7fzc2c4S~@R7!85Z5Eu=C(GVC7fzc2c4S~@R7!85Z5Eu=C(GVC7fzc2c4S~@R z7!85Z5Eu=C(GVC7fzc2c4S~@R7!85Z5Eu=C(GVC7fzc2c4S~@R7!85Z5Eu=C(GVC7 zfzc2c4S~@R7!85Z5Eu;s)DWo2b$V9Q5kXq>Z;Rp6BGN{zlNmoo@WN@jO zt6WjDU%R5#-f5W)L;inT&HrZ;82t8sma1nTgkL6ykW6j zs8)hn_?%XQ&czNY#sRjD{?T%Bzpb-U+d*&P^-yQ5$ZKTDpb?LAk~Y3*@A(A zBhl-BLk%bMvRY3Mi(1X-idswWidt>&iUw{5mJo)M|LPkaFgVU*5V6c=5O8An{|0pK zx(5RThXn%zqa_0ar?mqELoFxsG7km@nFdY<7V9VmMoUcwfdXy@M$o+jAq?{W^cxr$ z9Og0bI5GTx;K9IXW1YyrVX=Tgpg#F_1?b#%55_Z&mRA4&hiWRAStc+r)>?Xr)a-6q zRkN~n70Ars1_pz`1onRuI2l+PI2m|?7ci&=KVW?q{D93M_yJo{@B_9j!4KHJ1wUZ7 z2!6m`75spG-vmz3Iq#oRJQx@yEI3&h!smqU3*odd2+3Yzmp-R~p*Df3qJEBd1t{!l zEt!^uW(Q}5YBI1B0a|1EXa&14jt>%z6fs5Qdv|sA0n3W2yQ7fB0SowNOn7i~9Y) zE9$#E^(-|RSn6MS>eVoS{J{`loACd4v$sJAD2(eJ!z~I5apyI($|53mB|9^*l z3=;J({}t3f{#{Y1$&g{G$-p$xj)B24kwL(N@iqg@9?;qQ-v9pxg58tMAOms}$UWfv zh4AA)m>+EtAbv&DF9_DJ$RGgo4@f@@TO>fvjK@MVg49fCW?-;LU;wi%5*S!OVht<| zEEAX+7#dg@I3_SNur#nR@JwK4;AmiB5SYNszyneP!XS6lXmeM9&ieOYyg3yXX5Rn* zO|+5!|G`$_|N9zdhJZxP|NrY7JV0T`;r;(#DJUF3VdKGg^9RTbkbY2DcrcV-g!u&? z4jfF&kmNt&m#;9zkWc{@!VUf(h1ah-Q0z(#v2H6XB`-2IL3@RRsH?G!yW?EJw z#I($!k%0r`KJWj3KQ=66;HXKFu7L1=^I9e{D0nd5biks2YW**!Wi`?$`ZuEL|8*9s zp8@12kQtz|#AX2lBPh+;KmPwd{6B*js5}MP2M!a)<2OO&Y5h;;Wflw!0-$^f%HOt` z3Jez9EDWLA57=rLBA)JQymUdjJ0u+q|EF0pu6&|9=8&nqLHf%&0&7xx#~?T+sXf zA6q1OLo|6?@Be>fYidDt(I3&~|8R3S>yLb{s9F3Y0Ay$V;@1@(43(9TFulIS15{Rl z)64G-6B=1$EE-rCCNQ#ufXe+DjNtO0VFn`urv)QR#Ds+`3N@+H6%!7!s8}R|{mWx< zkb%R3kws#EBa6d?hb$V_4;e%zGP3B{ANc=!q9Tidbpr##L`N1A(0vsijHlOv+~>i- z;Gy5Zz~!)yfn)MKgP2A;gBblz21W}`2F3|;1~Cr%7-TFG80Vf(L`R zZbv->gY|y~Mh^yY#SVD}21xn(e~V=zO90q!jMvUUn(| ziSX}#Wce=;`F}q_^0o^9e>Y|tXjpJFFizk!&;ap67zC^=HQfbjG8y$ic>xsXAba*V zG6>YvNLPUDv48OYw?*C>`N_}pJQ!@Zg4y#Kcr22i8F?_+?X9VIT4ulC z|7Y+2|0AvUKC`o!&maKuyZwRxpFJ4t=7Q8)-~0N{BKeu82ZL>?#e4=4NE)(hY^Y)2 z@&5l`(PBP>1W1nugIz92y+!h~P!9%MPmB2sGElXl4K)m)vgfPCdM=j&|*G=1yt=`sM=N!2Ai1{^BHWQYL_N|Vpt3z7xq;b(QLNPa|35*C`3xRVJ+V+dD?J!& zEG_0U_(1izLiMnCFp4>P|Nnc}Vm<>T%`w<&LiOzNV6b7en9mRb)guTMKjXn*{T9rg z&k$je{OpPcgUwekKl#}s4+iUtAoUjW8DcybZ0IuOgI| z1gW){4=U@Qz42hMNd)tgpZ)Y;u(q|B&yWMv=Lr^{&ro2I{G0(4E;bAv3<(ShOqL4F z3==pR7$AHlCQAhtFyDfKnW2V(eT@YJ3zTMHs1f&G1}Qf$8-VH&nEz_(e=YN1ywnNG zKOjFs{Pw37YG(<=Z`Mb_{N!gZJQ%DNLfBvbS*uvzmN8ldjd z1@j^9;smqjGqhMFfBomdU=5A`=L{C}L3ia?GeX^Ig6huSTfP7P`-sJzJsu2JXR)~x zlrF$_B|ql_xzmHeDi>5v**ZYVE=wq*D3}yyqWHnP1|6c(4d;dZP75fAKKUv>PV3@$jU;+wb3ps{@3GxO89*n}iV0Sly z`wlGD3m6zY7=;qScOfw_Fj#>46fMsqEEX`Z%wS~T@nGDSs^84O;INOu1Z2MlqtH_Q zW(G!3J>#&CLBS${fz5+a=pe{_8!`<($RsmZ)Zdd5nS9^C03@bAmw~ZS)4-sClR*W< zpTKFr0_q1r!#39Y{|~VLz5oA^^k5K@g^Ew|{{Q1FC|p4PnWFeVV80^66p%kAFfwfM z{{Lkos1EmFNZRYc&@&g5Zaf&CJhg8CmCuJDWqtB{FYo_9I-%wr_x}F_oZi9yOn$*; z(egsl`~QzjkUyc}5#Yfn7ztJn3Wr990E^@o+A|m#LcIU~aD@0>aOHyi3=_`=Qm z{|{A*1O`?QM!{Yr@kHhS?mRKae`0Bx6?hCTVBKgH%4+b+$i}?&IAoYZ~DMb7whX;chsQy?3 zG6QNaq+CsYDPlbbR4$u>>c0&TeP(|_@#+2lJE;EK0@CBbUn(ns=xL?)x*MNj|YS4RFGPW`3wg<7#Mzl!ozy+OJk6j2ZL#)#e9Y% zi109L1c`&xcrcj8TJL>!#bQ3g36Ple9FSklpyfX(OiXP-;uiB6&Ui4Gf!YNZpmy6r z{QcsX2ZO03RLvELnP#AN#SM@;Xna7+KTz12ytSCma0jH`gTWNsF0x2|Y45>causae ze1->*_KoRXn*`Rc3CRx`y#IfzgvvdEl(nV@!TjWBdpsCS=7P+!n9uOSgTZtqMEs>Y zC=WvNn+Z5SSE`wEGt`3yfSl3&JpFqj&G>PPSYUm2_2ZPDSn)yzkJ_?U*!v9a8K1xUngB+;8Vws)5Aj8R^Vf~unL2W#P zUVTddsEnIX|DQ>uL7qVc)Yk#2_iJELY?#l$0(E<)_x~@Z4fh#XY!>|gY|+5rV6pUN znMEQ)fCuC8rBF4Y9t<|7;64gSoWVvDR4#zb^I#}U1gmA>S_WdK zLHQLbCSyJ4>Ln4EV_y5nj7IPVGpkkF4a~T~#_INNDxr_l&d1(9Rf!j<8$qnotjNF3W z|39s@n9CRe(QgRL|NlG~46a(tWsHI7F?>dmTdoAWNCV;}mgTW9Q|E~l+ z7z~zL%w2g*JO%0D&7jVmmczETC}qeO-PP+Ia}JQ54aBarlD0LkZb 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 [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); }