diff options
122 files changed, 1674 insertions, 463 deletions
diff --git a/docs/conf.py b/docs/conf.py index e09769e5f8..0c9ec74097 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -1,5 +1,3 @@ -# -*- coding: utf-8 -*- -# # QEMU documentation build configuration file, created by # sphinx-quickstart on Thu Jan 31 16:40:14 2019. # diff --git a/docs/interop/firmware.json b/docs/interop/firmware.json index 6bbe2cce0a..ccbfaf828d 100644 --- a/docs/interop/firmware.json +++ b/docs/interop/firmware.json @@ -85,12 +85,14 @@ # # @loongarch64: 64-bit LoongArch. (since: 7.1) # +# @riscv64: 64-bit RISC-V. +# # @x86_64: 64-bit x86. # # Since: 3.0 ## { 'enum' : 'FirmwareArchitecture', - 'data' : [ 'aarch64', 'arm', 'i386', 'loongarch64', 'x86_64' ] } + 'data' : [ 'aarch64', 'arm', 'i386', 'loongarch64', 'riscv64', 'x86_64' ] } ## # @FirmwareTarget: diff --git a/docs/specs/riscv-iommu.rst b/docs/specs/riscv-iommu.rst index 991d376fdc..571a6a6cc9 100644 --- a/docs/specs/riscv-iommu.rst +++ b/docs/specs/riscv-iommu.rst @@ -30,15 +30,15 @@ This will add a RISC-V IOMMU PCI device in the board following any additional PCI parameters (like PCI bus address). The behavior of the RISC-V IOMMU is defined by the spec but its operation is OS dependent. -As of this writing the existing Linux kernel support `linux-v8`_, not yet merged, -does not have support for features like VFIO passthrough. The IOMMU emulation -was tested using a public Ventana Micro Systems kernel repository in -`ventana-linux`_. This kernel is based on `linux-v8`_ with additional patches that -enable features like KVM VFIO passthrough with irqbypass. Until the kernel support -is feature complete feel free to use the kernel available in the Ventana Micro Systems -mirror. - -The current Linux kernel support will use the IOMMU device to create IOMMU groups +Linux kernel iommu support was merged in v6.13. QEMU IOMMU emulation can be +used with mainline kernels for simple IOMMU PCIe support. + +As of v6.17, it does not have support for features like VFIO passthrough. +There is a `VFIO`_ RFC series that is not yet merged. The public Ventana Micro +Systems kernel repository in `ventana-linux`_ can be used for testing the VFIO +functions. + +The v6.13+ Linux kernel support uses the IOMMU device to create IOMMU groups with any eligible cards available in the system, regardless of factors such as the order in which the devices are added in the command line. @@ -49,7 +49,7 @@ IOMMU kernel driver behaves: $ qemu-system-riscv64 \ -M virt,aia=aplic-imsic,aia-guests=5 \ - -device riscv-iommu-pci,addr=1.0,vendor-id=0x1efd,device-id=0xedf1 \ + -device riscv-iommu-pci,addr=1.0 \ -device e1000e,netdev=net1 -netdev user,id=net1,net=192.168.0.0/24 \ -device e1000e,netdev=net2 -netdev user,id=net2,net=192.168.200.0/24 \ (...) @@ -58,21 +58,11 @@ IOMMU kernel driver behaves: -M virt,aia=aplic-imsic,aia-guests=5 \ -device e1000e,netdev=net1 -netdev user,id=net1,net=192.168.0.0/24 \ -device e1000e,netdev=net2 -netdev user,id=net2,net=192.168.200.0/24 \ - -device riscv-iommu-pci,addr=1.0,vendor-id=0x1efd,device-id=0xedf1 \ + -device riscv-iommu-pci,addr=3.0 \ (...) Both will create iommu groups for the two e1000e cards. -Another thing to notice on `linux-v8`_ and `ventana-linux`_ is that the kernel driver -considers an IOMMU identified as a Rivos device, i.e. it uses Rivos vendor ID. To -use the riscv-iommu-pci device with the existing kernel support we need to emulate -a Rivos PCI IOMMU by setting 'vendor-id' and 'device-id': - -.. code-block:: bash - - $ qemu-system-riscv64 -M virt \ - -device riscv-iommu-pci,vendor-id=0x1efd,device-id=0xedf1 (...) - Several options are available to control the capabilities of the device, namely: - "bus": the bus that the IOMMU device uses @@ -84,6 +74,7 @@ Several options are available to control the capabilities of the device, namely: - "g-stage": enable g-stage support - "hpm-counters": number of hardware performance counters available. Maximum value is 31. Default value is 31. Use 0 (zero) to disable HPM support +- "vendor-id"/"device-id": pci device ID. Defaults to 1b36:0014 (Redhat) riscv-iommu-sys device ---------------------- @@ -111,6 +102,6 @@ riscv-iommu options: .. _iommu1.0.0: https://github.com/riscv-non-isa/riscv-iommu/releases/download/v1.0.0/riscv-iommu.pdf -.. _linux-v8: https://lore.kernel.org/linux-riscv/cover.1718388908.git.tjeznach@rivosinc.com/ +.. _VFIO: https://lore.kernel.org/linux-riscv/20241114161845.502027-17-ajones@ventanamicro.com/ .. _ventana-linux: https://github.com/ventanamicro/linux/tree/dev-upstream diff --git a/docs/sphinx-static/theme_overrides.css b/docs/sphinx-static/theme_overrides.css index b225bf706f..f312e9b57e 100644 --- a/docs/sphinx-static/theme_overrides.css +++ b/docs/sphinx-static/theme_overrides.css @@ -1,5 +1,4 @@ -/* -*- coding: utf-8; mode: css -*- - * +/* * Sphinx HTML theme customization: read the doc * Based on Linux Documentation/sphinx-static/theme_overrides.css */ diff --git a/hw/char/sifive_uart.c b/hw/char/sifive_uart.c index 9bc697a67b..e7357d585a 100644 --- a/hw/char/sifive_uart.c +++ b/hw/char/sifive_uart.c @@ -28,23 +28,18 @@ #define TX_INTERRUPT_TRIGGER_DELAY_NS 100 -/* - * Not yet implemented: - * - * Transmit FIFO using "qemu/fifo8.h" - */ - /* Returns the state of the IP (interrupt pending) register */ -static uint64_t sifive_uart_ip(SiFiveUARTState *s) +static uint32_t sifive_uart_ip(SiFiveUARTState *s) { - uint64_t ret = 0; + uint32_t ret = 0; - uint64_t txcnt = SIFIVE_UART_GET_TXCNT(s->txctrl); - uint64_t rxcnt = SIFIVE_UART_GET_RXCNT(s->rxctrl); + uint32_t txcnt = SIFIVE_UART_GET_TXCNT(s->txctrl); + uint32_t rxcnt = SIFIVE_UART_GET_RXCNT(s->rxctrl); - if (txcnt != 0) { + if (fifo8_num_used(&s->tx_fifo) < txcnt) { ret |= SIFIVE_UART_IP_TXWM; } + if (s->rx_fifo_len > rxcnt) { ret |= SIFIVE_UART_IP_RXWM; } @@ -55,15 +50,14 @@ static uint64_t sifive_uart_ip(SiFiveUARTState *s) static void sifive_uart_update_irq(SiFiveUARTState *s) { int cond = 0; - if ((s->ie & SIFIVE_UART_IE_TXWM) || - ((s->ie & SIFIVE_UART_IE_RXWM) && s->rx_fifo_len)) { + uint32_t ip = sifive_uart_ip(s); + + if (((ip & SIFIVE_UART_IP_TXWM) && (s->ie & SIFIVE_UART_IE_TXWM)) || + ((ip & SIFIVE_UART_IP_RXWM) && (s->ie & SIFIVE_UART_IE_RXWM))) { cond = 1; } - if (cond) { - qemu_irq_raise(s->irq); - } else { - qemu_irq_lower(s->irq); - } + + qemu_set_irq(s->irq, cond); } static gboolean sifive_uart_xmit(void *do_not_use, GIOCondition cond, @@ -119,10 +113,12 @@ static void sifive_uart_write_tx_fifo(SiFiveUARTState *s, const uint8_t *buf, if (size > fifo8_num_free(&s->tx_fifo)) { size = fifo8_num_free(&s->tx_fifo); - qemu_log_mask(LOG_GUEST_ERROR, "sifive_uart: TX FIFO overflow"); + qemu_log_mask(LOG_GUEST_ERROR, "sifive_uart: TX FIFO overflow.\n"); } - fifo8_push_all(&s->tx_fifo, buf, size); + if (size > 0) { + fifo8_push_all(&s->tx_fifo, buf, size); + } if (fifo8_is_full(&s->tx_fifo)) { s->txfifo |= SIFIVE_UART_TXFIFO_FULL; diff --git a/hw/char/trace-events b/hw/char/trace-events index 05a33036c1..9e74be2c14 100644 --- a/hw/char/trace-events +++ b/hw/char/trace-events @@ -58,15 +58,15 @@ imx_serial_write(const char *chrname, uint64_t addr, uint64_t value) "%s:[0x%03" imx_serial_put_data(const char *chrname, uint32_t value) "%s: 0x%" PRIx32 # pl011.c -pl011_irq_state(int level) "irq state %d" -pl011_read(uint32_t addr, uint32_t value, const char *regname) "addr 0x%03x value 0x%08x reg %s" -pl011_read_fifo(unsigned rx_fifo_used, size_t rx_fifo_depth) "RX FIFO read, used %u/%zu" -pl011_write(uint32_t addr, uint32_t value, const char *regname) "addr 0x%03x value 0x%08x reg %s" -pl011_can_receive(uint32_t lcr, unsigned rx_fifo_used, size_t rx_fifo_depth, unsigned rx_fifo_available) "LCR 0x%02x, RX FIFO used %u/%zu, can_receive %u chars" -pl011_fifo_rx_put(uint32_t c, unsigned read_count, size_t rx_fifo_depth) "RX FIFO push char [0x%02x] %d/%zu depth used" +pl011_irq_state(bool level) "irq state %d" +pl011_read(uint64_t addr, uint32_t value, const char *regname) "addr 0x%03" PRIx64 " value 0x%08x reg %s" +pl011_read_fifo(unsigned rx_fifo_used, unsigned rx_fifo_depth) "RX FIFO read, used %u/%u" +pl011_write(uint64_t addr, uint32_t value, const char *regname) "addr 0x%03" PRIx64 " value 0x%08x reg %s" +pl011_can_receive(uint32_t lcr, unsigned rx_fifo_used, unsigned rx_fifo_depth, unsigned rx_fifo_available) "LCR 0x%02x, RX FIFO used %u/%u, can_receive %u chars" +pl011_fifo_rx_put(uint32_t c, unsigned read_count, unsigned rx_fifo_depth) "RX FIFO push char [0x%02x] %d/%u depth used" pl011_fifo_rx_full(void) "RX FIFO now full, RXFF set" pl011_baudrate_change(unsigned int baudrate, uint64_t clock, uint32_t ibrd, uint32_t fbrd) "new baudrate %u (clk: %" PRIu64 "hz, ibrd: %" PRIu32 ", fbrd: %" PRIu32 ")" -pl011_receive(int size) "recv %d chars" +pl011_receive(size_t size) "recv %zd chars" # cmsdk-apb-uart.c cmsdk_apb_uart_read(uint64_t offset, uint64_t data, unsigned size) "CMSDK APB UART read: offset 0x%" PRIx64 " data 0x%" PRIx64 " size %u" diff --git a/hw/cxl/cxl-host.c b/hw/cxl/cxl-host.c index 5c2ce25a19..0d891c651d 100644 --- a/hw/cxl/cxl-host.c +++ b/hw/cxl/cxl-host.c @@ -72,6 +72,7 @@ static void cxl_fixed_memory_window_config(CXLFixedMemoryWindowOptions *object, static int cxl_fmws_link(Object *obj, void *opaque) { + Error **errp = opaque; struct CXLFixedWindow *fw; int i; @@ -87,9 +88,9 @@ static int cxl_fmws_link(Object *obj, void *opaque) o = object_resolve_path_type(fw->targets[i], TYPE_PXB_CXL_DEV, &ambig); if (!o) { - error_setg(&error_fatal, "Could not resolve CXLFM target %s", + error_setg(errp, "Could not resolve CXLFM target %s", fw->targets[i]); - return 1; + return -1; } fw->target_hbs[i] = PXB_CXL_DEV(o); } @@ -99,7 +100,7 @@ static int cxl_fmws_link(Object *obj, void *opaque) void cxl_fmws_link_targets(Error **errp) { /* Order doesn't matter for this, so no need to build list */ - object_child_foreach_recursive(object_get_root(), cxl_fmws_link, NULL); + object_child_foreach_recursive(object_get_root(), cxl_fmws_link, errp); } static bool cxl_hdm_find_target(uint32_t *cache_mem, hwaddr addr, diff --git a/hw/display/virtio-gpu.c b/hw/display/virtio-gpu.c index 0a1a625b0e..de35902213 100644 --- a/hw/display/virtio-gpu.c +++ b/hw/display/virtio-gpu.c @@ -242,6 +242,7 @@ static uint32_t calc_image_hostmem(pixman_format_code_t pformat, static void virtio_gpu_resource_create_2d(VirtIOGPU *g, struct virtio_gpu_ctrl_command *cmd) { + Error *err = NULL; pixman_format_code_t pformat; struct virtio_gpu_simple_resource *res; struct virtio_gpu_resource_create_2d c2d; @@ -293,7 +294,8 @@ static void virtio_gpu_resource_create_2d(VirtIOGPU *g, c2d.width, c2d.height, c2d.height ? res->hostmem / c2d.height : 0, - &error_warn)) { + &err)) { + warn_report_err(err); goto end; } } @@ -1282,6 +1284,7 @@ static int virtio_gpu_load(QEMUFile *f, void *opaque, size_t size, const VMStateField *field) { VirtIOGPU *g = opaque; + Error *err = NULL; struct virtio_gpu_simple_resource *res; uint32_t resource_id, pformat; int i; @@ -1317,7 +1320,8 @@ static int virtio_gpu_load(QEMUFile *f, void *opaque, size_t size, res->width, res->height, res->height ? res->hostmem / res->height : 0, - &error_warn)) { + &err)) { + warn_report_err(err); g_free(res); return -EINVAL; } diff --git a/hw/intc/riscv_aclint.c b/hw/intc/riscv_aclint.c index 4623cfa029..9f4c36e965 100644 --- a/hw/intc/riscv_aclint.c +++ b/hw/intc/riscv_aclint.c @@ -323,12 +323,15 @@ static void riscv_aclint_mtimer_reset_enter(Object *obj, ResetType type) static const VMStateDescription vmstate_riscv_mtimer = { .name = "riscv_mtimer", - .version_id = 1, - .minimum_version_id = 1, + .version_id = 3, + .minimum_version_id = 3, .fields = (const VMStateField[]) { + VMSTATE_UINT64(time_delta, RISCVAclintMTimerState), VMSTATE_VARRAY_UINT32(timecmp, RISCVAclintMTimerState, num_harts, 0, vmstate_info_uint64, uint64_t), + VMSTATE_TIMER_PTR_VARRAY(timers, RISCVAclintMTimerState, + num_harts), VMSTATE_END_OF_LIST() } }; diff --git a/hw/misc/ivshmem-flat.c b/hw/misc/ivshmem-flat.c index e83e6c6ee9..27ee8c9218 100644 --- a/hw/misc/ivshmem-flat.c +++ b/hw/misc/ivshmem-flat.c @@ -138,6 +138,8 @@ static void ivshmem_flat_remove_peer(IvshmemFTState *s, uint16_t peer_id) static void ivshmem_flat_add_vector(IvshmemFTState *s, IvshmemPeer *peer, int vector_fd) { + Error *err = NULL; + if (peer->vector_counter >= IVSHMEM_MAX_VECTOR_NUM) { trace_ivshmem_flat_add_vector_failure(peer->vector_counter, vector_fd, peer->id); @@ -154,8 +156,10 @@ static void ivshmem_flat_add_vector(IvshmemFTState *s, IvshmemPeer *peer, * peer. */ peer->vector[peer->vector_counter].id = peer->vector_counter; - /* WARNING: qemu_socket_set_nonblock() return code ignored */ - qemu_set_blocking(vector_fd, false, &error_warn); + if (!qemu_set_blocking(vector_fd, false, &err)) { + /* FIXME handle the error */ + warn_report_err(err); + } event_notifier_init_fd(&peer->vector[peer->vector_counter].event_notifier, vector_fd); diff --git a/hw/net/virtio-net.c b/hw/net/virtio-net.c index 6b5b5dace3..7848e26278 100644 --- a/hw/net/virtio-net.c +++ b/hw/net/virtio-net.c @@ -1289,6 +1289,8 @@ exit: static bool virtio_net_load_ebpf(VirtIONet *n, Error **errp) { + Error *err = NULL; + if (!virtio_net_attach_ebpf_to_backend(n->nic, -1)) { return true; } @@ -1306,7 +1308,11 @@ static bool virtio_net_load_ebpf(VirtIONet *n, Error **errp) return virtio_net_load_ebpf_fds(n, errp); } - ebpf_rss_load(&n->ebpf_rss, &error_warn); + ebpf_rss_load(&n->ebpf_rss, &err); + /* Beware, ebpf_rss_load() can return false with @err unset */ + if (err) { + warn_report_err(err); + } return true; } diff --git a/hw/remote/vfio-user-obj.c b/hw/remote/vfio-user-obj.c index ea6165ebdc..216b4876e2 100644 --- a/hw/remote/vfio-user-obj.c +++ b/hw/remote/vfio-user-obj.c @@ -75,12 +75,17 @@ OBJECT_DECLARE_TYPE(VfuObject, VfuObjectClass, VFU_OBJECT) */ #define VFU_OBJECT_ERROR(o, fmt, ...) \ { \ + error_report((fmt), ## __VA_ARGS__); \ if (vfu_object_auto_shutdown()) { \ - error_setg(&error_abort, (fmt), ## __VA_ARGS__); \ - } else { \ - error_report((fmt), ## __VA_ARGS__); \ + /* \ + * FIXME This looks inappropriate. The error is serious \ + * enough programming error to warrant aborting the process \ + * when auto-shutdown is enabled, yet harmless enough to \ + * permit carrying on when it's disabled. Makes no sense. \ + */ \ + abort(); \ } \ - } \ + } struct VfuObjectClass { ObjectClass parent_class; diff --git a/hw/riscv/riscv-iommu.c b/hw/riscv/riscv-iommu.c index 96a7fbdefc..b33c7fe325 100644 --- a/hw/riscv/riscv-iommu.c +++ b/hw/riscv/riscv-iommu.c @@ -558,6 +558,7 @@ static MemTxResult riscv_iommu_msi_write(RISCVIOMMUState *s, MemTxResult res; dma_addr_t addr; uint64_t intn; + size_t offset; uint32_t n190; uint64_t pte[2]; int fault_type = RISCV_IOMMU_FQ_TTYPE_UADDR_WR; @@ -565,16 +566,18 @@ static MemTxResult riscv_iommu_msi_write(RISCVIOMMUState *s, /* Interrupt File Number */ intn = riscv_iommu_pext_u64(PPN_DOWN(gpa), ctx->msi_addr_mask); - if (intn >= 256) { + offset = intn * sizeof(pte); + + /* fetch MSI PTE */ + addr = PPN_PHYS(get_field(ctx->msiptp, RISCV_IOMMU_DC_MSIPTP_PPN)); + if (addr & offset) { /* Interrupt file number out of range */ res = MEMTX_ACCESS_ERROR; cause = RISCV_IOMMU_FQ_CAUSE_MSI_LOAD_FAULT; goto err; } - /* fetch MSI PTE */ - addr = PPN_PHYS(get_field(ctx->msiptp, RISCV_IOMMU_DC_MSIPTP_PPN)); - addr = addr | (intn * sizeof(pte)); + addr |= offset; res = dma_memory_read(s->target_as, addr, &pte, sizeof(pte), MEMTXATTRS_UNSPECIFIED); if (res != MEMTX_OK) { @@ -866,6 +869,145 @@ static bool riscv_iommu_validate_process_ctx(RISCVIOMMUState *s, return true; } +/** + * pdt_memory_read: PDT wrapper of dma_memory_read. + * + * @s: IOMMU Device State + * @ctx: Device Translation Context with devid and pasid set + * @addr: address within that address space + * @buf: buffer with the data transferred + * @len: length of the data transferred + * @attrs: memory transaction attributes + */ +static MemTxResult pdt_memory_read(RISCVIOMMUState *s, + RISCVIOMMUContext *ctx, + dma_addr_t addr, + void *buf, dma_addr_t len, + MemTxAttrs attrs) +{ + uint64_t gatp_mode, pte; + struct { + unsigned char step; + unsigned char levels; + unsigned char ptidxbits; + unsigned char ptesize; + } sc; + MemTxResult ret; + dma_addr_t base = addr; + + /* G stages translation mode */ + gatp_mode = get_field(ctx->gatp, RISCV_IOMMU_ATP_MODE_FIELD); + if (gatp_mode == RISCV_IOMMU_DC_IOHGATP_MODE_BARE) { + goto out; + } + + /* G stages translation tables root pointer */ + base = PPN_PHYS(get_field(ctx->gatp, RISCV_IOMMU_ATP_PPN_FIELD)); + + /* Start at step 0 */ + sc.step = 0; + + if (s->fctl & RISCV_IOMMU_FCTL_GXL) { + /* 32bit mode for GXL == 1 */ + switch (gatp_mode) { + case RISCV_IOMMU_DC_IOHGATP_MODE_SV32X4: + if (!(s->cap & RISCV_IOMMU_CAP_SV32X4)) { + return MEMTX_ACCESS_ERROR; + } + sc.levels = 2; + sc.ptidxbits = 10; + sc.ptesize = 4; + break; + default: + return MEMTX_ACCESS_ERROR; + } + } else { + /* 64bit mode for GXL == 0 */ + switch (gatp_mode) { + case RISCV_IOMMU_DC_IOHGATP_MODE_SV39X4: + if (!(s->cap & RISCV_IOMMU_CAP_SV39X4)) { + return MEMTX_ACCESS_ERROR; + } + sc.levels = 3; + sc.ptidxbits = 9; + sc.ptesize = 8; + break; + case RISCV_IOMMU_DC_IOHGATP_MODE_SV48X4: + if (!(s->cap & RISCV_IOMMU_CAP_SV48X4)) { + return MEMTX_ACCESS_ERROR; + } + sc.levels = 4; + sc.ptidxbits = 9; + sc.ptesize = 8; + break; + case RISCV_IOMMU_DC_IOHGATP_MODE_SV57X4: + if (!(s->cap & RISCV_IOMMU_CAP_SV57X4)) { + return MEMTX_ACCESS_ERROR; + } + sc.levels = 5; + sc.ptidxbits = 9; + sc.ptesize = 8; + break; + default: + return MEMTX_ACCESS_ERROR; + } + } + + do { + const unsigned va_bits = (sc.step ? 0 : 2) + sc.ptidxbits; + const unsigned va_skip = TARGET_PAGE_BITS + sc.ptidxbits * + (sc.levels - 1 - sc.step); + const unsigned idx = (addr >> va_skip) & ((1 << va_bits) - 1); + const dma_addr_t pte_addr = base + idx * sc.ptesize; + + /* Address range check before first level lookup */ + if (!sc.step) { + const uint64_t va_mask = (1ULL << (va_skip + va_bits)) - 1; + if ((addr & va_mask) != addr) { + return MEMTX_ACCESS_ERROR; + } + } + + /* Read page table entry */ + if (sc.ptesize == 4) { + uint32_t pte32 = 0; + ret = ldl_le_dma(s->target_as, pte_addr, &pte32, attrs); + pte = pte32; + } else { + ret = ldq_le_dma(s->target_as, pte_addr, &pte, attrs); + } + if (ret != MEMTX_OK) { + return ret; + } + + sc.step++; + hwaddr ppn = pte >> PTE_PPN_SHIFT; + + if (!(pte & PTE_V)) { + return MEMTX_ACCESS_ERROR; /* Invalid PTE */ + } else if (!(pte & (PTE_R | PTE_W | PTE_X))) { + base = PPN_PHYS(ppn); /* Inner PTE, continue walking */ + } else if ((pte & (PTE_R | PTE_W | PTE_X)) == PTE_W) { + return MEMTX_ACCESS_ERROR; /* Reserved leaf PTE flags: PTE_W */ + } else if ((pte & (PTE_R | PTE_W | PTE_X)) == (PTE_W | PTE_X)) { + return MEMTX_ACCESS_ERROR; /* Reserved leaf PTE flags: PTE_W + PTE_X */ + } else if (ppn & ((1ULL << (va_skip - TARGET_PAGE_BITS)) - 1)) { + return MEMTX_ACCESS_ERROR; /* Misaligned PPN */ + } else { + /* Leaf PTE, translation completed. */ + base = PPN_PHYS(ppn) | (addr & ((1ULL << va_skip) - 1)); + break; + } + + if (sc.step == sc.levels) { + return MEMTX_ACCESS_ERROR; /* Can't find leaf PTE */ + } + } while (1); + +out: + return dma_memory_read(s->target_as, base, buf, len, attrs); +} + /* * RISC-V IOMMU Device Context Loopkup - Device Directory Tree Walk * @@ -1038,7 +1180,7 @@ static int riscv_iommu_ctx_fetch(RISCVIOMMUState *s, RISCVIOMMUContext *ctx) */ const int split = depth * 9 + 8; addr |= ((ctx->process_id >> split) << 3) & ~TARGET_PAGE_MASK; - if (dma_memory_read(s->target_as, addr, &de, sizeof(de), + if (pdt_memory_read(s, ctx, addr, &de, sizeof(de), MEMTXATTRS_UNSPECIFIED) != MEMTX_OK) { return RISCV_IOMMU_FQ_CAUSE_PDT_LOAD_FAULT; } @@ -1053,7 +1195,7 @@ static int riscv_iommu_ctx_fetch(RISCVIOMMUState *s, RISCVIOMMUContext *ctx) /* Leaf entry in PDT */ addr |= (ctx->process_id << 4) & ~TARGET_PAGE_MASK; - if (dma_memory_read(s->target_as, addr, &dc.ta, sizeof(uint64_t) * 2, + if (pdt_memory_read(s, ctx, addr, &dc.ta, sizeof(uint64_t) * 2, MEMTXATTRS_UNSPECIFIED) != MEMTX_OK) { return RISCV_IOMMU_FQ_CAUSE_PDT_LOAD_FAULT; } diff --git a/include/exec/gdbstub.h b/include/exec/gdbstub.h index a16c0051ce..bd7182c4d3 100644 --- a/include/exec/gdbstub.h +++ b/include/exec/gdbstub.h @@ -55,9 +55,6 @@ void gdb_unregister_coprocessor_all(CPUState *cpu); * system emulation you can use a full chardev spec for your gdbserver * port. * - * The error handle should be either &error_fatal (for start-up) or - * &error_warn (for QMP/HMP initiated sessions). - * * Returns true when server successfully started. */ bool gdbserver_start(const char *port_or_device, Error **errp); diff --git a/include/hw/intc/riscv_aclint.h b/include/hw/intc/riscv_aclint.h index 693415eb6d..4b7406eec0 100644 --- a/include/hw/intc/riscv_aclint.h +++ b/include/hw/intc/riscv_aclint.h @@ -80,4 +80,8 @@ enum { RISCV_ACLINT_SWI_SIZE = 0x4000 }; +#define VMSTATE_TIMER_PTR_VARRAY(_f, _s, _f_n) \ +VMSTATE_VARRAY_OF_POINTER_UINT32(_f, _s, _f_n, 0, vmstate_info_timer, \ + QEMUTimer *) + #endif diff --git a/include/migration/vmstate.h b/include/migration/vmstate.h index 1ff7bd9ac4..1cfddf31b5 100644 --- a/include/migration/vmstate.h +++ b/include/migration/vmstate.h @@ -522,6 +522,16 @@ extern const VMStateInfo vmstate_info_qlist; .offset = vmstate_offset_array(_s, _f, _type*, _n), \ } +#define VMSTATE_VARRAY_OF_POINTER_UINT32(_field, _state, _field_num, _version, _info, _type) { \ + .name = (stringify(_field)), \ + .version_id = (_version), \ + .num_offset = vmstate_offset_value(_state, _field_num, uint32_t), \ + .info = &(_info), \ + .size = sizeof(_type), \ + .flags = VMS_VARRAY_UINT32 | VMS_ARRAY_OF_POINTER | VMS_POINTER, \ + .offset = vmstate_offset_pointer(_state, _field, _type), \ +} + #define VMSTATE_STRUCT_SUB_ARRAY(_field, _state, _start, _num, _version, _vmsd, _type) { \ .name = (stringify(_field)), \ .version_id = (_version), \ diff --git a/include/qapi/error.h b/include/qapi/error.h index 41e3816380..b16c6303f8 100644 --- a/include/qapi/error.h +++ b/include/qapi/error.h @@ -534,12 +534,6 @@ static inline void error_propagator_cleanup(ErrorPropagator *prop) G_DEFINE_AUTO_CLEANUP_CLEAR_FUNC(ErrorPropagator, error_propagator_cleanup); /* - * Special error destination to warn on error. - * See error_setg() and error_propagate() for details. - */ -extern Error *error_warn; - -/* * Special error destination to abort on error. * See error_setg() and error_propagate() for details. */ diff --git a/include/qemu/log-for-trace.h b/include/qemu/log-for-trace.h index d47c9cd446..f3a8791f1d 100644 --- a/include/qemu/log-for-trace.h +++ b/include/qemu/log-for-trace.h @@ -19,9 +19,9 @@ #define QEMU_LOG_FOR_TRACE_H /* Private global variable, don't use */ -extern int qemu_loglevel; +extern unsigned qemu_loglevel; -#define LOG_TRACE (1 << 15) +#define LOG_TRACE (1u << 15) /* Returns true if a bit is set in the current loglevel mask */ static inline bool qemu_loglevel_mask(int mask) diff --git a/include/qemu/log.h b/include/qemu/log.h index aae72985f0..7effba4da4 100644 --- a/include/qemu/log.h +++ b/include/qemu/log.h @@ -14,30 +14,30 @@ bool qemu_log_enabled(void); /* Returns true if qemu_log() will write somewhere other than stderr. */ bool qemu_log_separate(void); -#define CPU_LOG_TB_OUT_ASM (1 << 0) -#define CPU_LOG_TB_IN_ASM (1 << 1) -#define CPU_LOG_TB_OP (1 << 2) -#define CPU_LOG_TB_OP_OPT (1 << 3) -#define CPU_LOG_INT (1 << 4) -#define CPU_LOG_EXEC (1 << 5) -#define CPU_LOG_PCALL (1 << 6) -#define CPU_LOG_TB_CPU (1 << 8) -#define CPU_LOG_RESET (1 << 9) -#define LOG_UNIMP (1 << 10) -#define LOG_GUEST_ERROR (1 << 11) -#define CPU_LOG_MMU (1 << 12) -#define CPU_LOG_TB_NOCHAIN (1 << 13) -#define CPU_LOG_PAGE (1 << 14) +#define CPU_LOG_TB_OUT_ASM (1u << 0) +#define CPU_LOG_TB_IN_ASM (1u << 1) +#define CPU_LOG_TB_OP (1u << 2) +#define CPU_LOG_TB_OP_OPT (1u << 3) +#define CPU_LOG_INT (1u << 4) +#define CPU_LOG_EXEC (1u << 5) +#define CPU_LOG_PCALL (1u << 6) +#define CPU_LOG_TB_CPU (1u << 8) +#define CPU_LOG_RESET (1u << 9) +#define LOG_UNIMP (1u << 10) +#define LOG_GUEST_ERROR (1u << 11) +#define CPU_LOG_MMU (1u << 12) +#define CPU_LOG_TB_NOCHAIN (1u << 13) +#define CPU_LOG_PAGE (1u << 14) /* LOG_TRACE (1 << 15) is defined in log-for-trace.h */ -#define CPU_LOG_TB_OP_IND (1 << 16) -#define CPU_LOG_TB_FPU (1 << 17) -#define CPU_LOG_PLUGIN (1 << 18) +#define CPU_LOG_TB_OP_IND (1u << 16) +#define CPU_LOG_TB_FPU (1u << 17) +#define CPU_LOG_PLUGIN (1u << 18) /* LOG_STRACE is used for user-mode strace logging. */ -#define LOG_STRACE (1 << 19) -#define LOG_PER_THREAD (1 << 20) -#define CPU_LOG_TB_VPU (1 << 21) -#define LOG_TB_OP_PLUGIN (1 << 22) -#define LOG_INVALID_MEM (1 << 23) +#define LOG_STRACE (1u << 19) +#define LOG_PER_THREAD (1u << 20) +#define CPU_LOG_TB_VPU (1u << 21) +#define LOG_TB_OP_PLUGIN (1u << 22) +#define LOG_INVALID_MEM (1u << 23) /* Lock/unlock output. */ diff --git a/include/qemu/osdep.h b/include/qemu/osdep.h index 1b38cb7e45..6de6c0c4e5 100644 --- a/include/qemu/osdep.h +++ b/include/qemu/osdep.h @@ -561,7 +561,7 @@ int madvise(char *, size_t, int); #if defined(__linux__) && \ (defined(__x86_64__) || defined(__arm__) || defined(__aarch64__) \ - || defined(__powerpc64__)) + || defined(__powerpc64__) || defined(__riscv)) /* Use 2 MiB alignment so transparent hugepages can be used by KVM. Valgrind does not support alignments larger than 1 MiB, therefore we need special code which handles running on Valgrind. */ diff --git a/include/system/os-win32.h b/include/system/os-win32.h index 3aa6cee4c2..22d72babdf 100644 --- a/include/system/os-win32.h +++ b/include/system/os-win32.h @@ -168,11 +168,14 @@ static inline void qemu_funlockfile(FILE *f) #endif } -/* Helper for WSAEventSelect, to report errors */ +/* Helpers for WSAEventSelect() */ bool qemu_socket_select(int sockfd, WSAEVENT hEventObject, long lNetworkEvents, Error **errp); +void qemu_socket_select_nofail(int sockfd, WSAEVENT hEventObject, + long lNetworkEvents); bool qemu_socket_unselect(int sockfd, Error **errp); +void qemu_socket_unselect_nofail(int sockfd); /* We wrap all the sockets functions so that we can set errno based on * WSAGetLastError(), and use file-descriptors instead of SOCKET. diff --git a/io/channel-socket.c b/io/channel-socket.c index e53d9ac76f..712b793eaf 100644 --- a/io/channel-socket.c +++ b/io/channel-socket.c @@ -454,7 +454,7 @@ static void qio_channel_socket_finalize(Object *obj) } } #ifdef WIN32 - qemu_socket_unselect(ioc->fd, NULL); + qemu_socket_unselect_nofail(ioc->fd); #endif close(ioc->fd); ioc->fd = -1; @@ -929,7 +929,7 @@ qio_channel_socket_close(QIOChannel *ioc, if (sioc->fd != -1) { #ifdef WIN32 - qemu_socket_unselect(sioc->fd, NULL); + qemu_socket_unselect_nofail(sioc->fd); #endif if (qio_channel_has_feature(ioc, QIO_CHANNEL_FEATURE_LISTEN)) { socket_listen_cleanup(sioc->fd, errp); diff --git a/io/channel-watch.c b/io/channel-watch.c index 64b486e378..018648b36b 100644 --- a/io/channel-watch.c +++ b/io/channel-watch.c @@ -281,9 +281,9 @@ GSource *qio_channel_create_socket_watch(QIOChannel *ioc, GSource *source; QIOChannelSocketSource *ssource; - qemu_socket_select(sockfd, ioc->event, - FD_READ | FD_ACCEPT | FD_CLOSE | - FD_CONNECT | FD_WRITE | FD_OOB, NULL); + qemu_socket_select_nofail(sockfd, ioc->event, + FD_READ | FD_ACCEPT | FD_CLOSE | + FD_CONNECT | FD_WRITE | FD_OOB); source = g_source_new(&qio_channel_socket_source_funcs, sizeof(QIOChannelSocketSource)); diff --git a/linux-user/syscall.c b/linux-user/syscall.c index 1a5f2a03f9..d78b2029fa 100644 --- a/linux-user/syscall.c +++ b/linux-user/syscall.c @@ -9023,6 +9023,29 @@ static int do_getdents64(abi_long dirfd, abi_long arg2, abi_long count) #define RISCV_HWPROBE_EXT_ZTSO (1ULL << 33) #define RISCV_HWPROBE_EXT_ZACAS (1ULL << 34) #define RISCV_HWPROBE_EXT_ZICOND (1ULL << 35) +#define RISCV_HWPROBE_EXT_ZIHINTPAUSE (1ULL << 36) +#define RISCV_HWPROBE_EXT_ZVE32X (1ULL << 37) +#define RISCV_HWPROBE_EXT_ZVE32F (1ULL << 38) +#define RISCV_HWPROBE_EXT_ZVE64X (1ULL << 39) +#define RISCV_HWPROBE_EXT_ZVE64F (1ULL << 40) +#define RISCV_HWPROBE_EXT_ZVE64D (1ULL << 41) +#define RISCV_HWPROBE_EXT_ZIMOP (1ULL << 42) +#define RISCV_HWPROBE_EXT_ZCA (1ULL << 43) +#define RISCV_HWPROBE_EXT_ZCB (1ULL << 44) +#define RISCV_HWPROBE_EXT_ZCD (1ULL << 45) +#define RISCV_HWPROBE_EXT_ZCF (1ULL << 46) +#define RISCV_HWPROBE_EXT_ZCMOP (1ULL << 47) +#define RISCV_HWPROBE_EXT_ZAWRS (1ULL << 48) +#define RISCV_HWPROBE_EXT_SUPM (1ULL << 49) +#define RISCV_HWPROBE_EXT_ZICNTR (1ULL << 50) +#define RISCV_HWPROBE_EXT_ZIHPM (1ULL << 51) +#define RISCV_HWPROBE_EXT_ZFBFMIN (1ULL << 52) +#define RISCV_HWPROBE_EXT_ZVFBFMIN (1ULL << 53) +#define RISCV_HWPROBE_EXT_ZVFBFWMA (1ULL << 54) +#define RISCV_HWPROBE_EXT_ZICBOM (1ULL << 55) +#define RISCV_HWPROBE_EXT_ZAAMO (1ULL << 56) +#define RISCV_HWPROBE_EXT_ZALRSC (1ULL << 57) +#define RISCV_HWPROBE_EXT_ZABHA (1ULL << 58) #define RISCV_HWPROBE_KEY_CPUPERF_0 5 #define RISCV_HWPROBE_MISALIGNED_UNKNOWN (0 << 0) @@ -9033,6 +9056,22 @@ static int do_getdents64(abi_long dirfd, abi_long arg2, abi_long count) #define RISCV_HWPROBE_MISALIGNED_MASK (7 << 0) #define RISCV_HWPROBE_KEY_ZICBOZ_BLOCK_SIZE 6 +#define RISCV_HWPROBE_KEY_HIGHEST_VIRT_ADDRESS 7 +#define RISCV_HWPROBE_KEY_TIME_CSR_FREQ 8 +#define RISCV_HWPROBE_KEY_MISALIGNED_SCALAR_PERF 9 +#define RISCV_HWPROBE_MISALIGNED_SCALAR_UNKNOWN 0 +#define RISCV_HWPROBE_MISALIGNED_SCALAR_EMULATED 1 +#define RISCV_HWPROBE_MISALIGNED_SCALAR_SLOW 2 +#define RISCV_HWPROBE_MISALIGNED_SCALAR_FAST 3 +#define RISCV_HWPROBE_MISALIGNED_SCALAR_UNSUPPORTED 4 +#define RISCV_HWPROBE_KEY_MISALIGNED_VECTOR_PERF 10 +#define RISCV_HWPROBE_MISALIGNED_VECTOR_UNKNOWN 0 +#define RISCV_HWPROBE_MISALIGNED_VECTOR_SLOW 2 +#define RISCV_HWPROBE_MISALIGNED_VECTOR_FAST 3 +#define RISCV_HWPROBE_MISALIGNED_VECTOR_UNSUPPORTED 4 +#define RISCV_HWPROBE_KEY_VENDOR_EXT_THEAD_0 11 +#define RISCV_HWPROBE_KEY_ZICBOM_BLOCK_SIZE 12 +#define RISCV_HWPROBE_KEY_VENDOR_EXT_SIFIVE_0 13 struct riscv_hwprobe { abi_llong key; @@ -9141,6 +9180,52 @@ static void risc_hwprobe_fill_pairs(CPURISCVState *env, RISCV_HWPROBE_EXT_ZACAS : 0; value |= cfg->ext_zicond ? RISCV_HWPROBE_EXT_ZICOND : 0; + value |= cfg->ext_zihintpause ? + RISCV_HWPROBE_EXT_ZIHINTPAUSE : 0; + value |= cfg->ext_zve32x ? + RISCV_HWPROBE_EXT_ZVE32X : 0; + value |= cfg->ext_zve32f ? + RISCV_HWPROBE_EXT_ZVE32F : 0; + value |= cfg->ext_zve64x ? + RISCV_HWPROBE_EXT_ZVE64X : 0; + value |= cfg->ext_zve64f ? + RISCV_HWPROBE_EXT_ZVE64F : 0; + value |= cfg->ext_zve64d ? + RISCV_HWPROBE_EXT_ZVE64D : 0; + value |= cfg->ext_zimop ? + RISCV_HWPROBE_EXT_ZIMOP : 0; + value |= cfg->ext_zca ? + RISCV_HWPROBE_EXT_ZCA : 0; + value |= cfg->ext_zcb ? + RISCV_HWPROBE_EXT_ZCB : 0; + value |= cfg->ext_zcd ? + RISCV_HWPROBE_EXT_ZCD : 0; + value |= cfg->ext_zcf ? + RISCV_HWPROBE_EXT_ZCF : 0; + value |= cfg->ext_zcmop ? + RISCV_HWPROBE_EXT_ZCMOP : 0; + value |= cfg->ext_zawrs ? + RISCV_HWPROBE_EXT_ZAWRS : 0; + value |= cfg->ext_supm ? + RISCV_HWPROBE_EXT_SUPM : 0; + value |= cfg->ext_zicntr ? + RISCV_HWPROBE_EXT_ZICNTR : 0; + value |= cfg->ext_zihpm ? + RISCV_HWPROBE_EXT_ZIHPM : 0; + value |= cfg->ext_zfbfmin ? + RISCV_HWPROBE_EXT_ZFBFMIN : 0; + value |= cfg->ext_zvfbfmin ? + RISCV_HWPROBE_EXT_ZVFBFMIN : 0; + value |= cfg->ext_zvfbfwma ? + RISCV_HWPROBE_EXT_ZVFBFWMA : 0; + value |= cfg->ext_zicbom ? + RISCV_HWPROBE_EXT_ZICBOM : 0; + value |= cfg->ext_zaamo ? + RISCV_HWPROBE_EXT_ZAAMO : 0; + value |= cfg->ext_zalrsc ? + RISCV_HWPROBE_EXT_ZALRSC : 0; + value |= cfg->ext_zabha ? + RISCV_HWPROBE_EXT_ZABHA : 0; __put_user(value, &pair->value); break; case RISCV_HWPROBE_KEY_CPUPERF_0: @@ -9150,6 +9235,10 @@ static void risc_hwprobe_fill_pairs(CPURISCVState *env, value = cfg->ext_zicboz ? cfg->cboz_blocksize : 0; __put_user(value, &pair->value); break; + case RISCV_HWPROBE_KEY_ZICBOM_BLOCK_SIZE: + value = cfg->ext_zicbom ? cfg->cbom_blocksize : 0; + __put_user(value, &pair->value); + break; default: __put_user(-1, &pair->key); break; diff --git a/migration/cpr.c b/migration/cpr.c index 42ad0b0d50..9848a21ea6 100644 --- a/migration/cpr.c +++ b/migration/cpr.c @@ -7,6 +7,7 @@ #include "qemu/osdep.h" #include "qapi/error.h" +#include "qemu/error-report.h" #include "hw/vfio/vfio-device.h" #include "migration/cpr.h" #include "migration/misc.h" @@ -100,10 +101,10 @@ void cpr_resave_fd(const char *name, int id, int fd) if (old_fd < 0) { cpr_save_fd(name, id, fd); } else if (old_fd != fd) { - error_setg(&error_fatal, - "internal error: cpr fd '%s' id %d value %d " - "already saved with a different value %d", - name, id, fd, old_fd); + error_report("internal error: cpr fd '%s' id %d value %d " + "already saved with a different value %d", + name, id, fd, old_fd); + g_assert_not_reached(); } } diff --git a/monitor/hmp-cmds.c b/monitor/hmp-cmds.c index 74a0f56566..33a88ce205 100644 --- a/monitor/hmp-cmds.c +++ b/monitor/hmp-cmds.c @@ -280,14 +280,15 @@ void hmp_log(Monitor *mon, const QDict *qdict) void hmp_gdbserver(Monitor *mon, const QDict *qdict) { + Error *err = NULL; const char *device = qdict_get_try_str(qdict, "device"); + if (!device) { device = "tcp::" DEFAULT_GDBSTUB_PORT; } - if (!gdbserver_start(device, &error_warn)) { - monitor_printf(mon, "Could not open gdbserver on device '%s'\n", - device); + if (!gdbserver_start(device, &err)) { + error_report_err(err); } else if (strcmp(device, "none") == 0) { monitor_printf(mon, "Disabled gdbserver\n"); } else { diff --git a/net/slirp.c b/net/slirp.c index 9657e86a84..0a1c2a5eac 100644 --- a/net/slirp.c +++ b/net/slirp.c @@ -258,11 +258,13 @@ static void net_slirp_register_poll_sock(slirp_os_socket fd, void *opaque) { #ifdef WIN32 AioContext *ctxt = qemu_get_aio_context(); + g_autofree char *msg = NULL; if (WSAEventSelect(fd, event_notifier_get_handle(&ctxt->notifier), FD_READ | FD_ACCEPT | FD_CLOSE | FD_CONNECT | FD_WRITE | FD_OOB) != 0) { - error_setg_win32(&error_warn, WSAGetLastError(), "failed to WSAEventSelect()"); + msg = g_win32_error_message(WSAGetLastError()); + warn_report("failed to WSAEventSelect(): %s", msg); } #endif } @@ -270,8 +272,11 @@ static void net_slirp_register_poll_sock(slirp_os_socket fd, void *opaque) static void net_slirp_unregister_poll_sock(slirp_os_socket fd, void *opaque) { #ifdef WIN32 + g_autofree char *msg = NULL; + if (WSAEventSelect(fd, NULL, 0) != 0) { - error_setg_win32(&error_warn, WSAGetLastError(), "failed to WSAEventSelect()"); + msg = g_win32_error_message(WSAGetLastError()); + warn_report("failed to WSAEventSelect(): %s", msg); } #endif } diff --git a/pc-bios/opensbi-riscv32-generic-fw_dynamic.bin b/pc-bios/opensbi-riscv32-generic-fw_dynamic.bin index b2e740010b..02be3a72a8 100644 --- a/pc-bios/opensbi-riscv32-generic-fw_dynamic.bin +++ b/pc-bios/opensbi-riscv32-generic-fw_dynamic.bin Binary files differdiff --git a/pc-bios/opensbi-riscv64-generic-fw_dynamic.bin b/pc-bios/opensbi-riscv64-generic-fw_dynamic.bin index 018b4731a7..cce35c65c2 100644 --- a/pc-bios/opensbi-riscv64-generic-fw_dynamic.bin +++ b/pc-bios/opensbi-riscv64-generic-fw_dynamic.bin Binary files differdiff --git a/roms/opensbi b/roms/opensbi -Subproject 43cace6c3671e5172d0df0a8963e552bb04b7b2 +Subproject a32a91069119e7a5aa31e6bc51d5e00860be3d8 diff --git a/rust/Cargo.lock b/rust/Cargo.lock index 8315f98c46..444ef516a7 100644 --- a/rust/Cargo.lock +++ b/rust/Cargo.lock @@ -159,6 +159,7 @@ dependencies = [ "migration", "qom", "system", + "trace", "util", ] @@ -259,6 +260,13 @@ dependencies = [ ] [[package]] +name = "trace" +version = "0.1.0" +dependencies = [ + "libc", +] + +[[package]] name = "unicode-ident" version = "1.0.12" source = "registry+https://github.com/rust-lang/crates.io-index" diff --git a/rust/Cargo.toml b/rust/Cargo.toml index d8183c614d..f372d7dbf7 100644 --- a/rust/Cargo.toml +++ b/rust/Cargo.toml @@ -11,6 +11,7 @@ members = [ "hw/core", "hw/char/pl011", "hw/timer/hpet", + "trace", "util", "tests", ] diff --git a/rust/hw/char/pl011/Cargo.toml b/rust/hw/char/pl011/Cargo.toml index b2418abc4b..dc41d0e499 100644 --- a/rust/hw/char/pl011/Cargo.toml +++ b/rust/hw/char/pl011/Cargo.toml @@ -24,6 +24,7 @@ qom = { path = "../../../qom" } chardev = { path = "../../../chardev" } system = { path = "../../../system" } hwcore = { path = "../../../hw/core" } +trace = { path = "../../../trace" } [lints] workspace = true diff --git a/rust/hw/char/pl011/meson.build b/rust/hw/char/pl011/meson.build index a33f32906e..07b3da17e8 100644 --- a/rust/hw/char/pl011/meson.build +++ b/rust/hw/char/pl011/meson.build @@ -40,6 +40,7 @@ _libpl011_rs = static_library( chardev_rs, system_rs, hwcore_rs, + trace_rs ], ) diff --git a/rust/hw/char/pl011/src/device.rs b/rust/hw/char/pl011/src/device.rs index 1b4587d5f6..8889d6e54f 100644 --- a/rust/hw/char/pl011/src/device.rs +++ b/rust/hw/char/pl011/src/device.rs @@ -21,6 +21,8 @@ use util::{log::Log, log_mask_ln}; use crate::registers::{self, Interrupt, RegisterOffset}; +::trace::include_trace!("hw_char"); + // TODO: You must disable the UART before any of the control registers are // reprogrammed. When the UART is disabled in the middle of transmission or // reception, it completes the current character before stopping @@ -208,13 +210,7 @@ impl PL011Registers { (update, result) } - pub(self) fn write( - &mut self, - offset: RegisterOffset, - value: u32, - char_backend: &CharBackend, - ) -> bool { - // eprintln!("write offset {offset} value {value}"); + pub(self) fn write(&mut self, offset: RegisterOffset, value: u32, device: &PL011State) -> bool { use RegisterOffset::*; match offset { DR => return self.write_data_register(value), @@ -229,9 +225,11 @@ impl PL011Registers { } IBRD => { self.ibrd = value; + device.trace_baudrate_change(self.ibrd, self.fbrd); } FBRD => { self.fbrd = value; + device.trace_baudrate_change(self.ibrd, self.fbrd); } LCR_H => { let new_val: registers::LineControl = value.into(); @@ -242,7 +240,7 @@ impl PL011Registers { } let update = (self.line_control.send_break() != new_val.send_break()) && { let break_enable = new_val.send_break(); - let _ = char_backend.send_break(break_enable); + let _ = device.char_backend.send_break(break_enable); self.loopback_break(break_enable) }; self.line_control = new_val; @@ -279,12 +277,13 @@ impl PL011Registers { } fn read_data_register(&mut self, update: &mut bool) -> u32 { + let depth = self.fifo_depth(); self.flags.set_receive_fifo_full(false); let c = self.read_fifo[self.read_pos]; if self.read_count > 0 { self.read_count -= 1; - self.read_pos = (self.read_pos + 1) & (self.fifo_depth() - 1); + self.read_pos = (self.read_pos + 1) & (depth - 1); } if self.read_count == 0 { self.flags.set_receive_fifo_empty(true); @@ -292,6 +291,7 @@ impl PL011Registers { if self.read_count + 1 == self.read_trigger { self.int_level &= !Interrupt::RX; } + trace::trace_pl011_read_fifo(self.read_count, depth); self.receive_status_error_clear.set_from_data(c); *update = true; u32::from(c) @@ -447,7 +447,9 @@ impl PL011Registers { self.read_fifo[slot] = value; self.read_count += 1; self.flags.set_receive_fifo_empty(false); + trace::trace_pl011_fifo_rx_put(value.into(), self.read_count, depth); if self.read_count == depth { + trace::trace_pl011_fifo_rx_full(); self.flags.set_receive_fifo_full(true); } @@ -516,8 +518,21 @@ impl PL011State { uninit_field_mut!(*this, clock).write(clock); } - const fn clock_update(&self, _event: ClockEvent) { - /* pl011_trace_baudrate_change(s); */ + pub fn trace_baudrate_change(&self, ibrd: u32, fbrd: u32) { + let divider = 4.0 / f64::from(ibrd * (FBRD_MASK + 1) + fbrd); + let hz = self.clock.hz(); + let rate = if ibrd == 0 { + 0 + } else { + ((hz as f64) * divider) as u32 + }; + trace::trace_pl011_baudrate_change(rate, hz, ibrd, fbrd); + } + + fn clock_update(&self, _event: ClockEvent) { + let regs = self.regs.borrow(); + let (ibrd, fbrd) = (regs.ibrd, regs.fbrd); + self.trace_baudrate_change(ibrd, fbrd) } pub fn clock_needed(&self) -> bool { @@ -543,6 +558,7 @@ impl PL011State { } Ok(field) => { let (update_irq, result) = self.regs.borrow_mut().read(field); + trace::trace_pl011_read(offset, result, c""); if update_irq { self.update(); self.char_backend.accept_input(); @@ -557,6 +573,7 @@ impl PL011State { if let Ok(field) = RegisterOffset::try_from(offset) { // qemu_chr_fe_write_all() calls into the can_receive // callback, so handle writes before entering PL011Registers. + trace::trace_pl011_write(offset, value as u32, c""); if field == RegisterOffset::DR { // ??? Check if transmitter is enabled. let ch: [u8; 1] = [value as u8]; @@ -565,10 +582,7 @@ impl PL011State { let _ = self.char_backend.write_all(&ch); } - update_irq = self - .regs - .borrow_mut() - .write(field, value as u32, &self.char_backend); + update_irq = self.regs.borrow_mut().write(field, value as u32, self); } else { log_mask_ln!( Log::GuestError, @@ -582,11 +596,19 @@ impl PL011State { fn can_receive(&self) -> u32 { let regs = self.regs.borrow(); - // trace_pl011_can_receive(s->lcr, s->read_count, r); - regs.fifo_depth() - regs.read_count + let fifo_available = regs.fifo_depth() - regs.read_count; + trace::trace_pl011_can_receive( + regs.line_control.into(), + regs.read_count, + regs.fifo_depth(), + fifo_available, + ); + fifo_available } fn receive(&self, buf: &[u8]) { + trace::trace_pl011_receive(buf.len()); + let mut regs = self.regs.borrow_mut(); if regs.loopback_enabled() { // In loopback mode, the RX input signal is internally disconnected @@ -635,6 +657,7 @@ impl PL011State { fn update(&self) { let regs = self.regs.borrow(); let flags = regs.int_level & regs.int_enabled; + trace::trace_pl011_irq_state(flags != 0); for (irq, i) in self.interrupts.iter().zip(IRQMASK) { irq.set(flags.any_set(i)); } diff --git a/rust/hw/core/src/qdev.rs b/rust/hw/core/src/qdev.rs index a4493dbf01..c3097a284d 100644 --- a/rust/hw/core/src/qdev.rs +++ b/rust/hw/core/src/qdev.rs @@ -411,6 +411,39 @@ where impl<R: ObjectDeref> DeviceMethods for R where R::Target: IsA<DeviceState> {} +impl Clock { + pub const PERIOD_1SEC: u64 = bindings::CLOCK_PERIOD_1SEC; + + pub const fn period_from_ns(ns: u64) -> u64 { + ns * Self::PERIOD_1SEC / 1_000_000_000 + } + + pub const fn period_from_hz(hz: u64) -> u64 { + if hz == 0 { + 0 + } else { + Self::PERIOD_1SEC / hz + } + } + + pub const fn period_to_hz(period: u64) -> u64 { + if period == 0 { + 0 + } else { + Self::PERIOD_1SEC / period + } + } + + pub const fn period(&self) -> u64 { + // SAFETY: Clock is returned by init_clock_in with zero value for period + unsafe { &*self.0.as_ptr() }.period + } + + pub const fn hz(&self) -> u64 { + Self::period_to_hz(self.period()) + } +} + unsafe impl ObjectType for Clock { type Class = ObjectClass; const TYPE_NAME: &'static CStr = diff --git a/rust/meson.build b/rust/meson.build index b3ac3a7197..695d5a62de 100644 --- a/rust/meson.build +++ b/rust/meson.build @@ -34,7 +34,7 @@ subdir('system') subdir('chardev') subdir('hw/core') subdir('tests') - +subdir('trace') subdir('hw') cargo = find_program('cargo', required: false) diff --git a/rust/trace/Cargo.toml b/rust/trace/Cargo.toml new file mode 100644 index 0000000000..fc81bce580 --- /dev/null +++ b/rust/trace/Cargo.toml @@ -0,0 +1,19 @@ +[package] +name = "trace" +version = "0.1.0" +authors = ["Tanish Desai <tanishdesai37@gmail.com>"] +description = "QEMU tracing infrastructure support" +resolver = "2" +publish = false + +edition.workspace = true +homepage.workspace = true +license.workspace = true +repository.workspace = true +rust-version.workspace = true + +[dependencies] +libc = { workspace = true } + +[lints] +workspace = true diff --git a/rust/trace/meson.build b/rust/trace/meson.build new file mode 100644 index 0000000000..adca57e550 --- /dev/null +++ b/rust/trace/meson.build @@ -0,0 +1,19 @@ +rust = import('rust') + +lib_rs = configure_file( + input: 'src/lib.rs', + output: 'lib.rs', + configuration: { + 'MESON_BUILD_ROOT': meson.project_build_root(), + }) + +_trace_rs = static_library( + 'trace', # Library name, + lib_rs, + trace_rs_targets, # List of generated `.rs` custom targets + override_options: ['rust_std=2021', 'build.rust_std=2021'], + dependencies: [libc_rs], + rust_abi: 'rust', +) + +trace_rs = declare_dependency(link_with: _trace_rs) diff --git a/rust/trace/src/lib.rs b/rust/trace/src/lib.rs new file mode 100644 index 0000000000..e03bce43c4 --- /dev/null +++ b/rust/trace/src/lib.rs @@ -0,0 +1,39 @@ +// SPDX-License-Identifier: GPL-2.0-or-later + +//! This crate provides macros that aid in using QEMU's tracepoint +//! functionality. + +#[doc(hidden)] +/// Re-exported item to avoid adding libc as a dependency everywhere. +pub use libc::{syslog, LOG_INFO}; + +#[macro_export] +/// Define the trace-points from the named directory (which should have slashes +/// replaced by underscore characters) as functions in a module called `trace`. +/// +/// ```ignore +/// ::trace::include_trace!("hw_char"); +/// // ... +/// trace::trace_pl011_read_fifo_rx_full(); +/// ``` +macro_rules! include_trace { + ($name:literal) => { + #[allow( + clippy::ptr_as_ptr, + clippy::cast_lossless, + clippy::used_underscore_binding + )] + mod trace { + #[cfg(not(MESON))] + include!(concat!( + env!("MESON_BUILD_ROOT"), + "/trace/trace-", + $name, + ".rs" + )); + + #[cfg(MESON)] + include!(concat!("@MESON_BUILD_ROOT@/trace/trace-", $name, ".rs")); + } + }; +} diff --git a/rust/util/src/log.rs b/rust/util/src/log.rs index af9a3e9123..0a4bc4249a 100644 --- a/rust/util/src/log.rs +++ b/rust/util/src/log.rs @@ -142,7 +142,7 @@ macro_rules! log_mask_ln { let _: $crate::log::Log = $mask; if unsafe { - ($crate::bindings::qemu_loglevel & ($mask as std::os::raw::c_int)) != 0 + ($crate::bindings::qemu_loglevel & ($mask as std::os::raw::c_uint)) != 0 } { _ = $crate::log::LogGuard::log_fmt( format_args!("{}\n", format_args!($fmt $($args)*))); diff --git a/scripts/analyse-locks-simpletrace.py b/scripts/analyse-locks-simpletrace.py index d650dd7140..bd04cd43c9 100755 --- a/scripts/analyse-locks-simpletrace.py +++ b/scripts/analyse-locks-simpletrace.py @@ -1,5 +1,4 @@ #!/usr/bin/env python3 -# -*- coding: utf-8 -*- # # Analyse lock events and compute statistics # diff --git a/scripts/modinfo-collect.py b/scripts/modinfo-collect.py index 48bd92bd61..6ebaea989d 100644 --- a/scripts/modinfo-collect.py +++ b/scripts/modinfo-collect.py @@ -1,5 +1,4 @@ #!/usr/bin/env python3 -# -*- coding: utf-8 -*- import os import sys diff --git a/scripts/modinfo-generate.py b/scripts/modinfo-generate.py index b1538fcced..aaf23544c4 100644 --- a/scripts/modinfo-generate.py +++ b/scripts/modinfo-generate.py @@ -1,5 +1,4 @@ #!/usr/bin/env python3 -# -*- coding: utf-8 -*- import os import sys diff --git a/scripts/oss-fuzz/minimize_qtest_trace.py b/scripts/oss-fuzz/minimize_qtest_trace.py index d1f3990c16..414a6d91dd 100755 --- a/scripts/oss-fuzz/minimize_qtest_trace.py +++ b/scripts/oss-fuzz/minimize_qtest_trace.py @@ -1,5 +1,4 @@ #!/usr/bin/env python3 -# -*- coding: utf-8 -*- """ This takes a crashing qtest trace and tries to remove superfluous operations diff --git a/scripts/oss-fuzz/output_reproducer.py b/scripts/oss-fuzz/output_reproducer.py index e8ef76b341..0df96cf958 100755 --- a/scripts/oss-fuzz/output_reproducer.py +++ b/scripts/oss-fuzz/output_reproducer.py @@ -1,5 +1,4 @@ #!/usr/bin/env python3 -# -*- coding: utf-8 -*- """ Convert plain qtest traces to C or Bash reproducers diff --git a/scripts/oss-fuzz/reorder_fuzzer_qtest_trace.py b/scripts/oss-fuzz/reorder_fuzzer_qtest_trace.py index b154a25508..8af0d5d9c4 100755 --- a/scripts/oss-fuzz/reorder_fuzzer_qtest_trace.py +++ b/scripts/oss-fuzz/reorder_fuzzer_qtest_trace.py @@ -1,5 +1,4 @@ #!/usr/bin/env python3 -# -*- coding: utf-8 -*- """ Use this to convert qtest log info from a generic fuzzer input into a qtest diff --git a/scripts/probe-gdb-support.py b/scripts/probe-gdb-support.py index 6bcadce150..43c7030287 100644 --- a/scripts/probe-gdb-support.py +++ b/scripts/probe-gdb-support.py @@ -1,5 +1,4 @@ #!/usr/bin/env python3 -# coding: utf-8 # # Probe gdb for supported architectures. # diff --git a/scripts/qapi/error.py b/scripts/qapi/error.py index e35e4ddb26..f73bc553db 100644 --- a/scripts/qapi/error.py +++ b/scripts/qapi/error.py @@ -1,5 +1,3 @@ -# -*- coding: utf-8 -*- -# # Copyright (c) 2017-2019 Red Hat Inc. # # Authors: diff --git a/scripts/qapi/expr.py b/scripts/qapi/expr.py index cae0a08359..f40b247f8b 100644 --- a/scripts/qapi/expr.py +++ b/scripts/qapi/expr.py @@ -1,5 +1,3 @@ -# -*- coding: utf-8 -*- -# # Copyright IBM, Corp. 2011 # Copyright (c) 2013-2021 Red Hat Inc. # diff --git a/scripts/qapi/gen.py b/scripts/qapi/gen.py index d3c56d45c8..0c9b8db3b0 100644 --- a/scripts/qapi/gen.py +++ b/scripts/qapi/gen.py @@ -1,5 +1,3 @@ -# -*- coding: utf-8 -*- -# # QAPI code generation # # Copyright (c) 2015-2019 Red Hat Inc. diff --git a/scripts/qapi/parser.py b/scripts/qapi/parser.py index 2529edf81a..9fbf80a541 100644 --- a/scripts/qapi/parser.py +++ b/scripts/qapi/parser.py @@ -1,5 +1,3 @@ -# -*- coding: utf-8 -*- -# # QAPI schema parser # # Copyright IBM, Corp. 2011 diff --git a/scripts/qapi/schema.py b/scripts/qapi/schema.py index 3abddea352..8d88b40de2 100644 --- a/scripts/qapi/schema.py +++ b/scripts/qapi/schema.py @@ -1,5 +1,3 @@ -# -*- coding: utf-8 -*- -# # QAPI schema internal representation # # Copyright (c) 2015-2019 Red Hat Inc. diff --git a/scripts/qemu-plugin-symbols.py b/scripts/qemu-plugin-symbols.py index e285ebb8f9..69644979c1 100755 --- a/scripts/qemu-plugin-symbols.py +++ b/scripts/qemu-plugin-symbols.py @@ -1,5 +1,4 @@ #!/usr/bin/env python3 -# -*- coding: utf-8 -*- # # Extract QEMU Plugin API symbols from a header file # diff --git a/scripts/qemugdb/tcg.py b/scripts/qemugdb/tcg.py index 16c03c06a9..22529c7277 100644 --- a/scripts/qemugdb/tcg.py +++ b/scripts/qemugdb/tcg.py @@ -1,5 +1,3 @@ -# -*- coding: utf-8 -*- -# # GDB debugging support, TCG status # # Copyright 2016 Linaro Ltd diff --git a/scripts/qemugdb/timers.py b/scripts/qemugdb/timers.py index 46537b27cf..5714f92cc2 100644 --- a/scripts/qemugdb/timers.py +++ b/scripts/qemugdb/timers.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- # GDB debugging support # # Copyright 2017 Linaro Ltd diff --git a/scripts/replay-dump.py b/scripts/replay-dump.py index 4ce7ff51cc..097636570d 100755 --- a/scripts/replay-dump.py +++ b/scripts/replay-dump.py @@ -1,5 +1,4 @@ #!/usr/bin/env python3 -# -*- coding: utf-8 -*- # # Dump the contents of a recorded execution stream # diff --git a/scripts/tracetool.py b/scripts/tracetool.py index 5de9ce96d3..0fdc9cb947 100755 --- a/scripts/tracetool.py +++ b/scripts/tracetool.py @@ -1,5 +1,4 @@ #!/usr/bin/env python3 -# -*- coding: utf-8 -*- """ Command-line wrapper for the tracetool machinery. diff --git a/scripts/tracetool/__init__.py b/scripts/tracetool/__init__.py index 1d5238a084..74062d21a7 100644 --- a/scripts/tracetool/__init__.py +++ b/scripts/tracetool/__init__.py @@ -1,4 +1,4 @@ -# -*- coding: utf-8 -*- +# SPDX-License-Identifier: GPL-2.0-or-later """ Machinery for generating tracing-related intermediate files. @@ -15,7 +15,6 @@ __email__ = "stefanha@redhat.com" import os import re import sys -import weakref from pathlib import PurePath import tracetool.backend @@ -31,6 +30,49 @@ def error(*lines): error_write(*lines) sys.exit(1) +FMT_TOKEN = re.compile(r'''(?: + " ( (?: [^"\\] | \\[\\"abfnrt] | # a string literal + \\x[0-9a-fA-F][0-9a-fA-F]) *? ) " + | ( PRI [duixX] (?:8|16|32|64|PTR|MAX) ) # a PRIxxx macro + | \s+ # spaces (ignored) + )''', re.X) + +PRI_SIZE_MAP = { + '8': 'hh', + '16': 'h', + '32': '', + '64': 'll', + 'PTR': 't', + 'MAX': 'j', +} + +def expand_format_string(c_fmt, prefix=""): + def pri_macro_to_fmt(pri_macro): + assert pri_macro.startswith("PRI") + fmt_type = pri_macro[3] # 'd', 'i', 'u', or 'x' + fmt_size = pri_macro[4:] # '8', '16', '32', '64', 'PTR', 'MAX' + + size = PRI_SIZE_MAP.get(fmt_size, None) + if size is None: + raise Exception(f"unknown macro {pri_macro}") + return size + fmt_type + + result = prefix + pos = 0 + while pos < len(c_fmt): + m = FMT_TOKEN.match(c_fmt, pos) + if not m: + print("No match at position", pos, ":", repr(c_fmt[pos:]), file=sys.stderr) + raise Exception("syntax error in trace file") + if m[1]: + substr = m[1] + elif m[2]: + substr = pri_macro_to_fmt(m[2]) + else: + substr = "" + result += substr + pos = m.end() + return result out_lineno = 1 out_filename = '<none>' @@ -90,6 +132,49 @@ ALLOWED_TYPES = [ "ptrdiff_t", ] +C_TYPE_KEYWORDS = {"char", "int", "void", "short", "long", "signed", "unsigned"} + +C_TO_RUST_TYPE_MAP = { + "int": "std::ffi::c_int", + "long": "std::ffi::c_long", + "long long": "std::ffi::c_longlong", + "short": "std::ffi::c_short", + "char": "std::ffi::c_char", + "bool": "bool", + "unsigned": "std::ffi::c_uint", + # multiple keywords, keep them sorted + "long unsigned": "std::ffi::c_long", + "long long unsigned": "std::ffi::c_ulonglong", + "short unsigned": "std::ffi::c_ushort", + "char unsigned": "u8", + "int8_t": "i8", + "uint8_t": "u8", + "int16_t": "i16", + "uint16_t": "u16", + "int32_t": "i32", + "uint32_t": "u32", + "int64_t": "i64", + "uint64_t": "u64", + "void": "()", + "size_t": "usize", + "ssize_t": "isize", + "uintptr_t": "usize", + "ptrdiff_t": "isize", +} + +# Rust requires manual casting of <32-bit types when passing them to +# variable-argument functions. +RUST_VARARGS_SMALL_TYPES = { + "std::ffi::c_short", + "std::ffi::c_ushort", + "std::ffi::c_char", + "i8", + "u8", + "i16", + "u16", + "bool", +} + def validate_type(name): bits = name.split(" ") for bit in bits: @@ -105,6 +190,38 @@ def validate_type(name): "other complex pointer types should be " "declared as 'void *'" % name) +def c_type_to_rust(name): + ptr = False + const = False + name = name.rstrip() + if name[-1] == '*': + name = name[:-1].rstrip() + ptr = True + if name[-1] == '*': + # pointers to pointers are the same as void* + name = "void" + + bits = name.split() + if "const" in bits: + const = True + bits.remove("const") + if bits[0] in C_TYPE_KEYWORDS: + if "signed" in bits: + bits.remove("signed") + if len(bits) > 1 and "int" in bits: + bits.remove("int") + bits.sort() + name = ' '.join(bits) + else: + if len(bits) > 1: + raise ValueError("Invalid type '%s'." % name) + name = bits[0] + + ty = C_TO_RUST_TYPE_MAP[name.strip()] + if ptr: + ty = f'*{"const" if const else "mut"} {ty}' + return ty + class Arguments: """Event arguments description.""" @@ -122,10 +239,6 @@ class Arguments: else: self._args.append(arg) - def copy(self): - """Create a new copy.""" - return Arguments(list(self._args)) - @staticmethod def build(arg_str): """Build and Arguments instance from an argument string. @@ -197,6 +310,43 @@ class Arguments: """List of argument names casted to their type.""" return ["(%s)%s" % (type_, name) for type_, name in self._args] + def rust_decl_extern(self): + """Return a Rust argument list for an extern "C" function""" + return ", ".join((f"_{name}: {c_type_to_rust(type_)}" + for type_, name in self._args)) + + def rust_decl(self): + """Return a Rust argument list for a tracepoint function""" + def decl_type(type_): + if type_ == "const char *": + return "&std::ffi::CStr" + return c_type_to_rust(type_) + + return ", ".join((f"_{name}: {decl_type(type_)}" + for type_, name in self._args)) + + def rust_call_extern(self): + """Return a Rust argument list for a call to an extern "C" function""" + def rust_cast(name, type_): + if type_ == "const char *": + return f"_{name}.as_ptr()" + return f"_{name}" + + return ", ".join((rust_cast(name, type_) for type_, name in self._args)) + + def rust_call_varargs(self): + """Return a Rust argument list for a call to a C varargs function""" + def rust_cast(name, type_): + if type_ == "const char *": + return f"_{name}.as_ptr()" + + type_ = c_type_to_rust(type_) + if type_ in RUST_VARARGS_SMALL_TYPES: + return f"_{name} as std::ffi::c_int" + return f"_{name} /* as {type_} */" + + return ", ".join((rust_cast(name, type_) for type_, name in self._args)) + class Event(object): """Event description. @@ -222,13 +372,12 @@ class Event(object): r"(?P<name>\w+)" r"\((?P<args>[^)]*)\)" r"\s*" - r"(?:(?:(?P<fmt_trans>\".+),)?\s*(?P<fmt>\".+))?" + r"(?P<fmt>\".+)?" r"\s*") _VALID_PROPS = set(["disable"]) - def __init__(self, name, props, fmt, args, lineno, filename, orig=None, - event_trans=None, event_exec=None): + def __init__(self, name, props, fmt, args, lineno, filename): """ Parameters ---------- @@ -236,20 +385,14 @@ class Event(object): Event name. props : list of str Property names. - fmt : str, list of str - Event printing format string(s). + fmt : str + Event printing format string. args : Arguments Event arguments. lineno : int The line number in the input file. filename : str The path to the input file. - orig : Event or None - Original Event before transformation/generation. - event_trans : Event or None - Generated translation-time event ("tcg" property). - event_exec : Event or None - Generated execution-time event ("tcg" property). """ self.name = name @@ -258,29 +401,16 @@ class Event(object): self.args = args self.lineno = int(lineno) self.filename = str(filename) - self.event_trans = event_trans - self.event_exec = event_exec if len(args) > 10: raise ValueError("Event '%s' has more than maximum permitted " "argument count" % name) - if orig is None: - self.original = weakref.ref(self) - else: - self.original = orig - unknown_props = set(self.properties) - self._VALID_PROPS if len(unknown_props) > 0: raise ValueError("Unknown properties: %s" % ", ".join(unknown_props)) - assert isinstance(self.fmt, str) or len(self.fmt) == 2 - def copy(self): - """Create a new copy.""" - return Event(self.name, list(self.properties), self.fmt, - self.args.copy(), self.lineno, self.filename, - self, self.event_trans, self.event_exec) @staticmethod def build(line_str, lineno, filename): @@ -302,8 +432,7 @@ class Event(object): name = groups["name"] props = groups["props"].split() fmt = groups["fmt"] - fmt_trans = groups["fmt_trans"] - if fmt.find("%m") != -1 or fmt_trans.find("%m") != -1: + if fmt.find("%m") != -1: raise ValueError("Event format '%m' is forbidden, pass the error " "as an explicit trace argument") if fmt.endswith(r'\n"'): @@ -312,29 +441,22 @@ class Event(object): if '\\n' in fmt: raise ValueError("Event format must not use new line character") - if len(fmt_trans) > 0: - fmt = [fmt_trans, fmt] args = Arguments.build(groups["args"]) return Event(name, props, fmt, args, lineno, posix_relpath(filename)) def __repr__(self): """Evaluable string representation for this object.""" - if isinstance(self.fmt, str): - fmt = self.fmt - else: - fmt = "%s, %s" % (self.fmt[0], self.fmt[1]) return "Event('%s %s(%s) %s')" % (" ".join(self.properties), self.name, self.args, - fmt) + self.fmt) # Star matching on PRI is dangerous as one might have multiple # arguments with that format, hence the non-greedy version of it. _FMT = re.compile(r"(%[\d\.]*\w+|%.*?PRI\S+)") def formats(self): """List conversion specifiers in the argument print format string.""" - assert not isinstance(self.fmt, list) return self._FMT.findall(self.fmt) QEMU_TRACE = "trace_%(name)s" diff --git a/scripts/tracetool/backend/__init__.py b/scripts/tracetool/backend/__init__.py index 7bfcc86cc5..9109a783c7 100644 --- a/scripts/tracetool/backend/__init__.py +++ b/scripts/tracetool/backend/__init__.py @@ -1,4 +1,4 @@ -# -*- coding: utf-8 -*- +# SPDX-License-Identifier: GPL-2.0-or-later """ Backend management. @@ -19,11 +19,15 @@ All backends must generate their contents through the 'tracetool.out' routine. Backend attributes ------------------ -========= ==================================================================== -Attribute Description -========= ==================================================================== -PUBLIC If exists and is set to 'True', the backend is considered "public". -========= ==================================================================== +=========================== ==================================================== +Attribute Description +=========================== ==================================================== +PUBLIC If exists and is set to 'True', the backend is + considered "public". +CHECK_TRACE_EVENT_GET_STATE If exists and is set to 'True', the backend-specific + code inside the tracepoint is emitted within an + ``if trace_event_get_state()`` conditional. +=========================== ==================================================== Backend functions @@ -94,29 +98,40 @@ def exists(name): if name == "nop": return True name = name.replace("-", "_") - return tracetool.try_import("tracetool.backend." + name)[1] + return tracetool.try_import("tracetool.backend." + name)[0] class Wrapper: def __init__(self, backends, format): self._backends = [backend.replace("-", "_") for backend in backends] self._format = format.replace("-", "_") + self.check_trace_event_get_state = False for backend in self._backends: assert exists(backend) assert tracetool.format.exists(self._format) + for backend in self.backend_modules(): + check_trace_event_get_state = getattr(backend, "CHECK_TRACE_EVENT_GET_STATE", False) + self.check_trace_event_get_state = self.check_trace_event_get_state or check_trace_event_get_state - def _run_function(self, name, *args, **kwargs): + def backend_modules(self): for backend in self._backends: - func = tracetool.try_import("tracetool.backend." + backend, - name % self._format, None)[1] - if func is not None: - func(*args, **kwargs) + module = tracetool.try_import("tracetool.backend." + backend)[1] + if module is not None: + yield module + + def _run_function(self, name, *args, check_trace_event_get_state=None, **kwargs): + for backend in self.backend_modules(): + func = getattr(backend, name % self._format, None) + if func is not None and \ + (check_trace_event_get_state is None or + check_trace_event_get_state == getattr(backend, 'CHECK_TRACE_EVENT_GET_STATE', False)): + func(*args, **kwargs) def generate_begin(self, events, group): self._run_function("generate_%s_begin", events, group) - def generate(self, event, group): - self._run_function("generate_%s", event, group) + def generate(self, event, group, check_trace_event_get_state=None): + self._run_function("generate_%s", event, group, check_trace_event_get_state=check_trace_event_get_state) def generate_backend_dstate(self, event, group): self._run_function("generate_%s_backend_dstate", event, group) diff --git a/scripts/tracetool/backend/dtrace.py b/scripts/tracetool/backend/dtrace.py index e17edc9b9d..b4af403025 100644 --- a/scripts/tracetool/backend/dtrace.py +++ b/scripts/tracetool/backend/dtrace.py @@ -1,4 +1,4 @@ -# -*- coding: utf-8 -*- +# SPDX-License-Identifier: GPL-2.0-or-later """ DTrace/SystemTAP backend. diff --git a/scripts/tracetool/backend/ftrace.py b/scripts/tracetool/backend/ftrace.py index 5fa30ccc08..e03698a2ed 100644 --- a/scripts/tracetool/backend/ftrace.py +++ b/scripts/tracetool/backend/ftrace.py @@ -1,4 +1,4 @@ -# -*- coding: utf-8 -*- +# SPDX-License-Identifier: GPL-2.0-or-later """ Ftrace built-in backend. @@ -12,10 +12,11 @@ __maintainer__ = "Stefan Hajnoczi" __email__ = "stefanha@redhat.com" -from tracetool import out +from tracetool import out, expand_format_string PUBLIC = True +CHECK_TRACE_EVENT_GET_STATE = True def generate_h_begin(events, group): @@ -28,22 +29,11 @@ def generate_h(event, group): if len(event.args) > 0: argnames = ", " + argnames - out(' {', - ' char ftrace_buf[MAX_TRACE_STRLEN];', - ' int unused __attribute__ ((unused));', - ' int trlen;', - ' if (trace_event_get_state(%(event_id)s)) {', - '#line %(event_lineno)d "%(event_filename)s"', - ' trlen = snprintf(ftrace_buf, MAX_TRACE_STRLEN,', - ' "%(name)s " %(fmt)s "\\n" %(argnames)s);', + out('#line %(event_lineno)d "%(event_filename)s"', + ' ftrace_write("%(name)s " %(fmt)s "\\n" %(argnames)s);', '#line %(out_next_lineno)d "%(out_filename)s"', - ' trlen = MIN(trlen, MAX_TRACE_STRLEN - 1);', - ' unused = write(trace_marker_fd, ftrace_buf, trlen);', - ' }', - ' }', name=event.name, args=event.args, - event_id="TRACE_" + event.name.upper(), event_lineno=event.lineno, event_filename=event.filename, fmt=event.fmt.rstrip("\n"), @@ -53,3 +43,9 @@ def generate_h(event, group): def generate_h_backend_dstate(event, group): out(' trace_event_get_state_dynamic_by_id(%(event_id)s) || \\', event_id="TRACE_" + event.name.upper()) + +def generate_rs(event, group): + out(' let format_string = c"%(fmt)s";', + ' unsafe {bindings::ftrace_write(format_string.as_ptr() as *const c_char, %(args)s);}', + fmt=expand_format_string(event.fmt), + args=event.args.rust_call_varargs()) diff --git a/scripts/tracetool/backend/log.py b/scripts/tracetool/backend/log.py index eb50ceea34..9e3e5046f5 100644 --- a/scripts/tracetool/backend/log.py +++ b/scripts/tracetool/backend/log.py @@ -1,4 +1,4 @@ -# -*- coding: utf-8 -*- +# SPDX-License-Identifier: GPL-2.0-or-later """ Stderr built-in backend. @@ -12,10 +12,11 @@ __maintainer__ = "Stefan Hajnoczi" __email__ = "stefanha@redhat.com" -from tracetool import out +from tracetool import out, expand_format_string PUBLIC = True +CHECK_TRACE_EVENT_GET_STATE = True def generate_h_begin(events, group): @@ -28,14 +29,11 @@ def generate_h(event, group): if len(event.args) > 0: argnames = ", " + argnames - cond = "trace_event_get_state(%s)" % ("TRACE_" + event.name.upper()) - - out(' if (%(cond)s && qemu_loglevel_mask(LOG_TRACE)) {', + out(' if (qemu_loglevel_mask(LOG_TRACE)) {', '#line %(event_lineno)d "%(event_filename)s"', - ' qemu_log("%(name)s " %(fmt)s "\\n"%(argnames)s);', + ' qemu_log("%(name)s " %(fmt)s "\\n"%(argnames)s);', '#line %(out_next_lineno)d "%(out_filename)s"', - ' }', - cond=cond, + ' }', event_lineno=event.lineno, event_filename=event.filename, name=event.name, @@ -46,3 +44,11 @@ def generate_h(event, group): def generate_h_backend_dstate(event, group): out(' trace_event_get_state_dynamic_by_id(%(event_id)s) || \\', event_id="TRACE_" + event.name.upper()) + +def generate_rs(event, group): + out(' let format_string = c"%(fmt)s\\n";', + ' if (unsafe { bindings::qemu_loglevel } & bindings::LOG_TRACE) != 0 {', + ' unsafe { bindings::qemu_log(format_string.as_ptr() as *const c_char, %(args)s);}', + ' }', + fmt=expand_format_string(event.fmt, event.name + " "), + args=event.args.rust_call_varargs()) diff --git a/scripts/tracetool/backend/simple.py b/scripts/tracetool/backend/simple.py index 7c84c06b20..b131e4fc19 100644 --- a/scripts/tracetool/backend/simple.py +++ b/scripts/tracetool/backend/simple.py @@ -1,4 +1,4 @@ -# -*- coding: utf-8 -*- +# SPDX-License-Identifier: GPL-2.0-or-later """ Simple built-in backend. @@ -16,6 +16,7 @@ from tracetool import out PUBLIC = True +CHECK_TRACE_EVENT_GET_STATE = True def is_string(arg): @@ -36,13 +37,8 @@ def generate_h_begin(events, group): def generate_h(event, group): - event_id = 'TRACE_' + event.name.upper() - cond = "trace_event_get_state(%s)" % event_id - out(' if (%(cond)s) {', - ' _simple_%(api)s(%(args)s);', - ' }', + out(' _simple_%(api)s(%(args)s);', api=event.api(), - cond=cond, args=", ".join(event.args.names())) @@ -102,3 +98,10 @@ def generate_c(event, group): out(' trace_record_finish(&rec);', '}', '') + +def generate_rs(event, group): + out(' extern "C" { fn _simple_%(api)s(%(rust_args)s); }', + ' unsafe { _simple_%(api)s(%(args)s); }', + api=event.api(), + rust_args=event.args.rust_decl_extern(), + args=event.args.rust_call_extern()) diff --git a/scripts/tracetool/backend/syslog.py b/scripts/tracetool/backend/syslog.py index 3f82e54aab..12b826593d 100644 --- a/scripts/tracetool/backend/syslog.py +++ b/scripts/tracetool/backend/syslog.py @@ -1,4 +1,4 @@ -# -*- coding: utf-8 -*- +# SPDX-License-Identifier: GPL-2.0-or-later """ Syslog built-in backend. @@ -12,10 +12,11 @@ __maintainer__ = "Stefan Hajnoczi" __email__ = "stefanha@redhat.com" -from tracetool import out +from tracetool import out, expand_format_string PUBLIC = True +CHECK_TRACE_EVENT_GET_STATE = True def generate_h_begin(events, group): @@ -28,20 +29,20 @@ def generate_h(event, group): if len(event.args) > 0: argnames = ", " + argnames - cond = "trace_event_get_state(%s)" % ("TRACE_" + event.name.upper()) - - out(' if (%(cond)s) {', - '#line %(event_lineno)d "%(event_filename)s"', + out('#line %(event_lineno)d "%(event_filename)s"', ' syslog(LOG_INFO, "%(name)s " %(fmt)s %(argnames)s);', '#line %(out_next_lineno)d "%(out_filename)s"', - ' }', - cond=cond, event_lineno=event.lineno, event_filename=event.filename, name=event.name, fmt=event.fmt.rstrip("\n"), argnames=argnames) +def generate_rs(event, group): + out(' let format_string = c"%(fmt)s";', + ' unsafe {::trace::syslog(::trace::LOG_INFO, format_string.as_ptr() as *const c_char, %(args)s);}', + fmt=expand_format_string(event.fmt), + args=event.args.rust_call_varargs()) def generate_h_backend_dstate(event, group): out(' trace_event_get_state_dynamic_by_id(%(event_id)s) || \\', diff --git a/scripts/tracetool/backend/ust.py b/scripts/tracetool/backend/ust.py index c857516f21..3aa9bb1da2 100644 --- a/scripts/tracetool/backend/ust.py +++ b/scripts/tracetool/backend/ust.py @@ -1,4 +1,4 @@ -# -*- coding: utf-8 -*- +# SPDX-License-Identifier: GPL-2.0-or-later """ LTTng User Space Tracing backend. diff --git a/scripts/tracetool/format/__init__.py b/scripts/tracetool/format/__init__.py index 2dc46f3dd9..7b9d1b5782 100644 --- a/scripts/tracetool/format/__init__.py +++ b/scripts/tracetool/format/__init__.py @@ -1,4 +1,4 @@ -# -*- coding: utf-8 -*- +# SPDX-License-Identifier: GPL-2.0-or-later """ Format management. @@ -70,7 +70,7 @@ def exists(name): if len(name) == 0: return False name = name.replace("-", "_") - return tracetool.try_import("tracetool.format." + name)[1] + return tracetool.try_import("tracetool.format." + name)[0] def generate(events, format, backend, group): diff --git a/scripts/tracetool/format/c.py b/scripts/tracetool/format/c.py index e473fb6c6e..50e03313cb 100644 --- a/scripts/tracetool/format/c.py +++ b/scripts/tracetool/format/c.py @@ -1,4 +1,4 @@ -# -*- coding: utf-8 -*- +# SPDX-License-Identifier: GPL-2.0-or-later """ trace/generated-tracers.c diff --git a/scripts/tracetool/format/d.py b/scripts/tracetool/format/d.py index a5e096e214..e9e33dfe30 100644 --- a/scripts/tracetool/format/d.py +++ b/scripts/tracetool/format/d.py @@ -1,4 +1,4 @@ -# -*- coding: utf-8 -*- +# SPDX-License-Identifier: GPL-2.0-or-later """ trace/generated-tracers.dtrace (DTrace only). diff --git a/scripts/tracetool/format/h.py b/scripts/tracetool/format/h.py index b42a8268a8..dd58713a15 100644 --- a/scripts/tracetool/format/h.py +++ b/scripts/tracetool/format/h.py @@ -1,4 +1,4 @@ -# -*- coding: utf-8 -*- +# SPDX-License-Identifier: GPL-2.0-or-later """ trace/generated-tracers.h @@ -40,11 +40,6 @@ def generate(events, backend, group): enabled = 0 else: enabled = 1 - if "tcg-exec" in e.properties: - # a single define for the two "sub-events" - out('#define TRACE_%(name)s_ENABLED %(enabled)d', - name=e.original.name.upper(), - enabled=enabled) out('#define TRACE_%s_ENABLED %d' % (e.name.upper(), enabled)) backend.generate_begin(events, group) @@ -60,7 +55,6 @@ def generate(events, backend, group): out(' false)') - # tracer without checks out('', 'static inline void %(api)s(%(args)s)', '{', @@ -68,11 +62,17 @@ def generate(events, backend, group): args=e.args) if "disable" not in e.properties: - backend.generate(e, group) - + backend.generate(e, group, check_trace_event_get_state=False) + + if backend.check_trace_event_get_state: + event_id = 'TRACE_' + e.name.upper() + cond = "trace_event_get_state(%s)" % event_id + out(' if (%(cond)s) {', + cond=cond) + backend.generate(e, group, check_trace_event_get_state=True) + out(' }') out('}') - backend.generate_end(events, group) out('#endif /* TRACE_%s_GENERATED_TRACERS_H */' % group.upper()) diff --git a/scripts/tracetool/format/log_stap.py b/scripts/tracetool/format/log_stap.py index 710d62bffe..259303a189 100644 --- a/scripts/tracetool/format/log_stap.py +++ b/scripts/tracetool/format/log_stap.py @@ -1,4 +1,4 @@ -# -*- coding: utf-8 -*- +# SPDX-License-Identifier: GPL-2.0-or-later """ Generate .stp file that printfs log messages (DTrace with SystemTAP only). @@ -18,8 +18,6 @@ from tracetool.backend.dtrace import binary, probeprefix from tracetool.backend.simple import is_string from tracetool.format.stap import stap_escape -def global_var_name(name): - return probeprefix().replace(".", "_") + "_" + name STATE_SKIP = 0 STATE_LITERAL = 1 diff --git a/scripts/tracetool/format/rs.py b/scripts/tracetool/format/rs.py new file mode 100644 index 0000000000..32ac4e5977 --- /dev/null +++ b/scripts/tracetool/format/rs.py @@ -0,0 +1,64 @@ +# SPDX-License-Identifier: GPL-2.0-or-later + +""" +trace-DIR.rs +""" + +__author__ = "Tanish Desai <tanishdesai37@gmail.com>" +__copyright__ = "Copyright 2025, Tanish Desai <tanishdesai37@gmail.com>" +__license__ = "GPL version 2 or (at your option) any later version" + +__maintainer__ = "Stefan Hajnoczi" +__email__ = "stefanha@redhat.com" + + +from tracetool import out + + +def generate(events, backend, group): + out('// SPDX-License-Identifier: GPL-2.0-or-later', + '// This file is @generated by tracetool, do not edit.', + '', + '#[allow(unused_imports)]', + 'use std::ffi::c_char;', + '#[allow(unused_imports)]', + 'use util::bindings;', + '', + '#[inline(always)]', + 'fn trace_event_state_is_enabled(dstate: u16) -> bool {', + ' (unsafe { trace_events_enabled_count }) != 0 && dstate != 0', + '}', + '', + 'extern "C" {', + ' static mut trace_events_enabled_count: u32;', + '}',) + + out('extern "C" {') + + for e in events: + out(' static mut %s: u16;' % e.api(e.QEMU_DSTATE)) + out('}') + + backend.generate_begin(events, group) + + for e in events: + out('', + '#[inline(always)]', + '#[allow(dead_code)]', + 'pub fn %(api)s(%(args)s)', + '{', + api=e.api(e.QEMU_TRACE), + args=e.args.rust_decl()) + + if "disable" not in e.properties: + backend.generate(e, group, check_trace_event_get_state=False) + if backend.check_trace_event_get_state: + event_id = 'TRACE_' + e.name.upper() + out(' if trace_event_state_is_enabled(unsafe { _%(event_id)s_DSTATE}) {', + event_id = event_id, + api=e.api()) + backend.generate(e, group, check_trace_event_get_state=True) + out(' }') + out('}') + + backend.generate_end(events, group) diff --git a/scripts/tracetool/format/simpletrace_stap.py b/scripts/tracetool/format/simpletrace_stap.py index 72971133bf..c7bde97a85 100644 --- a/scripts/tracetool/format/simpletrace_stap.py +++ b/scripts/tracetool/format/simpletrace_stap.py @@ -1,4 +1,4 @@ -# -*- coding: utf-8 -*- +# SPDX-License-Identifier: GPL-2.0-or-later """ Generate .stp file that outputs simpletrace binary traces (DTrace with SystemTAP only). @@ -17,8 +17,6 @@ from tracetool.backend.dtrace import probeprefix from tracetool.backend.simple import is_string from tracetool.format.stap import stap_escape -def global_var_name(name): - return probeprefix().replace(".", "_") + "_" + name def generate(events, backend, group): out('/* This file is autogenerated by tracetool, do not edit. */', diff --git a/scripts/tracetool/format/stap.py b/scripts/tracetool/format/stap.py index 4d77fbc11a..285c9203ba 100644 --- a/scripts/tracetool/format/stap.py +++ b/scripts/tracetool/format/stap.py @@ -1,4 +1,4 @@ -# -*- coding: utf-8 -*- +# SPDX-License-Identifier: GPL-2.0-or-later """ Generate .stp file (DTrace with SystemTAP only). diff --git a/scripts/tracetool/format/ust_events_c.py b/scripts/tracetool/format/ust_events_c.py index 569754a304..074226bfd3 100644 --- a/scripts/tracetool/format/ust_events_c.py +++ b/scripts/tracetool/format/ust_events_c.py @@ -1,4 +1,4 @@ -# -*- coding: utf-8 -*- +# SPDX-License-Identifier: GPL-2.0-or-later """ trace/generated-ust.c diff --git a/scripts/tracetool/format/ust_events_h.py b/scripts/tracetool/format/ust_events_h.py index 2a31fefeca..cee7970a40 100644 --- a/scripts/tracetool/format/ust_events_h.py +++ b/scripts/tracetool/format/ust_events_h.py @@ -1,4 +1,4 @@ -# -*- coding: utf-8 -*- +# SPDX-License-Identifier: GPL-2.0-or-later """ trace/generated-ust-provider.h diff --git a/target/riscv/cpu.c b/target/riscv/cpu.c index d055ddf462..a877018ab0 100644 --- a/target/riscv/cpu.c +++ b/target/riscv/cpu.c @@ -604,7 +604,7 @@ static void riscv_cpu_dump_state(CPUState *cs, FILE *f, int flags) } } } - if (riscv_has_ext(env, RVV) && (flags & CPU_DUMP_VPU)) { + if (riscv_cpu_cfg(env)->ext_zve32x && (flags & CPU_DUMP_VPU)) { static const int dump_rvv_csrs[] = { CSR_VSTART, CSR_VXSAT, diff --git a/target/riscv/cpu.h b/target/riscv/cpu.h index 4a862da615..2c2266415e 100644 --- a/target/riscv/cpu.h +++ b/target/riscv/cpu.h @@ -592,6 +592,7 @@ static inline int riscv_has_ext(CPURISCVState *env, target_ulong ext) extern const char * const riscv_int_regnames[]; extern const char * const riscv_int_regnamesh[]; extern const char * const riscv_fpr_regnames[]; +extern const char * const riscv_rvv_regnames[]; const char *riscv_cpu_get_trap_name(target_ulong cause, bool async); int riscv_cpu_write_elf64_note(WriteCoreDumpFunction f, CPUState *cs, @@ -873,7 +874,7 @@ static inline void riscv_csr_write(CPURISCVState *env, int csrno, static inline target_ulong riscv_csr_read(CPURISCVState *env, int csrno) { target_ulong val = 0; - riscv_csrrw(env, csrno, &val, 0, 0, 0); + riscv_csrr(env, csrno, &val); return val; } diff --git a/target/riscv/csr.c b/target/riscv/csr.c index 3c8989f522..5c91658c3d 100644 --- a/target/riscv/csr.c +++ b/target/riscv/csr.c @@ -203,6 +203,8 @@ static RISCVException cfi_ss(CPURISCVState *env, int csrno) #if !defined(CONFIG_USER_ONLY) if (env->debugger) { return RISCV_EXCP_NONE; + } else if (env->virt_enabled) { + return RISCV_EXCP_VIRT_INSTRUCTION_FAULT; } #endif return RISCV_EXCP_ILLEGAL_INST; @@ -2003,7 +2005,8 @@ static RISCVException write_mstatus(CPURISCVState *env, int csrno, if (riscv_has_ext(env, RVF)) { mask |= MSTATUS_FS; } - if (riscv_has_ext(env, RVV)) { + + if (riscv_cpu_cfg(env)->ext_zve32x) { mask |= MSTATUS_VS; } diff --git a/target/riscv/helper.h b/target/riscv/helper.h index f712b1c368..b785456ee0 100644 --- a/target/riscv/helper.h +++ b/target/riscv/helper.h @@ -1101,14 +1101,14 @@ DEF_HELPER_6(vslidedown_vx_b, void, ptr, ptr, tl, ptr, env, i32) DEF_HELPER_6(vslidedown_vx_h, void, ptr, ptr, tl, ptr, env, i32) DEF_HELPER_6(vslidedown_vx_w, void, ptr, ptr, tl, ptr, env, i32) DEF_HELPER_6(vslidedown_vx_d, void, ptr, ptr, tl, ptr, env, i32) -DEF_HELPER_6(vslide1up_vx_b, void, ptr, ptr, tl, ptr, env, i32) -DEF_HELPER_6(vslide1up_vx_h, void, ptr, ptr, tl, ptr, env, i32) -DEF_HELPER_6(vslide1up_vx_w, void, ptr, ptr, tl, ptr, env, i32) -DEF_HELPER_6(vslide1up_vx_d, void, ptr, ptr, tl, ptr, env, i32) -DEF_HELPER_6(vslide1down_vx_b, void, ptr, ptr, tl, ptr, env, i32) -DEF_HELPER_6(vslide1down_vx_h, void, ptr, ptr, tl, ptr, env, i32) -DEF_HELPER_6(vslide1down_vx_w, void, ptr, ptr, tl, ptr, env, i32) -DEF_HELPER_6(vslide1down_vx_d, void, ptr, ptr, tl, ptr, env, i32) +DEF_HELPER_6(vslide1up_vx_b, void, ptr, ptr, i64, ptr, env, i32) +DEF_HELPER_6(vslide1up_vx_h, void, ptr, ptr, i64, ptr, env, i32) +DEF_HELPER_6(vslide1up_vx_w, void, ptr, ptr, i64, ptr, env, i32) +DEF_HELPER_6(vslide1up_vx_d, void, ptr, ptr, i64, ptr, env, i32) +DEF_HELPER_6(vslide1down_vx_b, void, ptr, ptr, i64, ptr, env, i32) +DEF_HELPER_6(vslide1down_vx_h, void, ptr, ptr, i64, ptr, env, i32) +DEF_HELPER_6(vslide1down_vx_w, void, ptr, ptr, i64, ptr, env, i32) +DEF_HELPER_6(vslide1down_vx_d, void, ptr, ptr, i64, ptr, env, i32) DEF_HELPER_6(vfslide1up_vf_h, void, ptr, ptr, i64, ptr, env, i32) DEF_HELPER_6(vfslide1up_vf_w, void, ptr, ptr, i64, ptr, env, i32) @@ -1284,3 +1284,8 @@ DEF_HELPER_4(vgmul_vv, void, ptr, ptr, env, i32) DEF_HELPER_5(vsm4k_vi, void, ptr, ptr, i32, env, i32) DEF_HELPER_4(vsm4r_vv, void, ptr, ptr, env, i32) DEF_HELPER_4(vsm4r_vs, void, ptr, ptr, env, i32) + +/* CFI (zicfiss) helpers */ +#ifndef CONFIG_USER_ONLY +DEF_HELPER_1(ssamoswap_disabled, void, env) +#endif diff --git a/target/riscv/insn_trans/trans_rvv.c.inc b/target/riscv/insn_trans/trans_rvv.c.inc index 71f98fb350..f4b5460340 100644 --- a/target/riscv/insn_trans/trans_rvv.c.inc +++ b/target/riscv/insn_trans/trans_rvv.c.inc @@ -3561,7 +3561,6 @@ static bool slideup_check(DisasContext *s, arg_rmrr *a) } GEN_OPIVX_TRANS(vslideup_vx, slideup_check) -GEN_OPIVX_TRANS(vslide1up_vx, slideup_check) GEN_OPIVI_TRANS(vslideup_vi, IMM_ZX, vslideup_vx, slideup_check) static bool slidedown_check(DisasContext *s, arg_rmrr *a) @@ -3572,9 +3571,56 @@ static bool slidedown_check(DisasContext *s, arg_rmrr *a) } GEN_OPIVX_TRANS(vslidedown_vx, slidedown_check) -GEN_OPIVX_TRANS(vslide1down_vx, slidedown_check) GEN_OPIVI_TRANS(vslidedown_vi, IMM_ZX, vslidedown_vx, slidedown_check) +typedef void gen_helper_vslide1_vx(TCGv_ptr, TCGv_ptr, TCGv_i64, TCGv_ptr, + TCGv_env, TCGv_i32); + +#define GEN_OPIVX_VSLIDE1_TRANS(NAME, CHECK) \ +static bool trans_##NAME(DisasContext *s, arg_rmrr *a) \ +{ \ + if (CHECK(s, a)) { \ + static gen_helper_vslide1_vx * const fns[4] = { \ + gen_helper_##NAME##_b, gen_helper_##NAME##_h, \ + gen_helper_##NAME##_w, gen_helper_##NAME##_d, \ + }; \ + \ + TCGv_ptr dest, src2, mask; \ + TCGv_i64 src1; \ + TCGv_i32 desc; \ + uint32_t data = 0; \ + \ + dest = tcg_temp_new_ptr(); \ + mask = tcg_temp_new_ptr(); \ + src2 = tcg_temp_new_ptr(); \ + src1 = tcg_temp_new_i64(); \ + \ + data = FIELD_DP32(data, VDATA, VM, a->vm); \ + data = FIELD_DP32(data, VDATA, LMUL, s->lmul); \ + data = FIELD_DP32(data, VDATA, VTA, s->vta); \ + data = FIELD_DP32(data, VDATA, VTA_ALL_1S, s->cfg_vta_all_1s); \ + data = FIELD_DP32(data, VDATA, VMA, s->vma); \ + desc = tcg_constant_i32(simd_desc(s->cfg_ptr->vlenb, \ + s->cfg_ptr->vlenb, data)); \ + \ + tcg_gen_addi_ptr(dest, tcg_env, vreg_ofs(s, a->rd)); \ + tcg_gen_addi_ptr(src2, tcg_env, vreg_ofs(s, a->rs2)); \ + tcg_gen_addi_ptr(mask, tcg_env, vreg_ofs(s, 0)); \ + tcg_gen_ext_tl_i64(src1, get_gpr(s, a->rs1, EXT_SIGN)); \ + \ + fns[s->sew](dest, mask, src1, src2, tcg_env, desc); \ + \ + tcg_gen_movi_tl(cpu_vstart, 0); \ + finalize_rvv_inst(s); \ + \ + return true; \ + } \ + return false; \ +} + +GEN_OPIVX_VSLIDE1_TRANS(vslide1up_vx, slideup_check) +GEN_OPIVX_VSLIDE1_TRANS(vslide1down_vx, slidedown_check) + /* Vector Floating-Point Slide Instructions */ static bool fslideup_check(DisasContext *s, arg_rmrr *a) { diff --git a/target/riscv/insn_trans/trans_rvzce.c.inc b/target/riscv/insn_trans/trans_rvzce.c.inc index c77c2b927b..dd15af0f54 100644 --- a/target/riscv/insn_trans/trans_rvzce.c.inc +++ b/target/riscv/insn_trans/trans_rvzce.c.inc @@ -88,13 +88,13 @@ static bool trans_c_lbu(DisasContext *ctx, arg_c_lbu *a) static bool trans_c_lhu(DisasContext *ctx, arg_c_lhu *a) { REQUIRE_ZCB(ctx); - return gen_load(ctx, a, MO_UW); + return gen_load(ctx, a, MO_TEUW); } static bool trans_c_lh(DisasContext *ctx, arg_c_lh *a) { REQUIRE_ZCB(ctx); - return gen_load(ctx, a, MO_SW); + return gen_load(ctx, a, MO_TESW); } static bool trans_c_sb(DisasContext *ctx, arg_c_sb *a) @@ -106,7 +106,7 @@ static bool trans_c_sb(DisasContext *ctx, arg_c_sb *a) static bool trans_c_sh(DisasContext *ctx, arg_c_sh *a) { REQUIRE_ZCB(ctx); - return gen_store(ctx, a, MO_UW); + return gen_store(ctx, a, MO_TEUW); } #define X_S0 8 diff --git a/target/riscv/insn_trans/trans_rvzicfiss.c.inc b/target/riscv/insn_trans/trans_rvzicfiss.c.inc index b0096adcd0..f4a1c12ca0 100644 --- a/target/riscv/insn_trans/trans_rvzicfiss.c.inc +++ b/target/riscv/insn_trans/trans_rvzicfiss.c.inc @@ -40,6 +40,7 @@ static bool trans_sspopchk(DisasContext *ctx, arg_sspopchk *a) tcg_gen_brcond_tl(TCG_COND_EQ, data, rs1, skip); tcg_gen_st_tl(tcg_constant_tl(RISCV_EXCP_SW_CHECK_BCFI_TVAL), tcg_env, offsetof(CPURISCVState, sw_check_code)); + gen_update_pc(ctx, 0); gen_helper_raise_exception(tcg_env, tcg_constant_i32(RISCV_EXCP_SW_CHECK)); gen_set_label(skip); @@ -90,7 +91,11 @@ static bool trans_ssamoswap_w(DisasContext *ctx, arg_amoswap_w *a) } if (!ctx->bcfi_enabled) { +#ifndef CONFIG_USER_ONLY + gen_helper_ssamoswap_disabled(tcg_env); +#else return false; +#endif } TCGv dest = dest_gpr(ctx, a->rd); @@ -115,7 +120,11 @@ static bool trans_ssamoswap_d(DisasContext *ctx, arg_amoswap_w *a) } if (!ctx->bcfi_enabled) { +#ifndef CONFIG_USER_ONLY + gen_helper_ssamoswap_disabled(tcg_env); +#else return false; +#endif } TCGv dest = dest_gpr(ctx, a->rd); diff --git a/target/riscv/kvm/kvm-cpu.c b/target/riscv/kvm/kvm-cpu.c index 5c19062c19..187c2c9501 100644 --- a/target/riscv/kvm/kvm-cpu.c +++ b/target/riscv/kvm/kvm-cpu.c @@ -1588,7 +1588,7 @@ static void kvm_riscv_handle_sbi_dbcn(CPUState *cs, struct kvm_run *run) * Handle the case where a 32 bit CPU is running in a * 64 bit addressing env. */ - if (riscv_cpu_mxl(&cpu->env) == MXL_RV32) { + if (riscv_cpu_is_32bit(cpu)) { addr |= (uint64_t)run->riscv_sbi.args[2] << 32; } diff --git a/target/riscv/machine.c b/target/riscv/machine.c index 1600ec44f0..18d790af0d 100644 --- a/target/riscv/machine.c +++ b/target/riscv/machine.c @@ -131,7 +131,8 @@ static bool vector_needed(void *opaque) RISCVCPU *cpu = opaque; CPURISCVState *env = &cpu->env; - return riscv_has_ext(env, RVV); + return kvm_enabled() ? riscv_has_ext(env, RVV) : + riscv_cpu_cfg(env)->ext_zve32x; } static const VMStateDescription vmstate_vector = { @@ -400,6 +401,30 @@ static const VMStateDescription vmstate_ssp = { } }; +static bool sstc_timer_needed(void *opaque) +{ + RISCVCPU *cpu = opaque; + CPURISCVState *env = &cpu->env; + + if (!cpu->cfg.ext_sstc) { + return false; + } + + return env->stimer != NULL || env->vstimer != NULL; +} + +static const VMStateDescription vmstate_sstc = { + .name = "cpu/timer", + .version_id = 1, + .minimum_version_id = 1, + .needed = sstc_timer_needed, + .fields = (const VMStateField[]) { + VMSTATE_TIMER_PTR(env.stimer, RISCVCPU), + VMSTATE_TIMER_PTR(env.vstimer, RISCVCPU), + VMSTATE_END_OF_LIST() + } +}; + const VMStateDescription vmstate_riscv_cpu = { .name = "cpu", .version_id = 10, @@ -476,6 +501,7 @@ const VMStateDescription vmstate_riscv_cpu = { &vmstate_elp, &vmstate_ssp, &vmstate_ctr, + &vmstate_sstc, NULL } }; diff --git a/target/riscv/op_helper.c b/target/riscv/op_helper.c index 110292e84d..8382aa94cb 100644 --- a/target/riscv/op_helper.c +++ b/target/riscv/op_helper.c @@ -717,4 +717,53 @@ target_ulong helper_hyp_hlvx_wu(CPURISCVState *env, target_ulong addr) return cpu_ldl_code_mmu(env, addr, oi, ra); } +void helper_ssamoswap_disabled(CPURISCVState *env) +{ + int exception = RISCV_EXCP_ILLEGAL_INST; + + /* + * Here we follow the RISC-V CFI spec [1] to implement the exception type + * of ssamoswap* instruction. + * + * [1] RISC-V CFI spec v1.0, ch2.7 Atomic Swap from a Shadow Stack Location + * + * Note: We have already checked some conditions in trans_* functions: + * 1. The effective priv mode is not M-mode. + * 2. The xSSE specific to the effictive priv mode is disabled. + */ + if (!get_field(env->menvcfg, MENVCFG_SSE)) { + /* + * Disabled M-mode SSE always trigger illegal instruction when + * current priv mode is not M-mode. + */ + exception = RISCV_EXCP_ILLEGAL_INST; + goto done; + } + + if (!riscv_has_ext(env, RVS)) { + /* S-mode is not implemented */ + exception = RISCV_EXCP_ILLEGAL_INST; + goto done; + } else if (env->virt_enabled) { + /* + * VU/VS-mode with disabled xSSE will trigger the virtual instruction + * exception. + */ + exception = RISCV_EXCP_VIRT_INSTRUCTION_FAULT; + goto done; + } else { + /* + * U-mode with disabled S-mode SSE will trigger the illegal instruction + * exception. + * + * Note: S-mode is already handled in the disabled M-mode SSE case. + */ + exception = RISCV_EXCP_ILLEGAL_INST; + goto done; + } + +done: + riscv_raise_exception(env, exception, GETPC()); +} + #endif /* !CONFIG_USER_ONLY */ diff --git a/target/riscv/riscv-qmp-cmds.c b/target/riscv/riscv-qmp-cmds.c index 8a1856c50e..c499f9b9a7 100644 --- a/target/riscv/riscv-qmp-cmds.c +++ b/target/riscv/riscv-qmp-cmds.c @@ -31,6 +31,10 @@ #include "qapi/qobject-input-visitor.h" #include "qapi/visitor.h" #include "qom/qom-qobject.h" +#include "qemu/ctype.h" +#include "qemu/qemu-print.h" +#include "monitor/hmp.h" +#include "monitor/hmp-target.h" #include "system/kvm.h" #include "system/tcg.h" #include "cpu-qom.h" @@ -240,3 +244,147 @@ CpuModelExpansionInfo *qmp_query_cpu_model_expansion(CpuModelExpansionType type, return expansion_info; } + +/* + * We have way too many potential CSRs and regs being added + * regularly to register them in a static array. + * + * Declare an empty array instead, making get_monitor_def() use + * the target_get_monitor_def() API directly. + */ +const MonitorDef monitor_defs[] = { { } }; +const MonitorDef *target_monitor_defs(void) +{ + return monitor_defs; +} + +static bool reg_is_ulong_integer(CPURISCVState *env, const char *name, + target_ulong *val, bool is_gprh) +{ + const char * const *reg_names; + target_ulong *vals; + + if (is_gprh) { + reg_names = riscv_int_regnamesh; + vals = env->gprh; + } else { + reg_names = riscv_int_regnames; + vals = env->gpr; + } + + for (int i = 0; i < 32; i++) { + g_autofree char *reg_name = g_strdup(reg_names[i]); + char *reg1 = strtok(reg_name, "/"); + char *reg2 = strtok(NULL, "/"); + + if (strcasecmp(reg1, name) == 0 || + (reg2 && strcasecmp(reg2, name) == 0)) { + *val = vals[i]; + return true; + } + } + + return false; +} + +static bool reg_is_u64_fpu(CPURISCVState *env, const char *name, uint64_t *val) +{ + if (qemu_tolower(name[0]) != 'f') { + return false; + } + + for (int i = 0; i < 32; i++) { + g_autofree char *reg_name = g_strdup(riscv_fpr_regnames[i]); + char *reg1 = strtok(reg_name, "/"); + char *reg2 = strtok(NULL, "/"); + + if (strcasecmp(reg1, name) == 0 || + (reg2 && strcasecmp(reg2, name) == 0)) { + *val = env->fpr[i]; + return true; + } + } + + return false; +} + +static bool reg_is_vreg(const char *name) +{ + if (qemu_tolower(name[0]) != 'v' || strlen(name) > 3) { + return false; + } + + for (int i = 0; i < 32; i++) { + if (strcasecmp(name, riscv_rvv_regnames[i]) == 0) { + return true; + } + } + + return false; +} + +int target_get_monitor_def(CPUState *cs, const char *name, uint64_t *pval) +{ + CPURISCVState *env = &RISCV_CPU(cs)->env; + target_ulong val = 0; + uint64_t val64 = 0; + int i; + + if (reg_is_ulong_integer(env, name, &val, false) || + reg_is_ulong_integer(env, name, &val, true)) { + *pval = val; + return 0; + } + + if (reg_is_u64_fpu(env, name, &val64)) { + *pval = val64; + return 0; + } + + if (reg_is_vreg(name)) { + if (!riscv_cpu_cfg(env)->ext_zve32x) { + return -EINVAL; + } + + qemu_printf("Unable to print the value of vector " + "vreg '%s' from this API\n", name); + + /* + * We're returning 0 because returning -EINVAL triggers + * an 'unknown register' message in exp_unary() later, + * which feels ankward after our own error message. + */ + *pval = 0; + return 0; + } + + for (i = 0; i < ARRAY_SIZE(csr_ops); i++) { + RISCVException res; + int csrno = i; + + /* + * Early skip when possible since we're going + * through a lot of NULL entries. + */ + if (csr_ops[csrno].predicate == NULL) { + continue; + } + + if (strcasecmp(csr_ops[csrno].name, name) != 0) { + continue; + } + + res = riscv_csrrw_debug(env, csrno, &val, 0, 0); + + /* + * Rely on the smode, hmode, etc, predicates within csr.c + * to do the filtering of the registers that are present. + */ + if (res == RISCV_EXCP_NONE) { + *pval = val; + return 0; + } + } + + return -EINVAL; +} diff --git a/target/riscv/tcg/tcg-cpu.c b/target/riscv/tcg/tcg-cpu.c index 143ab079d4..1150bd1469 100644 --- a/target/riscv/tcg/tcg-cpu.c +++ b/target/riscv/tcg/tcg-cpu.c @@ -417,12 +417,21 @@ static void riscv_cpu_validate_misa_priv(CPURISCVState *env, Error **errp) static void riscv_cpu_validate_v(CPURISCVState *env, RISCVCPUConfig *cfg, Error **errp) { + uint32_t min_vlen; uint32_t vlen = cfg->vlenb << 3; - if (vlen > RV_VLEN_MAX || vlen < 128) { + if (riscv_has_ext(env, RVV)) { + min_vlen = 128; + } else if (cfg->ext_zve64x) { + min_vlen = 64; + } else if (cfg->ext_zve32x) { + min_vlen = 32; + } + + if (vlen > RV_VLEN_MAX || vlen < min_vlen) { error_setg(errp, "Vector extension implementation only supports VLEN " - "in the range [128, %d]", RV_VLEN_MAX); + "in the range [%d, %d]", min_vlen, RV_VLEN_MAX); return; } @@ -432,6 +441,12 @@ static void riscv_cpu_validate_v(CPURISCVState *env, RISCVCPUConfig *cfg, "in the range [8, 64]"); return; } + + if (vlen < cfg->elen) { + error_setg(errp, "Vector extension implementation requires VLEN " + "to be greater than or equal to ELEN"); + return; + } } static void riscv_cpu_disable_priv_spec_isa_exts(RISCVCPU *cpu) @@ -661,7 +676,7 @@ void riscv_cpu_validate_set_extensions(RISCVCPU *cpu, Error **errp) return; } - if (riscv_has_ext(env, RVV)) { + if (cpu->cfg.ext_zve32x) { riscv_cpu_validate_v(env, &cpu->cfg, &local_err); if (local_err != NULL) { error_propagate(errp, local_err); diff --git a/target/riscv/translate.c b/target/riscv/translate.c index 9ddef2d6e2..6fc06c71f5 100644 --- a/target/riscv/translate.c +++ b/target/riscv/translate.c @@ -24,6 +24,7 @@ #include "exec/helper-gen.h" #include "exec/target_page.h" #include "exec/translator.h" +#include "accel/tcg/cpu-ldst.h" #include "exec/translation-block.h" #include "exec/log.h" #include "semihosting/semihost.h" @@ -1166,7 +1167,7 @@ static uint32_t opcode_at(DisasContextBase *dcbase, target_ulong pc) CPUState *cpu = ctx->cs; CPURISCVState *env = cpu_env(cpu); - return translator_ldl(env, &ctx->base, pc); + return cpu_ldl_code(env, pc); } #define SS_MMU_INDEX(ctx) (ctx->mem_idx | MMU_IDX_SS_WRITE) diff --git a/target/riscv/vector_helper.c b/target/riscv/vector_helper.c index 7c67d67a13..41ea223106 100644 --- a/target/riscv/vector_helper.c +++ b/target/riscv/vector_helper.c @@ -5198,11 +5198,11 @@ GEN_VEXT_VSLIE1UP(16, H2) GEN_VEXT_VSLIE1UP(32, H4) GEN_VEXT_VSLIE1UP(64, H8) -#define GEN_VEXT_VSLIDE1UP_VX(NAME, BITWIDTH) \ -void HELPER(NAME)(void *vd, void *v0, target_ulong s1, void *vs2, \ - CPURISCVState *env, uint32_t desc) \ -{ \ - vslide1up_##BITWIDTH(vd, v0, s1, vs2, env, desc); \ +#define GEN_VEXT_VSLIDE1UP_VX(NAME, BITWIDTH) \ +void HELPER(NAME)(void *vd, void *v0, uint64_t s1, void *vs2, \ + CPURISCVState *env, uint32_t desc) \ +{ \ + vslide1up_##BITWIDTH(vd, v0, s1, vs2, env, desc); \ } /* vslide1up.vx vd, vs2, rs1, vm # vd[0]=x[rs1], vd[i+1] = vs2[i] */ @@ -5249,11 +5249,11 @@ GEN_VEXT_VSLIDE1DOWN(16, H2) GEN_VEXT_VSLIDE1DOWN(32, H4) GEN_VEXT_VSLIDE1DOWN(64, H8) -#define GEN_VEXT_VSLIDE1DOWN_VX(NAME, BITWIDTH) \ -void HELPER(NAME)(void *vd, void *v0, target_ulong s1, void *vs2, \ - CPURISCVState *env, uint32_t desc) \ -{ \ - vslide1down_##BITWIDTH(vd, v0, s1, vs2, env, desc); \ +#define GEN_VEXT_VSLIDE1DOWN_VX(NAME, BITWIDTH) \ +void HELPER(NAME)(void *vd, void *v0, uint64_t s1, void *vs2, \ + CPURISCVState *env, uint32_t desc) \ +{ \ + vslide1down_##BITWIDTH(vd, v0, s1, vs2, env, desc); \ } /* vslide1down.vx vd, vs2, rs1, vm # vd[i] = vs2[i+1], vd[vl-1]=x[rs1] */ diff --git a/tcg/region.c b/tcg/region.c index 7ea0b37a84..2181267e48 100644 --- a/tcg/region.c +++ b/tcg/region.c @@ -832,13 +832,16 @@ void tcg_region_init(size_t tb_size, int splitwx, unsigned max_threads) } else { #ifdef CONFIG_POSIX rc = mprotect(start, end - start, need_prot); + if (rc) { + error_report("mprotect of jit buffer: %s", + strerror(errno)); + } #else g_assert_not_reached(); #endif } if (rc) { - error_setg_errno(&error_fatal, errno, - "mprotect of jit buffer"); + exit(1); } } if (have_prot != 0) { diff --git a/tests/qtest/bios-tables-test.c b/tests/qtest/bios-tables-test.c index 4fa8ac5096..6b892ef23e 100644 --- a/tests/qtest/bios-tables-test.c +++ b/tests/qtest/bios-tables-test.c @@ -2864,7 +2864,7 @@ int main(int argc, char *argv[]) test_acpi_riscv64_virt_tcg_acpi_spcr); } } else if (strcmp(arch, "loongarch64") == 0) { - if (has_tcg) { + if (has_tcg && qtest_has_machine("virt")) { qtest_add_func("acpi/virt", test_acpi_loongarch64_virt); qtest_add_func("acpi/virt/topology", test_acpi_loongarch64_virt_topology); diff --git a/tests/qtest/cpu-plug-test.c b/tests/qtest/cpu-plug-test.c index 44d704680b..0aa4ccc5b6 100644 --- a/tests/qtest/cpu-plug-test.c +++ b/tests/qtest/cpu-plug-test.c @@ -190,7 +190,7 @@ int main(int argc, char **argv) qtest_cb_for_every_machine(add_pseries_test_case, g_test_quick()); } else if (g_str_equal(arch, "s390x")) { qtest_cb_for_every_machine(add_s390x_test_case, g_test_quick()); - } else if (g_str_equal(arch, "loongarch64")) { + } else if (g_str_equal(arch, "loongarch64") && qtest_has_machine("virt")) { add_loongarch_test_case("virt"); } diff --git a/tests/qtest/libqtest.c b/tests/qtest/libqtest.c index 94526b7f9c..933d085869 100644 --- a/tests/qtest/libqtest.c +++ b/tests/qtest/libqtest.c @@ -357,7 +357,7 @@ void qtest_remove_abrt_handler(void *data) } } -static const char *qtest_qemu_binary(const char *var) +const char *qtest_qemu_binary(const char *var) { const char *qemu_bin; @@ -409,30 +409,30 @@ static pid_t qtest_create_process(char *cmd) } #endif /* _WIN32 */ -static QTestState *G_GNUC_PRINTF(2, 3) qtest_spawn_qemu(const char *qemu_bin, - const char *fmt, ...) +static QTestState *qtest_create_test_state(int pid) { - va_list ap; QTestState *s = g_new0(QTestState, 1); - const char *trace = g_getenv("QTEST_TRACE"); - g_autofree char *tracearg = trace ? - g_strdup_printf("-trace %s ", trace) : g_strdup(""); - g_autoptr(GString) command = g_string_new(""); - - va_start(ap, fmt); - g_string_append_printf(command, CMD_EXEC "%s %s", qemu_bin, tracearg); - g_string_append_vprintf(command, fmt, ap); - va_end(ap); + s->qemu_pid = pid; qtest_add_abrt_handler(kill_qemu_hook_func, s); + return s; +} + +static QTestState *qtest_spawn_qemu(const char *qemu_bin, const char *args, + void *opaque) +{ + int pid; + g_autoptr(GString) command = g_string_new(""); + + g_string_printf(command, CMD_EXEC "%s %s", qemu_bin, args); if (!silence_spawn_log) { g_test_message("starting QEMU: %s", command->str); } #ifndef _WIN32 - s->qemu_pid = fork(); - if (s->qemu_pid == 0) { + pid = fork(); + if (pid == 0) { #ifdef __linux__ /* * Although we register a ABRT handler to kill off QEMU @@ -455,10 +455,10 @@ static QTestState *G_GNUC_PRINTF(2, 3) qtest_spawn_qemu(const char *qemu_bin, exit(1); } #else - s->qemu_pid = qtest_create_process(command->str); + pid = qtest_create_process(command->str); #endif /* _WIN32 */ - return s; + return qtest_create_test_state(pid); } static char *qtest_socket_path(const char *suffix) @@ -466,14 +466,48 @@ static char *qtest_socket_path(const char *suffix) return g_strdup_printf("%s/qtest-%d.%s", g_get_tmp_dir(), getpid(), suffix); } +gchar *qtest_qemu_args(const char *extra_args) +{ + g_autofree gchar *socket_path = qtest_socket_path("sock"); + g_autofree gchar *qmp_socket_path = qtest_socket_path("qmp"); + const char *trace = g_getenv("QTEST_TRACE"); + g_autofree char *tracearg = trace ? g_strdup_printf("-trace %s ", trace) : + g_strdup(""); + gchar *args = g_strdup_printf( + "%s" + "-qtest unix:%s " + "-qtest-log %s " + "-chardev socket,path=%s,id=char0 " + "-mon chardev=char0,mode=control " + "-display none " + "-audio none " + "%s" + " -accel qtest", + + tracearg, + socket_path, + getenv("QTEST_LOG") ? DEV_STDERR : DEV_NULL, + qmp_socket_path, + extra_args ?: ""); + + return args; +} + +typedef QTestState *(*qtest_qemu_spawn_func)(const char *qemu_bin, + const char *extra_args, + void *opaque); + static QTestState *qtest_init_internal(const char *qemu_bin, const char *extra_args, - bool do_connect) + bool do_connect, + qtest_qemu_spawn_func spawn, + void *opaque) { QTestState *s; int sock, qmpsock, i; g_autofree gchar *socket_path = qtest_socket_path("sock"); g_autofree gchar *qmp_socket_path = qtest_socket_path("qmp"); + g_autofree gchar *args = qtest_qemu_args(extra_args); /* * It's possible that if an earlier test run crashed it might @@ -488,19 +522,7 @@ static QTestState *qtest_init_internal(const char *qemu_bin, sock = init_socket(socket_path); qmpsock = init_socket(qmp_socket_path); - s = qtest_spawn_qemu(qemu_bin, - "-qtest unix:%s " - "-qtest-log %s " - "-chardev socket,path=%s,id=char0 " - "-mon chardev=char0,mode=control " - "-display none " - "-audio none " - "%s" - " -accel qtest", - socket_path, - getenv("QTEST_LOG") ? DEV_STDERR : DEV_NULL, - qmp_socket_path, - extra_args ?: ""); + s = spawn(qemu_bin, args, opaque); qtest_client_set_rx_handler(s, qtest_client_socket_recv_line); qtest_client_set_tx_handler(s, qtest_client_socket_send); @@ -555,7 +577,8 @@ void qtest_connect(QTestState *s) QTestState *qtest_init_without_qmp_handshake(const char *extra_args) { - return qtest_init_internal(qtest_qemu_binary(NULL), extra_args, true); + return qtest_init_internal(qtest_qemu_binary(NULL), extra_args, true, + qtest_spawn_qemu, NULL); } void qtest_qmp_handshake(QTestState *s, QList *capabilities) @@ -578,7 +601,7 @@ QTestState *qtest_init_ext(const char *var, const char *extra_args, QList *capabilities, bool do_connect) { QTestState *s = qtest_init_internal(qtest_qemu_binary(var), extra_args, - do_connect); + do_connect, qtest_spawn_qemu, NULL); if (do_connect) { qtest_qmp_handshake(s, capabilities); @@ -592,6 +615,25 @@ QTestState *qtest_init_ext(const char *var, const char *extra_args, return s; } +static QTestState *qtest_attach_qemu(const char *qemu_bin, + const char *extra_args, + void *opaque) +{ + int pid = *(int *)opaque; + return qtest_create_test_state(pid); +} + +QTestState *qtest_init_after_exec(QTestState *qts) +{ + void *opaque = (void *)&qts->qemu_pid; + QTestState *s; + + s = qtest_init_internal(NULL, NULL, true, qtest_attach_qemu, opaque); + qts->qemu_pid = -1; + qtest_qmp_handshake(s, NULL); + return s; +} + QTestState *qtest_init(const char *extra_args) { return qtest_init_ext(NULL, extra_args, NULL, true); @@ -1630,7 +1672,8 @@ static void qtest_free_machine_list(struct MachInfo *machines) static struct MachInfo *qtest_get_machines(const char *var) { static struct MachInfo *machines; - static char *qemu_var; + static char *qemu_bin; + const char *new_qemu_bin; QDict *response, *minfo; QList *list; const QListEntry *p; @@ -1639,9 +1682,10 @@ static struct MachInfo *qtest_get_machines(const char *var) QTestState *qts; int idx; - if (g_strcmp0(qemu_var, var)) { - g_free(qemu_var); - qemu_var = g_strdup(var); + new_qemu_bin = qtest_qemu_binary(var); + if (g_strcmp0(qemu_bin, new_qemu_bin)) { + g_free(qemu_bin); + qemu_bin = g_strdup(new_qemu_bin); /* new qemu, clear the cache */ qtest_free_machine_list(machines); @@ -1654,7 +1698,7 @@ static struct MachInfo *qtest_get_machines(const char *var) silence_spawn_log = !g_test_verbose(); - qts = qtest_init_ext(qemu_var, "-machine none", NULL, true); + qts = qtest_init_ext(var, "-machine none", NULL, true); response = qtest_qmp(qts, "{ 'execute': 'query-machines' }"); g_assert(response); list = qdict_get_qlist(response, "return"); diff --git a/tests/qtest/libqtest.h b/tests/qtest/libqtest.h index fd27521a9c..9c118c89ca 100644 --- a/tests/qtest/libqtest.h +++ b/tests/qtest/libqtest.h @@ -48,6 +48,31 @@ QTestState *qtest_initf(const char *fmt, ...) G_GNUC_PRINTF(1, 2); QTestState *qtest_vinitf(const char *fmt, va_list ap) G_GNUC_PRINTF(1, 0); /** + * qtest_qemu_binary: + * @var: environment variable name + * + * Look up @var and return its value as the qemu binary path. + * If @var is NULL, look up the default var name. + */ +const char *qtest_qemu_binary(const char *var); + +/** + * qtest_init_after_exec: + * @qts: the previous QEMU state + * + * Return a test state representing new QEMU after @qts exec's it. + */ +QTestState *qtest_init_after_exec(QTestState *qts); + +/** + * qtest_qemu_args: + * @extra_args: Other arguments to pass to QEMU. + * + * Return the command line used to start QEMU, sans binary. + */ +gchar *qtest_qemu_args(const char *extra_args); + +/** * qtest_init: * @extra_args: other arguments to pass to QEMU. CAUTION: these * arguments are subject to word splitting and shell evaluation. diff --git a/tests/qtest/migration/bootfile.c b/tests/qtest/migration/bootfile.c index fac059d11d..479c43231d 100644 --- a/tests/qtest/migration/bootfile.c +++ b/tests/qtest/migration/bootfile.c @@ -68,3 +68,8 @@ char *bootfile_create(const char *arch, const char *dir, bool suspend_me) return bootpath; } + +char *bootfile_get(void) +{ + return bootpath; +} diff --git a/tests/qtest/migration/bootfile.h b/tests/qtest/migration/bootfile.h index 6d6a67386e..96e784b163 100644 --- a/tests/qtest/migration/bootfile.h +++ b/tests/qtest/migration/bootfile.h @@ -35,5 +35,6 @@ void bootfile_delete(void); char *bootfile_create(const char *arch, const char *dir, bool suspend_me); +char *bootfile_get(void); #endif /* BOOTFILE_H */ diff --git a/tests/qtest/migration/cpr-tests.c b/tests/qtest/migration/cpr-tests.c index 5e764a6787..c4ce60ff66 100644 --- a/tests/qtest/migration/cpr-tests.c +++ b/tests/qtest/migration/cpr-tests.c @@ -97,7 +97,10 @@ static void test_mode_transfer_common(bool incoming_defer) .start_hook = test_mode_transfer_start, }; - test_precopy_common(&args); + if (test_precopy_common(&args) < 0) { + close(cpr_sockfd); + unlink(cpr_path); + } } static void test_mode_transfer(void) diff --git a/tests/qtest/migration/framework.c b/tests/qtest/migration/framework.c index 407c9023c0..a9be9c2dbf 100644 --- a/tests/qtest/migration/framework.c +++ b/tests/qtest/migration/framework.c @@ -234,7 +234,7 @@ static void migrate_start_set_capabilities(QTestState *from, QTestState *to, * to mimic as closer as that. */ migrate_set_capability(from, "events", true); - if (!args->defer_target_connect) { + if (!args->defer_target_connect && to) { migrate_set_capability(to, "events", true); } @@ -246,20 +246,26 @@ static void migrate_start_set_capabilities(QTestState *from, QTestState *to, if (args->caps[MIGRATION_CAPABILITY_MULTIFD]) { migrate_set_parameter_int(from, "multifd-channels", MULTIFD_TEST_CHANNELS); - migrate_set_parameter_int(to, "multifd-channels", - MULTIFD_TEST_CHANNELS); + if (to) { + migrate_set_parameter_int(to, "multifd-channels", + MULTIFD_TEST_CHANNELS); + } } return; } -int migrate_start(QTestState **from, QTestState **to, const char *uri, - MigrateStart *args) +static char *test_shmem_path(void) +{ + return g_strdup_printf("/dev/shm/qemu-%d", getpid()); +} + +int migrate_args(char **from, char **to, const char *uri, MigrateStart *args) { /* options for source and target */ g_autofree gchar *arch_opts = NULL; - g_autofree gchar *cmd_source = NULL; - g_autofree gchar *cmd_target = NULL; + gchar *cmd_source = NULL; + gchar *cmd_target = NULL; const gchar *ignore_stderr; g_autofree char *shmem_opts = NULL; g_autofree char *shmem_path = NULL; @@ -268,23 +274,10 @@ int migrate_start(QTestState **from, QTestState **to, const char *uri, const char *memory_size; const char *machine_alias, *machine_opts = ""; g_autofree char *machine = NULL; - const char *bootpath; - g_autoptr(QList) capabilities = migrate_start_get_qmp_capabilities(args); + const char *bootpath = bootfile_get(); g_autofree char *memory_backend = NULL; const char *events; - if (args->use_shmem) { - if (!g_file_test("/dev/shm", G_FILE_TEST_IS_DIR)) { - g_test_skip("/dev/shm is not supported"); - return -1; - } - } - - dst_state = (QTestMigrationState) { }; - src_state = (QTestMigrationState) { }; - bootpath = bootfile_create(arch, tmpfs, args->suspend_me); - src_state.suspend_me = args->suspend_me; - if (strcmp(arch, "i386") == 0 || strcmp(arch, "x86_64") == 0) { memory_size = "150M"; @@ -340,7 +333,7 @@ int migrate_start(QTestState **from, QTestState **to, const char *uri, } if (args->use_shmem) { - shmem_path = g_strdup_printf("/dev/shm/qemu-%d", getpid()); + shmem_path = test_shmem_path(); shmem_opts = g_strdup_printf( "-object memory-backend-file,id=mem0,size=%s" ",mem-path=%s,share=on -numa node,memdev=mem0", @@ -381,12 +374,6 @@ int migrate_start(QTestState **from, QTestState **to, const char *uri, shmem_opts ? shmem_opts : "", args->opts_source ? args->opts_source : "", ignore_stderr); - if (!args->only_target) { - *from = qtest_init_ext(QEMU_ENV_SRC, cmd_source, capabilities, true); - qtest_qmp_set_event_callback(*from, - migrate_watch_for_events, - &src_state); - } /* * If the monitor connection is deferred, enable events on the command line @@ -410,21 +397,62 @@ int migrate_start(QTestState **from, QTestState **to, const char *uri, shmem_opts ? shmem_opts : "", args->opts_target ? args->opts_target : "", ignore_stderr); - *to = qtest_init_ext(QEMU_ENV_DST, cmd_target, capabilities, - !args->defer_target_connect); - qtest_qmp_set_event_callback(*to, - migrate_watch_for_events, - &dst_state); + + *from = cmd_source; + *to = cmd_target; + return 0; +} + +int migrate_start(QTestState **from, QTestState **to, const char *uri, + MigrateStart *args) +{ + g_autofree gchar *cmd_source = NULL; + g_autofree gchar *cmd_target = NULL; + g_autoptr(QList) capabilities = migrate_start_get_qmp_capabilities(args); + + if (args->use_shmem) { + if (!g_file_test("/dev/shm", G_FILE_TEST_IS_DIR)) { + g_test_skip("/dev/shm is not supported"); + return -1; + } + } + + dst_state = (QTestMigrationState) { }; + src_state = (QTestMigrationState) { }; + bootfile_create(qtest_get_arch(), tmpfs, args->suspend_me); + src_state.suspend_me = args->suspend_me; + + if (migrate_args(&cmd_source, &cmd_target, uri, args)) { + return -1; + } + + if (!args->only_target) { + *from = qtest_init_ext(QEMU_ENV_SRC, cmd_source, capabilities, true); + qtest_qmp_set_event_callback(*from, + migrate_watch_for_events, + &src_state); + } + + if (!args->only_source) { + *to = qtest_init_ext(QEMU_ENV_DST, cmd_target, capabilities, + !args->defer_target_connect); + qtest_qmp_set_event_callback(*to, + migrate_watch_for_events, + &dst_state); + } /* * Remove shmem file immediately to avoid memory leak in test failed case. * It's valid because QEMU has already opened this file */ if (args->use_shmem) { + g_autofree char *shmem_path = test_shmem_path(); unlink(shmem_path); } - migrate_start_set_capabilities(*from, *to, args); + migrate_start_set_capabilities(*from, + args->only_source ? NULL : *to, + args); return 0; } @@ -736,7 +764,7 @@ void test_postcopy_recovery_common(MigrateCommon *args) migrate_postcopy_complete(from, to, args); } -void test_precopy_common(MigrateCommon *args) +int test_precopy_common(MigrateCommon *args) { QTestState *from, *to; void *data_hook = NULL; @@ -746,7 +774,7 @@ void test_precopy_common(MigrateCommon *args) g_assert(!args->cpr_channel || args->connect_channels); if (migrate_start(&from, &to, args->listen_uri, &args->start)) { - return; + return -1; } if (args->start_hook) { @@ -869,6 +897,8 @@ finish: } migrate_end(from, to, args->result == MIG_TEST_SUCCEED); + + return 0; } static void file_dirty_offset_region(void) @@ -994,6 +1024,11 @@ QTestMigrationState *get_src(void) return &src_state; } +QTestMigrationState *get_dst(void) +{ + return &dst_state; +} + MigrationTestEnv *migration_get_env(void) { static MigrationTestEnv *env; diff --git a/tests/qtest/migration/framework.h b/tests/qtest/migration/framework.h index 01e425e64e..9bb584a6bb 100644 --- a/tests/qtest/migration/framework.h +++ b/tests/qtest/migration/framework.h @@ -103,6 +103,8 @@ typedef struct { */ bool hide_stderr; bool use_shmem; + /* only launch the source process */ + bool only_source; /* only launch the target process */ bool only_target; /* Use dirty ring if true; dirty logging otherwise */ @@ -221,13 +223,15 @@ typedef struct { void wait_for_serial(const char *side); void migrate_prepare_for_dirty_mem(QTestState *from); void migrate_wait_for_dirty_mem(QTestState *from, QTestState *to); + +int migrate_args(char **from, char **to, const char *uri, MigrateStart *args); int migrate_start(QTestState **from, QTestState **to, const char *uri, MigrateStart *args); void migrate_end(QTestState *from, QTestState *to, bool test_dest); void test_postcopy_common(MigrateCommon *args); void test_postcopy_recovery_common(MigrateCommon *args); -void test_precopy_common(MigrateCommon *args); +int test_precopy_common(MigrateCommon *args); void test_file_common(MigrateCommon *args, bool stop_src); void *migrate_hook_start_precopy_tcp_multifd_common(QTestState *from, QTestState *to, @@ -235,6 +239,7 @@ void *migrate_hook_start_precopy_tcp_multifd_common(QTestState *from, typedef struct QTestMigrationState QTestMigrationState; QTestMigrationState *get_src(void); +QTestMigrationState *get_dst(void); #ifdef CONFIG_GNUTLS void migration_test_add_tls(MigrationTestEnv *env); diff --git a/tests/qtest/migration/migration-qmp.c b/tests/qtest/migration/migration-qmp.c index 66dd369ba7..c803fcee9d 100644 --- a/tests/qtest/migration/migration-qmp.c +++ b/tests/qtest/migration/migration-qmp.c @@ -442,6 +442,22 @@ void migrate_set_parameter_str(QTestState *who, const char *parameter, migrate_check_parameter_str(who, parameter, value); } +void migrate_set_parameter_strv(QTestState *who, const char *parameter, + char **strv) +{ + g_autofree char *args = g_strjoinv("\",\"", strv); + g_autoptr(GString) value = g_string_new(""); + g_autofree char *command = NULL; + + g_string_printf(value, "\"%s\"", args); + + command = g_strdup_printf("{ 'execute': 'migrate-set-parameters'," + "'arguments': { %%s: [ %s ]}}", + value->str); + + qtest_qmp_assert_success(who, command, parameter); +} + static long long migrate_get_parameter_bool(QTestState *who, const char *parameter) { diff --git a/tests/qtest/migration/migration-qmp.h b/tests/qtest/migration/migration-qmp.h index faa8181d91..44482d250f 100644 --- a/tests/qtest/migration/migration-qmp.h +++ b/tests/qtest/migration/migration-qmp.h @@ -34,6 +34,8 @@ void read_blocktime(QTestState *who); void wait_for_migration_pass(QTestState *who, QTestMigrationState *src_state); void migrate_set_parameter_str(QTestState *who, const char *parameter, const char *value); +void migrate_set_parameter_strv(QTestState *who, const char *parameter, + char **strv); void migrate_set_parameter_bool(QTestState *who, const char *parameter, int value); void migrate_ensure_non_converge(QTestState *who); diff --git a/tests/qtest/riscv-csr-test.c b/tests/qtest/riscv-csr-test.c index ff5c29e6c6..bb1b0ffed3 100644 --- a/tests/qtest/riscv-csr-test.c +++ b/tests/qtest/riscv-csr-test.c @@ -50,7 +50,9 @@ int main(int argc, char **argv) { g_test_init(&argc, &argv, NULL); - qtest_add_func("/cpu/csr", run_test_csr); + if (qtest_has_machine("virt")) { + qtest_add_func("/cpu/csr", run_test_csr); + } return g_test_run(); } diff --git a/tests/tracetool/ftrace.h b/tests/tracetool/ftrace.h index fe22ea0f09..1dfe423941 100644 --- a/tests/tracetool/ftrace.h +++ b/tests/tracetool/ftrace.h @@ -21,18 +21,10 @@ extern uint16_t _TRACE_TEST_WIBBLE_DSTATE; static inline void trace_test_blah(void *context, const char *filename) { - { - char ftrace_buf[MAX_TRACE_STRLEN]; - int unused __attribute__ ((unused)); - int trlen; - if (trace_event_get_state(TRACE_TEST_BLAH)) { + if (trace_event_get_state(TRACE_TEST_BLAH)) { #line 4 "trace-events" - trlen = snprintf(ftrace_buf, MAX_TRACE_STRLEN, - "test_blah " "Blah context=%p filename=%s" "\n" , context, filename); -#line 33 "ftrace.h" - trlen = MIN(trlen, MAX_TRACE_STRLEN - 1); - unused = write(trace_marker_fd, ftrace_buf, trlen); - } + ftrace_write("test_blah " "Blah context=%p filename=%s" "\n" , context, filename); +#line 28 "ftrace.h" } } @@ -42,18 +34,10 @@ static inline void trace_test_blah(void *context, const char *filename) static inline void trace_test_wibble(void *context, int value) { - { - char ftrace_buf[MAX_TRACE_STRLEN]; - int unused __attribute__ ((unused)); - int trlen; - if (trace_event_get_state(TRACE_TEST_WIBBLE)) { + if (trace_event_get_state(TRACE_TEST_WIBBLE)) { #line 5 "trace-events" - trlen = snprintf(ftrace_buf, MAX_TRACE_STRLEN, - "test_wibble " "Wibble context=%p value=%d" "\n" , context, value); -#line 54 "ftrace.h" - trlen = MIN(trlen, MAX_TRACE_STRLEN - 1); - unused = write(trace_marker_fd, ftrace_buf, trlen); - } + ftrace_write("test_wibble " "Wibble context=%p value=%d" "\n" , context, value); +#line 41 "ftrace.h" } } #endif /* TRACE_TESTSUITE_GENERATED_TRACERS_H */ diff --git a/tests/tracetool/ftrace.rs b/tests/tracetool/ftrace.rs new file mode 100644 index 0000000000..07b9259cf2 --- /dev/null +++ b/tests/tracetool/ftrace.rs @@ -0,0 +1,40 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +// This file is @generated by tracetool, do not edit. + +#[allow(unused_imports)] +use std::ffi::c_char; +#[allow(unused_imports)] +use util::bindings; + +#[inline(always)] +fn trace_event_state_is_enabled(dstate: u16) -> bool { + (unsafe { trace_events_enabled_count }) != 0 && dstate != 0 +} + +extern "C" { + static mut trace_events_enabled_count: u32; +} +extern "C" { + static mut _TRACE_TEST_BLAH_DSTATE: u16; + static mut _TRACE_TEST_WIBBLE_DSTATE: u16; +} + +#[inline(always)] +#[allow(dead_code)] +pub fn trace_test_blah(_context: *mut (), _filename: &std::ffi::CStr) +{ + if trace_event_state_is_enabled(unsafe { _TRACE_TEST_BLAH_DSTATE}) { + let format_string = c"Blah context=%p filename=%s"; + unsafe {bindings::ftrace_write(format_string.as_ptr() as *const c_char, _context /* as *mut () */, _filename.as_ptr());} + } +} + +#[inline(always)] +#[allow(dead_code)] +pub fn trace_test_wibble(_context: *mut (), _value: std::ffi::c_int) +{ + if trace_event_state_is_enabled(unsafe { _TRACE_TEST_WIBBLE_DSTATE}) { + let format_string = c"Wibble context=%p value=%d"; + unsafe {bindings::ftrace_write(format_string.as_ptr() as *const c_char, _context /* as *mut () */, _value /* as std::ffi::c_int */);} + } +} diff --git a/tests/tracetool/log.h b/tests/tracetool/log.h index edcc7f9d47..c7795871f8 100644 --- a/tests/tracetool/log.h +++ b/tests/tracetool/log.h @@ -21,10 +21,12 @@ extern uint16_t _TRACE_TEST_WIBBLE_DSTATE; static inline void trace_test_blah(void *context, const char *filename) { - if (trace_event_get_state(TRACE_TEST_BLAH) && qemu_loglevel_mask(LOG_TRACE)) { + if (trace_event_get_state(TRACE_TEST_BLAH)) { + if (qemu_loglevel_mask(LOG_TRACE)) { #line 4 "trace-events" - qemu_log("test_blah " "Blah context=%p filename=%s" "\n", context, filename); -#line 28 "log.h" + qemu_log("test_blah " "Blah context=%p filename=%s" "\n", context, filename); +#line 29 "log.h" + } } } @@ -34,10 +36,12 @@ static inline void trace_test_blah(void *context, const char *filename) static inline void trace_test_wibble(void *context, int value) { - if (trace_event_get_state(TRACE_TEST_WIBBLE) && qemu_loglevel_mask(LOG_TRACE)) { + if (trace_event_get_state(TRACE_TEST_WIBBLE)) { + if (qemu_loglevel_mask(LOG_TRACE)) { #line 5 "trace-events" - qemu_log("test_wibble " "Wibble context=%p value=%d" "\n", context, value); -#line 41 "log.h" + qemu_log("test_wibble " "Wibble context=%p value=%d" "\n", context, value); +#line 44 "log.h" + } } } #endif /* TRACE_TESTSUITE_GENERATED_TRACERS_H */ diff --git a/tests/tracetool/log.rs b/tests/tracetool/log.rs new file mode 100644 index 0000000000..c191895c8f --- /dev/null +++ b/tests/tracetool/log.rs @@ -0,0 +1,44 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +// This file is @generated by tracetool, do not edit. + +#[allow(unused_imports)] +use std::ffi::c_char; +#[allow(unused_imports)] +use util::bindings; + +#[inline(always)] +fn trace_event_state_is_enabled(dstate: u16) -> bool { + (unsafe { trace_events_enabled_count }) != 0 && dstate != 0 +} + +extern "C" { + static mut trace_events_enabled_count: u32; +} +extern "C" { + static mut _TRACE_TEST_BLAH_DSTATE: u16; + static mut _TRACE_TEST_WIBBLE_DSTATE: u16; +} + +#[inline(always)] +#[allow(dead_code)] +pub fn trace_test_blah(_context: *mut (), _filename: &std::ffi::CStr) +{ + if trace_event_state_is_enabled(unsafe { _TRACE_TEST_BLAH_DSTATE}) { + let format_string = c"test_blah Blah context=%p filename=%s\n"; + if (unsafe { bindings::qemu_loglevel } & bindings::LOG_TRACE) != 0 { + unsafe { bindings::qemu_log(format_string.as_ptr() as *const c_char, _context /* as *mut () */, _filename.as_ptr());} + } + } +} + +#[inline(always)] +#[allow(dead_code)] +pub fn trace_test_wibble(_context: *mut (), _value: std::ffi::c_int) +{ + if trace_event_state_is_enabled(unsafe { _TRACE_TEST_WIBBLE_DSTATE}) { + let format_string = c"test_wibble Wibble context=%p value=%d\n"; + if (unsafe { bindings::qemu_loglevel } & bindings::LOG_TRACE) != 0 { + unsafe { bindings::qemu_log(format_string.as_ptr() as *const c_char, _context /* as *mut () */, _value /* as std::ffi::c_int */);} + } + } +} diff --git a/tests/tracetool/simple.rs b/tests/tracetool/simple.rs new file mode 100644 index 0000000000..9ee39495e3 --- /dev/null +++ b/tests/tracetool/simple.rs @@ -0,0 +1,40 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +// This file is @generated by tracetool, do not edit. + +#[allow(unused_imports)] +use std::ffi::c_char; +#[allow(unused_imports)] +use util::bindings; + +#[inline(always)] +fn trace_event_state_is_enabled(dstate: u16) -> bool { + (unsafe { trace_events_enabled_count }) != 0 && dstate != 0 +} + +extern "C" { + static mut trace_events_enabled_count: u32; +} +extern "C" { + static mut _TRACE_TEST_BLAH_DSTATE: u16; + static mut _TRACE_TEST_WIBBLE_DSTATE: u16; +} + +#[inline(always)] +#[allow(dead_code)] +pub fn trace_test_blah(_context: *mut (), _filename: &std::ffi::CStr) +{ + if trace_event_state_is_enabled(unsafe { _TRACE_TEST_BLAH_DSTATE}) { + extern "C" { fn _simple_trace_test_blah(_context: *mut (), _filename: *const std::ffi::c_char); } + unsafe { _simple_trace_test_blah(_context, _filename.as_ptr()); } + } +} + +#[inline(always)] +#[allow(dead_code)] +pub fn trace_test_wibble(_context: *mut (), _value: std::ffi::c_int) +{ + if trace_event_state_is_enabled(unsafe { _TRACE_TEST_WIBBLE_DSTATE}) { + extern "C" { fn _simple_trace_test_wibble(_context: *mut (), _value: std::ffi::c_int); } + unsafe { _simple_trace_test_wibble(_context, _value); } + } +} diff --git a/tests/tracetool/syslog.rs b/tests/tracetool/syslog.rs new file mode 100644 index 0000000000..9d3675a0b5 --- /dev/null +++ b/tests/tracetool/syslog.rs @@ -0,0 +1,40 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +// This file is @generated by tracetool, do not edit. + +#[allow(unused_imports)] +use std::ffi::c_char; +#[allow(unused_imports)] +use util::bindings; + +#[inline(always)] +fn trace_event_state_is_enabled(dstate: u16) -> bool { + (unsafe { trace_events_enabled_count }) != 0 && dstate != 0 +} + +extern "C" { + static mut trace_events_enabled_count: u32; +} +extern "C" { + static mut _TRACE_TEST_BLAH_DSTATE: u16; + static mut _TRACE_TEST_WIBBLE_DSTATE: u16; +} + +#[inline(always)] +#[allow(dead_code)] +pub fn trace_test_blah(_context: *mut (), _filename: &std::ffi::CStr) +{ + if trace_event_state_is_enabled(unsafe { _TRACE_TEST_BLAH_DSTATE}) { + let format_string = c"Blah context=%p filename=%s"; + unsafe {::trace::syslog(::trace::LOG_INFO, format_string.as_ptr() as *const c_char, _context /* as *mut () */, _filename.as_ptr());} + } +} + +#[inline(always)] +#[allow(dead_code)] +pub fn trace_test_wibble(_context: *mut (), _value: std::ffi::c_int) +{ + if trace_event_state_is_enabled(unsafe { _TRACE_TEST_WIBBLE_DSTATE}) { + let format_string = c"Wibble context=%p value=%d"; + unsafe {::trace::syslog(::trace::LOG_INFO, format_string.as_ptr() as *const c_char, _context /* as *mut () */, _value /* as std::ffi::c_int */);} + } +} diff --git a/tests/tracetool/tracetool-test.py b/tests/tracetool/tracetool-test.py index 65430fdedc..786083ad7f 100755 --- a/tests/tracetool/tracetool-test.py +++ b/tests/tracetool/tracetool-test.py @@ -14,6 +14,8 @@ def get_formats(backend): "c", "h", ] + if backend in {"ftrace", "log", "simple", "syslog"}: + formats += ["rs"] if backend == "dtrace": formats += [ "d", diff --git a/tests/unit/test-error-report.c b/tests/unit/test-error-report.c index 54319c86c9..0cbde3c4cf 100644 --- a/tests/unit/test-error-report.c +++ b/tests/unit/test-error-report.c @@ -104,22 +104,6 @@ test_error_report_timestamp(void) "); } -static void -test_error_warn(void) -{ - if (g_test_subprocess()) { - error_setg(&error_warn, "Testing &error_warn"); - return; - } - - g_test_trap_subprocess(NULL, 0, 0); - g_test_trap_assert_passed(); - g_test_trap_assert_stderr("\ -test-error-report: warning: Testing &error_warn*\ -"); -} - - int main(int argc, char *argv[]) { @@ -133,7 +117,6 @@ main(int argc, char *argv[]) g_test_add_func("/error-report/glog", test_error_report_glog); g_test_add_func("/error-report/once", test_error_report_once); g_test_add_func("/error-report/timestamp", test_error_report_timestamp); - g_test_add_func("/error-report/warn", test_error_warn); return g_test_run(); } diff --git a/trace/ftrace.c b/trace/ftrace.c index 9749543d9b..6875faedb9 100644 --- a/trace/ftrace.c +++ b/trace/ftrace.c @@ -38,6 +38,21 @@ static int find_mount(char *mount_point, const char *fstype) return ret; } +void ftrace_write(const char *fmt, ...) +{ + char ftrace_buf[MAX_TRACE_STRLEN]; + int unused __attribute__ ((unused)); + int trlen; + va_list ap; + + va_start(ap, fmt); + trlen = vsnprintf(ftrace_buf, MAX_TRACE_STRLEN, fmt, ap); + va_end(ap); + + trlen = MIN(trlen, MAX_TRACE_STRLEN - 1); + unused = write(trace_marker_fd, ftrace_buf, trlen); +} + bool ftrace_init(void) { char mount_point[PATH_MAX]; diff --git a/trace/ftrace.h b/trace/ftrace.h index cb5e35d217..16c122816d 100644 --- a/trace/ftrace.h +++ b/trace/ftrace.h @@ -8,5 +8,6 @@ extern int trace_marker_fd; bool ftrace_init(void); +G_GNUC_PRINTF(1, 2) void ftrace_write(const char *fmt, ...); #endif /* TRACE_FTRACE_H */ diff --git a/trace/meson.build b/trace/meson.build index 9c42a57a05..d89a0db82a 100644 --- a/trace/meson.build +++ b/trace/meson.build @@ -1,5 +1,5 @@ system_ss.add(files('control-target.c', 'trace-hmp-cmds.c')) - +trace_rs_targets = [] trace_events_files = [] foreach item : [ '.' ] + trace_events_subdirs + qapi_trace_events if item in qapi_trace_events @@ -24,6 +24,11 @@ foreach item : [ '.' ] + trace_events_subdirs + qapi_trace_events input: trace_events_file, command: [ tracetool, group, '--format=c', '@INPUT@', '@OUTPUT@' ], depend_files: tracetool_depends) + trace_rs = custom_target(fmt.format('trace', 'rs'), + output: fmt.format('trace', 'rs'), + input: trace_events_file, + command: [ tracetool, group, '--format=rs', '@INPUT@', '@OUTPUT@' ], + depend_files: tracetool_depends) if 'ust' in get_option('trace_backends') trace_ust_h = custom_target(fmt.format('trace-ust', 'h'), output: fmt.format('trace-ust', 'h'), @@ -34,6 +39,7 @@ foreach item : [ '.' ] + trace_events_subdirs + qapi_trace_events genh += trace_ust_h endif trace_ss.add(trace_h, trace_c) + trace_rs_targets += trace_rs if 'dtrace' in get_option('trace_backends') trace_dtrace = custom_target(fmt.format('trace-dtrace', 'dtrace'), output: fmt.format('trace-dtrace', 'dtrace'), diff --git a/ui/dbus-listener.c b/ui/dbus-listener.c index 42875b8eed..52e041edb0 100644 --- a/ui/dbus-listener.c +++ b/ui/dbus-listener.c @@ -214,24 +214,32 @@ static void dbus_update_gl_cb(GObject *source_object, GAsyncResult *res, gpointer user_data) { - g_autoptr(GError) err = NULL; + g_autoptr(GError) gerr = NULL; +#ifdef WIN32 + Error *err = NULL; +#endif DBusDisplayListener *ddl = user_data; bool success; #ifdef CONFIG_GBM success = qemu_dbus_display1_listener_call_update_dmabuf_finish( - ddl->proxy, res, &err); + ddl->proxy, res, &gerr); + if (!success) { + error_report("Failed to call update: %s", gerr->message); + } #endif #ifdef WIN32 success = qemu_dbus_display1_listener_win32_d3d11_call_update_texture2d_finish( - ddl->d3d11_proxy, res, &err); - d3d_texture2d_acquire0(ddl->d3d_texture, &error_warn); -#endif - + ddl->d3d11_proxy, res, &gerr); if (!success) { - error_report("Failed to call update: %s", err->message); + error_report("Failed to call update: %s", gerr->message); + } + + if (!d3d_texture2d_acquire0(ddl->d3d_texture, &err)) { + error_report_err(err); } +#endif graphic_hw_gl_block(ddl->dcl.con, false); g_object_unref(ddl); diff --git a/ui/gtk.c b/ui/gtk.c index e91d093a49..9a08cadc88 100644 --- a/ui/gtk.c +++ b/ui/gtk.c @@ -1181,6 +1181,7 @@ static gboolean gd_touch_event(GtkWidget *widget, GdkEventTouch *touch, void *opaque) { VirtualConsole *vc = opaque; + Error *err = NULL; uint64_t num_slot = GPOINTER_TO_UINT(touch->sequence); int type = -1; @@ -1203,7 +1204,10 @@ static gboolean gd_touch_event(GtkWidget *widget, GdkEventTouch *touch, console_handle_touch_event(vc->gfx.dcl.con, touch_slots, num_slot, surface_width(vc->gfx.ds), surface_height(vc->gfx.ds), touch->x, - touch->y, type, &error_warn); + touch->y, type, &err); + if (err) { + warn_report_err(err); + } return TRUE; } diff --git a/ui/qemu-pixman.c b/ui/qemu-pixman.c index ef4e71da11..e46c6232cf 100644 --- a/ui/qemu-pixman.c +++ b/ui/qemu-pixman.c @@ -288,7 +288,10 @@ qemu_pixman_shareable_free(qemu_pixman_shareable handle, void *ptr, size_t size) { #ifdef WIN32 - qemu_win32_map_free(ptr, handle, &error_warn); + Error *err = NULL; + + qemu_win32_map_free(ptr, handle, &err); + error_report_err(err); #else qemu_memfd_free(ptr, size, handle); #endif diff --git a/ui/spice-core.c b/ui/spice-core.c index 2645e96ef6..8a6050f4ae 100644 --- a/ui/spice-core.c +++ b/ui/spice-core.c @@ -126,11 +126,13 @@ static void watch_update_mask(SpiceWatch *watch, int event_mask) static SpiceWatch *watch_add(int fd, int event_mask, SpiceWatchFunc func, void *opaque) { SpiceWatch *watch; - #ifdef WIN32 + g_autofree char *msg = NULL; + fd = _open_osfhandle(fd, _O_BINARY); if (fd < 0) { - error_setg_win32(&error_warn, WSAGetLastError(), "Couldn't associate a FD with the SOCKET"); + msg = g_win32_error_message(WSAGetLastError()); + warn_report("Couldn't associate a FD with the SOCKET: %s", msg); return NULL; } #endif diff --git a/util/aio-win32.c b/util/aio-win32.c index 6583d5c5f3..c6fbce64c2 100644 --- a/util/aio-win32.c +++ b/util/aio-win32.c @@ -121,7 +121,7 @@ void aio_set_fd_handler(AioContext *ctx, QLIST_INSERT_HEAD_RCU(&ctx->aio_handlers, node, node); event = event_notifier_get_handle(&ctx->notifier); - qemu_socket_select(fd, event, bitmask, NULL); + qemu_socket_select_nofail(fd, event, bitmask); } if (old_node) { aio_remove_fd_handler(ctx, old_node); diff --git a/util/error.c b/util/error.c index daea2142f3..0ae08225c0 100644 --- a/util/error.c +++ b/util/error.c @@ -19,7 +19,6 @@ Error *error_abort; Error *error_fatal; -Error *error_warn; static void error_handle(Error **errp, Error *err) { @@ -41,9 +40,7 @@ static void error_handle(Error **errp, Error *err) error_report_err(err); exit(1); } - if (errp == &error_warn) { - warn_report_err(err); - } else if (errp && !*errp) { + if (errp && !*errp) { *errp = err; } else { error_free(err); diff --git a/util/log.c b/util/log.c index abdcb6b311..41f78ce86b 100644 --- a/util/log.c +++ b/util/log.c @@ -44,7 +44,7 @@ static FILE *global_file; static __thread FILE *thread_file; static __thread Notifier qemu_log_thread_cleanup_notifier; -int qemu_loglevel; +unsigned qemu_loglevel; static bool log_per_thread; static GArray *debug_regions; diff --git a/util/oslib-win32.c b/util/oslib-win32.c index b9ce2f96ee..84bc65a765 100644 --- a/util/oslib-win32.c +++ b/util/oslib-win32.c @@ -182,7 +182,7 @@ bool qemu_set_blocking(int fd, bool block, Error **errp) unsigned long opt = block ? 0 : 1; if (block) { - qemu_socket_unselect(fd, NULL); + qemu_socket_unselect_nofail(fd); } if (ioctlsocket(fd, FIONBIO, &opt) != NO_ERROR) { @@ -293,10 +293,6 @@ bool qemu_socket_select(int sockfd, WSAEVENT hEventObject, { SOCKET s = _get_osfhandle(sockfd); - if (errp == NULL) { - errp = &error_warn; - } - if (s == INVALID_SOCKET) { error_setg(errp, "invalid socket fd=%d", sockfd); return false; @@ -315,6 +311,25 @@ bool qemu_socket_unselect(int sockfd, Error **errp) return qemu_socket_select(sockfd, NULL, 0, errp); } +void qemu_socket_select_nofail(int sockfd, WSAEVENT hEventObject, + long lNetworkEvents) +{ + Error *err = NULL; + + if (!qemu_socket_select(sockfd, hEventObject, lNetworkEvents, &err)) { + warn_report_err(err); + } +} + +void qemu_socket_unselect_nofail(int sockfd) +{ + Error *err = NULL; + + if (!qemu_socket_unselect(sockfd, &err)) { + warn_report_err(err); + } +} + int qemu_socketpair(int domain, int type, int protocol, int sv[2]) { struct sockaddr_un addr = { |