From 4e082566a9cd17e5f00d0df85a67dc7a086d30b0 Mon Sep 17 00:00:00 2001 From: Victor Kaplansky Date: Sun, 14 Feb 2016 18:59:27 +0200 Subject: tests: add pxe e1000 and virtio-pci tests The test is based on bios-tables-test.c. It creates a file with the boot sector image and loads it into a guest using PXE and TFTP functionality. Cc: Jason Wang Signed-off-by: Victor Kaplansky Suggested-by: Michael S. Tsirkin Reviewed-by: Michael S. Tsirkin Signed-off-by: Michael S. Tsirkin --- tests/Makefile | 5 +- tests/bios-tables-test.c | 72 +++------------------------- tests/boot-sector.c | 119 +++++++++++++++++++++++++++++++++++++++++++++++ tests/boot-sector.h | 26 +++++++++++ tests/pxe-test.c | 69 +++++++++++++++++++++++++++ 5 files changed, 224 insertions(+), 67 deletions(-) create mode 100644 tests/boot-sector.c create mode 100644 tests/boot-sector.h create mode 100644 tests/pxe-test.c (limited to 'tests') diff --git a/tests/Makefile b/tests/Makefile index 650e654..fc042f7 100644 --- a/tests/Makefile +++ b/tests/Makefile @@ -173,6 +173,7 @@ check-qtest-i386-y += tests/hd-geo-test$(EXESUF) gcov-files-i386-y += hw/block/hd-geometry.c check-qtest-i386-y += tests/boot-order-test$(EXESUF) check-qtest-i386-y += tests/bios-tables-test$(EXESUF) +check-qtest-i386-y += tests/pxe-test$(EXESUF) check-qtest-i386-y += tests/rtc-test$(EXESUF) check-qtest-i386-y += tests/ipmi-kcs-test$(EXESUF) check-qtest-i386-y += tests/ipmi-bt-test$(EXESUF) @@ -518,7 +519,9 @@ tests/ipmi-kcs-test$(EXESUF): tests/ipmi-kcs-test.o tests/ipmi-bt-test$(EXESUF): tests/ipmi-bt-test.o tests/hd-geo-test$(EXESUF): tests/hd-geo-test.o tests/boot-order-test$(EXESUF): tests/boot-order-test.o $(libqos-obj-y) -tests/bios-tables-test$(EXESUF): tests/bios-tables-test.o $(libqos-obj-y) +tests/bios-tables-test$(EXESUF): tests/bios-tables-test.o \ + tests/boot-sector.o $(libqos-obj-y) +tests/pxe-test$(EXESUF): tests/pxe-test.o tests/boot-sector.o $(libqos-obj-y) tests/tmp105-test$(EXESUF): tests/tmp105-test.o $(libqos-omap-obj-y) tests/ds1338-test$(EXESUF): tests/ds1338-test.o $(libqos-imx-obj-y) tests/i440fx-test$(EXESUF): tests/i440fx-test.o $(libqos-pc-obj-y) diff --git a/tests/bios-tables-test.c b/tests/bios-tables-test.c index 75ec330..3488058 100644 --- a/tests/bios-tables-test.c +++ b/tests/bios-tables-test.c @@ -20,6 +20,7 @@ #include "hw/acpi/acpi-defs.h" #include "hw/smbios/smbios.h" #include "qemu/bitmap.h" +#include "boot-sector.h" #define MACHINE_PC "pc" #define MACHINE_Q35 "q35" @@ -53,13 +54,6 @@ typedef struct { struct smbios_21_entry_point smbios_ep_table; } test_data; -#define LOW(x) ((x) & 0xff) -#define HIGH(x) ((x) >> 8) - -#define SIGNATURE 0xdead -#define SIGNATURE_OFFSET 0x10 -#define BOOT_SECTOR_ADDRESS 0x7c00 - #define ACPI_READ_FIELD(field, addr) \ do { \ switch (sizeof(field)) { \ @@ -119,35 +113,6 @@ typedef struct { g_assert_cmpstr(ACPI_ASSERT_CMP_str, ==, expected); \ } while (0) -/* Boot sector code: write SIGNATURE into memory, - * then halt. - * Q35 machine requires a minimum 0x7e000 bytes disk. - * (bug or feature?) - */ -static uint8_t boot_sector[0x7e000] = { - /* 7c00: mov $0xdead,%ax */ - [0x00] = 0xb8, - [0x01] = LOW(SIGNATURE), - [0x02] = HIGH(SIGNATURE), - /* 7c03: mov %ax,0x7c10 */ - [0x03] = 0xa3, - [0x04] = LOW(BOOT_SECTOR_ADDRESS + SIGNATURE_OFFSET), - [0x05] = HIGH(BOOT_SECTOR_ADDRESS + SIGNATURE_OFFSET), - /* 7c06: cli */ - [0x06] = 0xfa, - /* 7c07: hlt */ - [0x07] = 0xf4, - /* 7c08: jmp 0x7c07=0x7c0a-3 */ - [0x08] = 0xeb, - [0x09] = LOW(-3), - /* We mov 0xdead here: set value to make debugging easier */ - [SIGNATURE_OFFSET] = LOW(0xface), - [SIGNATURE_OFFSET + 1] = HIGH(0xface), - /* End of boot sector marker */ - [0x1FE] = 0x55, - [0x1FF] = 0xAA, -}; - static const char *disk = "tests/acpi-test-disk.raw"; static const char *data_dir = "tests/acpi-test-data"; #ifdef CONFIG_IASL @@ -739,10 +704,6 @@ static void test_smbios_structs(test_data *data) static void test_acpi_one(const char *params, test_data *data) { char *args; - uint8_t signature_low; - uint8_t signature_high; - uint16_t signature; - int i; args = g_strdup_printf("-net none -display none %s " "-drive id=hd0,if=none,file=%s,format=raw " @@ -751,24 +712,7 @@ static void test_acpi_one(const char *params, test_data *data) qtest_start(args); - /* Wait at most 1 minute */ -#define TEST_DELAY (1 * G_USEC_PER_SEC / 10) -#define TEST_CYCLES MAX((60 * G_USEC_PER_SEC / TEST_DELAY), 1) - - /* Poll until code has run and modified memory. Once it has we know BIOS - * initialization is done. TODO: check that IP reached the halt - * instruction. - */ - for (i = 0; i < TEST_CYCLES; ++i) { - signature_low = readb(BOOT_SECTOR_ADDRESS + SIGNATURE_OFFSET); - signature_high = readb(BOOT_SECTOR_ADDRESS + SIGNATURE_OFFSET + 1); - signature = (signature_high << 8) | signature_low; - if (signature == SIGNATURE) { - break; - } - g_usleep(TEST_DELAY); - } - g_assert_cmphex(signature, ==, SIGNATURE); + boot_sector_test(); test_acpi_rsdp_address(data); test_acpi_rsdp_table(data); @@ -842,15 +786,11 @@ static void test_acpi_q35_tcg_bridge(void) int main(int argc, char *argv[]) { const char *arch = qtest_get_arch(); - FILE *f = fopen(disk, "w"); int ret; - if (!f) { - fprintf(stderr, "Couldn't open \"%s\": %s", disk, strerror(errno)); - return 1; - } - fwrite(boot_sector, 1, sizeof boot_sector, f); - fclose(f); + ret = boot_sector_init(disk); + if(ret) + return ret; g_test_init(&argc, &argv, NULL); @@ -861,6 +801,6 @@ int main(int argc, char *argv[]) qtest_add_func("acpi/q35/tcg/bridge", test_acpi_q35_tcg_bridge); } ret = g_test_run(); - unlink(disk); + boot_sector_cleanup(disk); return ret; } diff --git a/tests/boot-sector.c b/tests/boot-sector.c new file mode 100644 index 0000000..0b48bb3 --- /dev/null +++ b/tests/boot-sector.c @@ -0,0 +1,119 @@ +/* + * QEMU boot sector testing helpers. + * + * Copyright (c) 2016 Red Hat Inc. + * + * Authors: + * Michael S. Tsirkin + * Victor Kaplansky + * + * This work is licensed under the terms of the GNU GPL, version 2 or later. + * See the COPYING file in the top-level directory. + */ +#include "boot-sector.h" +#include +#include +#include "qemu-common.h" +#include "libqtest.h" + +#define LOW(x) ((x) & 0xff) +#define HIGH(x) ((x) >> 8) + +#define SIGNATURE 0xdead +#define SIGNATURE_OFFSET 0x10 +#define BOOT_SECTOR_ADDRESS 0x7c00 + +/* Boot sector code: write SIGNATURE into memory, + * then halt. + * Q35 machine requires a minimum 0x7e000 bytes disk. + * (bug or feature?) + */ +static uint8_t boot_sector[0x7e000] = { + /* The first sector will be placed at RAM address 00007C00, and + * the BIOS transfers control to 00007C00 + */ + + /* Data Segment register should be initialized, since pxe + * boot loader can leave it dirty. + */ + + /* 7c00: move $0000,%ax */ + [0x00] = 0xb8, + [0x01] = 0x00, + [0x02] = 0x00, + /* 7c03: move %ax,%ds */ + [0x03] = 0x8e, + [0x04] = 0xd8, + + /* 7c05: mov $0xdead,%ax */ + [0x05] = 0xb8, + [0x06] = LOW(SIGNATURE), + [0x07] = HIGH(SIGNATURE), + /* 7c08: mov %ax,0x7c10 */ + [0x08] = 0xa3, + [0x09] = LOW(BOOT_SECTOR_ADDRESS + SIGNATURE_OFFSET), + [0x0a] = HIGH(BOOT_SECTOR_ADDRESS + SIGNATURE_OFFSET), + + /* 7c0b cli */ + [0x0b] = 0xfa, + /* 7c0c: hlt */ + [0x0c] = 0xf4, + /* 7c0e: jmp 0x7c07=0x7c0f-3 */ + [0x0d] = 0xeb, + [0x0e] = LOW(-3), + /* We mov 0xdead here: set value to make debugging easier */ + [SIGNATURE_OFFSET] = LOW(0xface), + [SIGNATURE_OFFSET + 1] = HIGH(0xface), + /* End of boot sector marker */ + [0x1FE] = 0x55, + [0x1FF] = 0xAA, +}; + +/* Create boot disk file. */ +int boot_sector_init(const char *fname) +{ + FILE *f = fopen(fname, "w"); + + if (!f) { + fprintf(stderr, "Couldn't open \"%s\": %s", fname, strerror(errno)); + return 1; + } + fwrite(boot_sector, 1, sizeof boot_sector, f); + fclose(f); + return 0; +} + +/* Loop until signature in memory is OK. */ +void boot_sector_test(void) +{ + uint8_t signature_low; + uint8_t signature_high; + uint16_t signature; + int i; + + /* Wait at most 1 minute */ +#define TEST_DELAY (1 * G_USEC_PER_SEC / 10) +#define TEST_CYCLES MAX((60 * G_USEC_PER_SEC / TEST_DELAY), 1) + + /* Poll until code has run and modified memory. Once it has we know BIOS + * initialization is done. TODO: check that IP reached the halt + * instruction. + */ + for (i = 0; i < TEST_CYCLES; ++i) { + signature_low = readb(BOOT_SECTOR_ADDRESS + SIGNATURE_OFFSET); + signature_high = readb(BOOT_SECTOR_ADDRESS + SIGNATURE_OFFSET + 1); + signature = (signature_high << 8) | signature_low; + if (signature == SIGNATURE) { + break; + } + g_usleep(TEST_DELAY); + } + + g_assert_cmphex(signature, ==, SIGNATURE); +} + +/* unlink boot disk file. */ +void boot_sector_cleanup(const char *fname) +{ + unlink(fname); +} diff --git a/tests/boot-sector.h b/tests/boot-sector.h new file mode 100644 index 0000000..38be029 --- /dev/null +++ b/tests/boot-sector.h @@ -0,0 +1,26 @@ +/* + * QEMU boot sector testing helpers. + * + * Copyright (c) 2016 Red Hat Inc. + * + * Authors: + * Michael S. Tsirkin + * Victor Kaplansky + * + * This work is licensed under the terms of the GNU GPL, version 2 or later. + * See the COPYING file in the top-level directory. + */ + +#ifndef TEST_BOOT_SECTOR +#define TEST_BOOT_SECTOR + +/* Create boot disk file. */ +int boot_sector_init(const char *fname); + +/* Loop until signature in memory is OK. */ +void boot_sector_test(void); + +/* unlink boot disk file. */ +void boot_sector_cleanup(const char *fname); + +#endif /* TEST_BOOT_SECTOR */ diff --git a/tests/pxe-test.c b/tests/pxe-test.c new file mode 100644 index 0000000..fa43095 --- /dev/null +++ b/tests/pxe-test.c @@ -0,0 +1,69 @@ +/* + * PXE test cases. + * + * Copyright (c) 2016 Red Hat Inc. + * + * Authors: + * Michael S. Tsirkin , + * Victor Kaplansky + * + * This work is licensed under the terms of the GNU GPL, version 2 or later. + * See the COPYING file in the top-level directory. + */ + +#include +#include +#include +#include +#include "qemu-common.h" +#include "libqtest.h" +#include "boot-sector.h" + +#define NETNAME "net0" + +static const char *disk = "tests/pxe-test-disk.raw"; + +static void test_pxe_one(const char *params) +{ + char *args; + + args = g_strdup_printf("-machine accel=tcg " + "-netdev user,id=" NETNAME ",tftp=./,bootfile=%s " + "%s ", + disk, params); + + qtest_start(args); + boot_sector_test(); + qtest_quit(global_qtest); + g_free(args); +} + +static void test_pxe_e1000(void) +{ + test_pxe_one("-device e1000,netdev=" NETNAME); +} + +static void test_pxe_virtio_pci(void) +{ + test_pxe_one("-device virtio-net-pci,netdev=" NETNAME); +} + +int main(int argc, char *argv[]) +{ + int ret; + const char *arch = qtest_get_arch(); + + ret = boot_sector_init(disk); + if(ret) + return ret; + + g_test_init(&argc, &argv, NULL); + + if (strcmp(arch, "i386") == 0 || strcmp(arch, "x86_64") == 0) { + qtest_add_func("pxe/e1000", test_pxe_e1000); + qtest_add_func("pxe/virtio", test_pxe_virtio_pci); + } + ret = g_test_run(); + boot_sector_cleanup(disk); + return ret; +} -- cgit v1.1 From a28c393cc261afeb4863b05d7c33c2a5fc55ef38 Mon Sep 17 00:00:00 2001 From: Victor Kaplansky Date: Thu, 18 Feb 2016 16:45:05 +0200 Subject: tests/vhost-user-bridge: add scattering of incoming packets This patch adds to the vubr test the scattering of incoming packets to the chain of RX buffer. Also, this patch corrects the size of the header preceding the packet in RX buffers. Note that this patch doesn't add the support for mergeable buffers. Signed-off-by: Victor Kaplansky Reviewed-by: Michael S. Tsirkin Signed-off-by: Michael S. Tsirkin --- tests/vhost-user-bridge.c | 92 +++++++++++++++++++++++++++++++---------------- 1 file changed, 61 insertions(+), 31 deletions(-) (limited to 'tests') diff --git a/tests/vhost-user-bridge.c b/tests/vhost-user-bridge.c index 9fb09f1..f5030b2 100644 --- a/tests/vhost-user-bridge.c +++ b/tests/vhost-user-bridge.c @@ -47,6 +47,7 @@ #include #include #include +#include #include @@ -186,6 +187,8 @@ typedef struct VubrVirtq { #define VHOST_MEMORY_MAX_NREGIONS 8 #define VHOST_USER_F_PROTOCOL_FEATURES 30 +/* v1.0 compliant. */ +#define VIRTIO_F_VERSION_1 32 #define VHOST_LOG_PAGE 4096 @@ -294,6 +297,7 @@ typedef struct VubrDev { struct sockaddr_in backend_udp_dest; int ready; uint64_t features; + int hdrlen; } VubrDev; static const char *vubr_request_str[] = { @@ -484,7 +488,8 @@ vubr_backend_udp_recvbuf(VubrDev *dev, uint8_t *buf, size_t buflen) static void vubr_consume_raw_packet(VubrDev *dev, uint8_t *buf, uint32_t len) { - int hdrlen = sizeof(struct virtio_net_hdr_v1); + int hdrlen = dev->hdrlen; + DPRINT(" hdrlen = %d\n", dev->hdrlen); if (VHOST_USER_BRIDGE_DEBUG) { print_buffer(buf, len); @@ -546,6 +551,7 @@ vubr_post_buffer(VubrDev *dev, VubrVirtq *vq, uint8_t *buf, int32_t len) struct vring_avail *avail = vq->avail; struct vring_used *used = vq->used; uint64_t log_guest_addr = vq->log_guest_addr; + int32_t remaining_len = len; unsigned int size = vq->size; @@ -560,36 +566,49 @@ vubr_post_buffer(VubrDev *dev, VubrVirtq *vq, uint8_t *buf, int32_t len) uint16_t d_index = avail->ring[a_index]; int i = d_index; + uint32_t written_len = 0; - DPRINT("Post packet to guest on vq:\n"); - DPRINT(" size = %d\n", vq->size); - DPRINT(" last_avail_index = %d\n", vq->last_avail_index); - DPRINT(" last_used_index = %d\n", vq->last_used_index); - DPRINT(" a_index = %d\n", a_index); - DPRINT(" u_index = %d\n", u_index); - DPRINT(" d_index = %d\n", d_index); - DPRINT(" desc[%d].addr = 0x%016"PRIx64"\n", i, desc[i].addr); - DPRINT(" desc[%d].len = %d\n", i, desc[i].len); - DPRINT(" desc[%d].flags = %d\n", i, desc[i].flags); - DPRINT(" avail->idx = %d\n", avail_index); - DPRINT(" used->idx = %d\n", used->idx); - - if (!(desc[i].flags & VRING_DESC_F_WRITE)) { - /* FIXME: we should find writable descriptor. */ - fprintf(stderr, "Error: descriptor is not writable. Exiting.\n"); - exit(1); - } + do { + DPRINT("Post packet to guest on vq:\n"); + DPRINT(" size = %d\n", vq->size); + DPRINT(" last_avail_index = %d\n", vq->last_avail_index); + DPRINT(" last_used_index = %d\n", vq->last_used_index); + DPRINT(" a_index = %d\n", a_index); + DPRINT(" u_index = %d\n", u_index); + DPRINT(" d_index = %d\n", d_index); + DPRINT(" desc[%d].addr = 0x%016"PRIx64"\n", i, desc[i].addr); + DPRINT(" desc[%d].len = %d\n", i, desc[i].len); + DPRINT(" desc[%d].flags = %d\n", i, desc[i].flags); + DPRINT(" avail->idx = %d\n", avail_index); + DPRINT(" used->idx = %d\n", used->idx); + + if (!(desc[i].flags & VRING_DESC_F_WRITE)) { + /* FIXME: we should find writable descriptor. */ + fprintf(stderr, "Error: descriptor is not writable. Exiting.\n"); + exit(1); + } - void *chunk_start = (void *)gpa_to_va(dev, desc[i].addr); - uint32_t chunk_len = desc[i].len; + void *chunk_start = (void *)gpa_to_va(dev, desc[i].addr); + uint32_t chunk_len = desc[i].len; + uint32_t chunk_write_len = MIN(remaining_len, chunk_len); - if (len <= chunk_len) { - memcpy(chunk_start, buf, len); - vubr_log_write(dev, desc[i].addr, len); - } else { - fprintf(stderr, - "Received too long packet from the backend. Dropping...\n"); - return; + memcpy(chunk_start, buf + written_len, chunk_write_len); + vubr_log_write(dev, desc[i].addr, chunk_write_len); + remaining_len -= chunk_write_len; + written_len += chunk_write_len; + + if ((remaining_len == 0) || !(desc[i].flags & VRING_DESC_F_NEXT)) { + break; + } + + i = desc[i].next; + } while (1); + + if (remaining_len > 0) { + fprintf(stderr, + "Too long packet for RX, remaining_len = %d, Dropping...\n", + remaining_len); + return; } /* Add descriptor to the used ring. */ @@ -697,7 +716,7 @@ vubr_backend_recv_cb(int sock, void *ctx) VubrVirtq *rx_vq = &dev->vq[0]; uint8_t buf[4096]; struct virtio_net_hdr_v1 *hdr = (struct virtio_net_hdr_v1 *)buf; - int hdrlen = sizeof(struct virtio_net_hdr_v1); + int hdrlen = dev->hdrlen; int buflen = sizeof(buf); int len; @@ -706,6 +725,7 @@ vubr_backend_recv_cb(int sock, void *ctx) } DPRINT("\n\n *** IN UDP RECEIVE CALLBACK ***\n\n"); + DPRINT(" hdrlen = %d\n", hdrlen); uint16_t avail_index = atomic_mb_read(&rx_vq->avail->idx); @@ -717,10 +737,12 @@ vubr_backend_recv_cb(int sock, void *ctx) return; } + memset(buf, 0, hdrlen); + /* TODO: support mergeable buffers. */ + if (hdrlen == 12) + hdr->num_buffers = 1; len = vubr_backend_udp_recvbuf(dev, buf + hdrlen, buflen - hdrlen); - *hdr = (struct virtio_net_hdr_v1) { }; - hdr->num_buffers = 1; vubr_post_buffer(dev, rx_vq, buf, len + hdrlen); } @@ -768,7 +790,15 @@ static int vubr_set_features_exec(VubrDev *dev, VhostUserMsg *vmsg) { DPRINT("u64: 0x%016"PRIx64"\n", vmsg->payload.u64); + dev->features = vmsg->payload.u64; + if ((dev->features & (1ULL << VIRTIO_F_VERSION_1)) || + (dev->features & (1ULL << VIRTIO_NET_F_MRG_RXBUF))) { + dev->hdrlen = 12; + } else { + dev->hdrlen = 10; + } + return 0; } -- cgit v1.1