diff options
-rw-r--r-- | MAINTAINERS | 1 | ||||
-rw-r--r-- | docs/system/deprecated.rst | 9 | ||||
-rw-r--r-- | hw/net/virtio-net.c | 4 | ||||
-rw-r--r-- | hw/sd/sd.c | 23 | ||||
-rw-r--r-- | hw/sd/sdhci.c | 53 | ||||
-rw-r--r-- | include/net/eth.h | 17 | ||||
-rw-r--r-- | include/net/net.h | 1 | ||||
-rw-r--r-- | net/colo-compare.c | 3 | ||||
-rw-r--r-- | net/eth.c | 61 | ||||
-rw-r--r-- | net/slirp.c | 10 | ||||
-rw-r--r-- | net/tap-win32.c | 10 | ||||
-rw-r--r-- | net/tap.c | 10 | ||||
-rw-r--r-- | target/mips/mxu_translate.c | 8 | ||||
-rw-r--r-- | tests/qtest/fuzz-e1000e-test.c | 53 | ||||
-rw-r--r-- | tests/qtest/meson.build | 1 |
15 files changed, 211 insertions, 53 deletions
diff --git a/MAINTAINERS b/MAINTAINERS index 25fc49d1dc..9147e9a429 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -2027,6 +2027,7 @@ e1000e M: Dmitry Fleytman <dmitry.fleytman@gmail.com> S: Maintained F: hw/net/e1000e* +F: tests/qtest/fuzz-e1000e-test.c eepro100 M: Stefan Weil <sw@weilnetz.de> diff --git a/docs/system/deprecated.rst b/docs/system/deprecated.rst index 67c98dcaa0..80cae86252 100644 --- a/docs/system/deprecated.rst +++ b/docs/system/deprecated.rst @@ -186,6 +186,15 @@ Use the more generic commands ``block-export-add`` and ``block-export-del`` instead. As part of this deprecation, where ``nbd-server-add`` used a single ``bitmap``, the new ``block-export-add`` uses a list of ``bitmaps``. +System accelerators +------------------- + +MIPS ``Trap-and-Emul`` KVM support (since 6.0) +'''''''''''''''''''''''''''''''''''''''''''''' + +The MIPS ``Trap-and-Emul`` KVM host and guest support has been removed +from Linux upstream kernel, declare it deprecated. + System emulator CPUS -------------------- diff --git a/hw/net/virtio-net.c b/hw/net/virtio-net.c index 96a3cc8357..66b9ff4511 100644 --- a/hw/net/virtio-net.c +++ b/hw/net/virtio-net.c @@ -3314,6 +3314,10 @@ static void virtio_net_device_realize(DeviceState *dev, Error **errp) object_get_typename(OBJECT(dev)), dev->id, n); } + for (i = 0; i < n->max_queues; i++) { + n->nic->ncs[i].do_not_pad = true; + } + peer_test_vnet_hdr(n); if (peer_has_vnet_hdr(n)) { for (i = 0; i < n->max_queues; i++) { diff --git a/hw/sd/sd.c b/hw/sd/sd.c index 8b397effbc..282d39a704 100644 --- a/hw/sd/sd.c +++ b/hw/sd/sd.c @@ -47,6 +47,7 @@ #include "qemu/timer.h" #include "qemu/log.h" #include "qemu/module.h" +#include "qemu-common.h" #include "sdmmc-internal.h" #include "trace.h" @@ -762,10 +763,12 @@ static void sd_blk_write(SDState *sd, uint64_t addr, uint32_t len) static void sd_erase(SDState *sd) { - int i; uint64_t erase_start = sd->erase_start; uint64_t erase_end = sd->erase_end; bool sdsc = true; + uint64_t wpnum; + uint64_t erase_addr; + int erase_len = 1 << HWBLOCK_SHIFT; trace_sdcard_erase(sd->erase_start, sd->erase_end); if (sd->erase_start == INVALID_ADDRESS @@ -794,17 +797,19 @@ static void sd_erase(SDState *sd) sd->erase_end = INVALID_ADDRESS; sd->csd[14] |= 0x40; - /* Only SDSC cards support write protect groups */ - if (sdsc) { - erase_start = sd_addr_to_wpnum(erase_start); - erase_end = sd_addr_to_wpnum(erase_end); - - for (i = erase_start; i <= erase_end; i++) { - assert(i < sd->wpgrps_size); - if (test_bit(i, sd->wp_groups)) { + memset(sd->data, 0xff, erase_len); + for (erase_addr = erase_start; erase_addr <= erase_end; + erase_addr += erase_len) { + if (sdsc) { + /* Only SDSC cards support write protect groups */ + wpnum = sd_addr_to_wpnum(erase_addr); + assert(wpnum < sd->wpgrps_size); + if (test_bit(wpnum, sd->wp_groups)) { sd->card_status |= WP_ERASE_SKIP; + continue; } } + BLK_WRITE_BLOCK(erase_addr, erase_len); } } diff --git a/hw/sd/sdhci.c b/hw/sd/sdhci.c index 9acf4467a3..5b8678110b 100644 --- a/hw/sd/sdhci.c +++ b/hw/sd/sdhci.c @@ -326,6 +326,7 @@ static void sdhci_send_command(SDHCIState *s) SDRequest request; uint8_t response[16]; int rlen; + bool timeout = false; s->errintsts = 0; s->acmd12errsts = 0; @@ -349,6 +350,7 @@ static void sdhci_send_command(SDHCIState *s) trace_sdhci_response16(s->rspreg[3], s->rspreg[2], s->rspreg[1], s->rspreg[0]); } else { + timeout = true; trace_sdhci_error("timeout waiting for command response"); if (s->errintstsen & SDHC_EISEN_CMDTIMEOUT) { s->errintsts |= SDHC_EIS_CMDTIMEOUT; @@ -369,7 +371,7 @@ static void sdhci_send_command(SDHCIState *s) sdhci_update_irq(s); - if (s->blksize && (s->cmdreg & SDHC_CMD_DATA_PRESENT)) { + if (!timeout && s->blksize && (s->cmdreg & SDHC_CMD_DATA_PRESENT)) { s->data_count = 0; sdhci_data_transfer(s); } @@ -766,7 +768,9 @@ static void sdhci_do_adma(SDHCIState *s) switch (dscr.attr & SDHC_ADMA_ATTR_ACT_MASK) { case SDHC_ADMA_ATTR_ACT_TRAN: /* data transfer */ + s->prnsts |= SDHC_DATA_INHIBIT | SDHC_DAT_LINE_ACTIVE; if (s->trnmod & SDHC_TRNS_READ) { + s->prnsts |= SDHC_DOING_READ; while (length) { if (s->data_count == 0) { sdbus_read_data(&s->sdbus, s->fifo_buffer, block_size); @@ -794,6 +798,7 @@ static void sdhci_do_adma(SDHCIState *s) } } } else { + s->prnsts |= SDHC_DOING_WRITE; while (length) { begin = s->data_count; if ((length + begin) < block_size) { @@ -1119,31 +1124,45 @@ sdhci_write(void *opaque, hwaddr offset, uint64_t val, unsigned size) switch (offset & ~0x3) { case SDHC_SYSAD: - s->sdmasysad = (s->sdmasysad & mask) | value; - MASKED_WRITE(s->sdmasysad, mask, value); - /* Writing to last byte of sdmasysad might trigger transfer */ - if (!(mask & 0xFF000000) && TRANSFERRING_DATA(s->prnsts) && s->blkcnt && - s->blksize && SDHC_DMA_TYPE(s->hostctl1) == SDHC_CTRL_SDMA) { - if (s->trnmod & SDHC_TRNS_MULTI) { - sdhci_sdma_transfer_multi_blocks(s); - } else { - sdhci_sdma_transfer_single_block(s); + if (!TRANSFERRING_DATA(s->prnsts)) { + s->sdmasysad = (s->sdmasysad & mask) | value; + MASKED_WRITE(s->sdmasysad, mask, value); + /* Writing to last byte of sdmasysad might trigger transfer */ + if (!(mask & 0xFF000000) && s->blkcnt && s->blksize && + SDHC_DMA_TYPE(s->hostctl1) == SDHC_CTRL_SDMA) { + if (s->trnmod & SDHC_TRNS_MULTI) { + sdhci_sdma_transfer_multi_blocks(s); + } else { + sdhci_sdma_transfer_single_block(s); + } } } break; case SDHC_BLKSIZE: if (!TRANSFERRING_DATA(s->prnsts)) { + uint16_t blksize = s->blksize; + MASKED_WRITE(s->blksize, mask, extract32(value, 0, 12)); MASKED_WRITE(s->blkcnt, mask >> 16, value >> 16); - } - /* Limit block size to the maximum buffer size */ - if (extract32(s->blksize, 0, 12) > s->buf_maxsz) { - qemu_log_mask(LOG_GUEST_ERROR, "%s: Size 0x%x is larger than " - "the maximum buffer 0x%x\n", __func__, s->blksize, - s->buf_maxsz); + /* Limit block size to the maximum buffer size */ + if (extract32(s->blksize, 0, 12) > s->buf_maxsz) { + qemu_log_mask(LOG_GUEST_ERROR, "%s: Size 0x%x is larger than " + "the maximum buffer 0x%x\n", __func__, s->blksize, + s->buf_maxsz); + + s->blksize = deposit32(s->blksize, 0, 12, s->buf_maxsz); + } - s->blksize = deposit32(s->blksize, 0, 12, s->buf_maxsz); + /* + * If the block size is programmed to a different value from + * the previous one, reset the data pointer of s->fifo_buffer[] + * so that s->fifo_buffer[] can be filled in using the new block + * size in the next transfer. + */ + if (blksize != s->blksize) { + s->data_count = 0; + } } break; diff --git a/include/net/eth.h b/include/net/eth.h index 0671be6916..7767ae880e 100644 --- a/include/net/eth.h +++ b/include/net/eth.h @@ -31,6 +31,7 @@ #define ETH_ALEN 6 #define ETH_HLEN 14 +#define ETH_ZLEN 60 /* Min. octets in frame without FCS */ struct eth_header { uint8_t h_dest[ETH_ALEN]; /* destination eth addr */ @@ -422,4 +423,20 @@ bool eth_parse_ipv6_hdr(const struct iovec *pkt, int pkt_frags, size_t ip6hdr_off, eth_ip6_hdr_info *info); +/** + * eth_pad_short_frame - pad a short frame to the minimum Ethernet frame length + * + * If the Ethernet frame size is shorter than 60 bytes, it will be padded to + * 60 bytes at the address @padded_pkt. + * + * @padded_pkt: buffer address to hold the padded frame + * @padded_buflen: pointer holding length of @padded_pkt. If the frame is + * padded, the length will be updated to the padded one. + * @pkt: address to hold the original Ethernet frame + * @pkt_size: size of the original Ethernet frame + * @return true if the frame is padded, otherwise false + */ +bool eth_pad_short_frame(uint8_t *padded_pkt, size_t *padded_buflen, + const void *pkt, size_t pkt_size); + #endif diff --git a/include/net/net.h b/include/net/net.h index a02949f6db..3559f3ca19 100644 --- a/include/net/net.h +++ b/include/net/net.h @@ -103,6 +103,7 @@ struct NetClientState { int vring_enable; int vnet_hdr_len; bool is_netdev; + bool do_not_pad; /* do not pad to the minimum ethernet frame length */ QTAILQ_HEAD(, NetFilterState) filters; }; diff --git a/net/colo-compare.c b/net/colo-compare.c index 84db4978ac..9d1ad99941 100644 --- a/net/colo-compare.c +++ b/net/colo-compare.c @@ -690,7 +690,8 @@ static void colo_compare_packet(CompareState *s, Connection *conn, if (result) { colo_release_primary_pkt(s, pkt); - g_queue_remove(&conn->secondary_list, result->data); + packet_destroy(result->data, NULL); + g_queue_delete_link(&conn->secondary_list, result); } else { /* * If one packet arrive late, the secondary_list or @@ -401,31 +401,29 @@ eth_is_ip6_extension_header_type(uint8_t hdr_type) static bool _eth_get_rss_ex_dst_addr(const struct iovec *pkt, int pkt_frags, - size_t rthdr_offset, + size_t ext_hdr_offset, struct ip6_ext_hdr *ext_hdr, struct in6_address *dst_addr) { - struct ip6_ext_hdr_routing *rthdr = (struct ip6_ext_hdr_routing *) ext_hdr; - - if ((rthdr->rtype == 2) && - (rthdr->len == sizeof(struct in6_address) / 8) && - (rthdr->segleft == 1)) { - - size_t input_size = iov_size(pkt, pkt_frags); - size_t bytes_read; - - if (input_size < rthdr_offset + sizeof(*ext_hdr)) { - return false; - } + struct ip6_ext_hdr_routing rt_hdr; + size_t input_size = iov_size(pkt, pkt_frags); + size_t bytes_read; - bytes_read = iov_to_buf(pkt, pkt_frags, - rthdr_offset + sizeof(*ext_hdr), - dst_addr, sizeof(*dst_addr)); + if (input_size < ext_hdr_offset + sizeof(rt_hdr) + sizeof(*dst_addr)) { + return false; + } - return bytes_read == sizeof(*dst_addr); + bytes_read = iov_to_buf(pkt, pkt_frags, ext_hdr_offset, + &rt_hdr, sizeof(rt_hdr)); + assert(bytes_read == sizeof(rt_hdr)); + if ((rt_hdr.rtype != 2) || (rt_hdr.segleft != 1)) { + return false; } + bytes_read = iov_to_buf(pkt, pkt_frags, ext_hdr_offset + sizeof(rt_hdr), + dst_addr, sizeof(*dst_addr)); + assert(bytes_read == sizeof(*dst_addr)); - return false; + return true; } static bool @@ -528,10 +526,12 @@ bool eth_parse_ipv6_hdr(const struct iovec *pkt, int pkt_frags, } if (curr_ext_hdr_type == IP6_ROUTING) { - info->rss_ex_dst_valid = - _eth_get_rss_ex_dst_addr(pkt, pkt_frags, - ip6hdr_off + info->full_hdr_len, - &ext_hdr, &info->rss_ex_dst); + if (ext_hdr.ip6r_len == sizeof(struct in6_address) / 8) { + info->rss_ex_dst_valid = + _eth_get_rss_ex_dst_addr(pkt, pkt_frags, + ip6hdr_off + info->full_hdr_len, + &ext_hdr, &info->rss_ex_dst); + } } else if (curr_ext_hdr_type == IP6_DESTINATON) { info->rss_ex_src_valid = _eth_get_rss_ex_src_addr(pkt, pkt_frags, @@ -548,3 +548,20 @@ bool eth_parse_ipv6_hdr(const struct iovec *pkt, int pkt_frags, info->l4proto = ext_hdr.ip6r_nxt; return true; } + +bool eth_pad_short_frame(uint8_t *padded_pkt, size_t *padded_buflen, + const void *pkt, size_t pkt_size) +{ + assert(padded_buflen && *padded_buflen >= ETH_ZLEN); + + if (pkt_size >= ETH_ZLEN) { + return false; + } + + /* pad to minimum Ethernet frame length */ + memcpy(padded_pkt, pkt, pkt_size); + memset(&padded_pkt[pkt_size], 0, ETH_ZLEN - pkt_size); + *padded_buflen = ETH_ZLEN; + + return true; +} diff --git a/net/slirp.c b/net/slirp.c index 9454a673d6..a9fdc7a08f 100644 --- a/net/slirp.c +++ b/net/slirp.c @@ -31,6 +31,7 @@ #include <pwd.h> #include <sys/wait.h> #endif +#include "net/eth.h" #include "net/net.h" #include "clients.h" #include "hub.h" @@ -115,6 +116,15 @@ static ssize_t net_slirp_send_packet(const void *pkt, size_t pkt_len, void *opaque) { SlirpState *s = opaque; + uint8_t min_pkt[ETH_ZLEN]; + size_t min_pktsz = sizeof(min_pkt); + + if (!s->nc.peer->do_not_pad) { + if (eth_pad_short_frame(min_pkt, &min_pktsz, pkt, pkt_len)) { + pkt = min_pkt; + pkt_len = min_pktsz; + } + } return qemu_send_packet(&s->nc, pkt, pkt_len); } diff --git a/net/tap-win32.c b/net/tap-win32.c index 21e451107b..d7c2a8759c 100644 --- a/net/tap-win32.c +++ b/net/tap-win32.c @@ -31,6 +31,7 @@ #include "qemu-common.h" #include "clients.h" /* net_init_tap */ +#include "net/eth.h" #include "net/net.h" #include "net/tap.h" /* tap_has_ufo, ... */ #include "qemu/error-report.h" @@ -688,9 +689,18 @@ static void tap_win32_send(void *opaque) uint8_t *buf; int max_size = 4096; int size; + uint8_t min_pkt[ETH_ZLEN]; + size_t min_pktsz = sizeof(min_pkt); size = tap_win32_read(s->handle, &buf, max_size); if (size > 0) { + if (!s->nc.peer->do_not_pad) { + if (eth_pad_short_frame(min_pkt, &min_pktsz, buf, size)) { + buf = min_pkt; + size = min_pktsz; + } + } + qemu_send_packet(&s->nc, buf, size); tap_win32_free_buffer(s->handle, buf); } @@ -32,6 +32,7 @@ #include <sys/socket.h> #include <net/if.h> +#include "net/eth.h" #include "net/net.h" #include "clients.h" #include "monitor/monitor.h" @@ -189,6 +190,8 @@ static void tap_send(void *opaque) while (true) { uint8_t *buf = s->buf; + uint8_t min_pkt[ETH_ZLEN]; + size_t min_pktsz = sizeof(min_pkt); size = tap_read_packet(s->fd, s->buf, sizeof(s->buf)); if (size <= 0) { @@ -200,6 +203,13 @@ static void tap_send(void *opaque) size -= s->host_vnet_hdr_len; } + if (!s->nc.peer->do_not_pad) { + if (eth_pad_short_frame(min_pkt, &min_pktsz, buf, size)) { + buf = min_pkt; + size = min_pktsz; + } + } + size = qemu_send_packet_async(&s->nc, buf, size, tap_send_completed); if (size == 0) { tap_read_poll(s, false); diff --git a/target/mips/mxu_translate.c b/target/mips/mxu_translate.c index afc008eeee..fb0a811af6 100644 --- a/target/mips/mxu_translate.c +++ b/target/mips/mxu_translate.c @@ -1095,12 +1095,12 @@ static void gen_mxu_D16MAX_D16MIN(DisasContext *ctx) if (unlikely(pad != 0)) { /* opcode padding incorrect -> do nothing */ - } else if (unlikely(XRc == 0)) { + } else if (unlikely(XRa == 0)) { /* destination is zero register -> do nothing */ - } else if (unlikely((XRb == 0) && (XRa == 0))) { + } else if (unlikely((XRb == 0) && (XRc == 0))) { /* both operands zero registers -> just set destination to zero */ - tcg_gen_movi_i32(mxu_gpr[XRc - 1], 0); - } else if (unlikely((XRb == 0) || (XRa == 0))) { + tcg_gen_movi_i32(mxu_gpr[XRa - 1], 0); + } else if (unlikely((XRb == 0) || (XRc == 0))) { /* exactly one operand is zero register - find which one is not...*/ uint32_t XRx = XRb ? XRb : XRc; /* ...and do half-word-wise max/min with one operand 0 */ diff --git a/tests/qtest/fuzz-e1000e-test.c b/tests/qtest/fuzz-e1000e-test.c new file mode 100644 index 0000000000..66229e6096 --- /dev/null +++ b/tests/qtest/fuzz-e1000e-test.c @@ -0,0 +1,53 @@ +/* + * QTest testcase for e1000e device generated by fuzzer + * + * Copyright (c) 2021 Red Hat, Inc. + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#include "qemu/osdep.h" + +#include "libqos/libqtest.h" + +/* + * https://bugs.launchpad.net/qemu/+bug/1879531 + */ +static void test_lp1879531_eth_get_rss_ex_dst_addr(void) +{ + QTestState *s; + + s = qtest_init("-nographic -monitor none -serial none -M pc-q35-5.0"); + + qtest_outl(s, 0xcf8, 0x80001010); + qtest_outl(s, 0xcfc, 0xe1020000); + qtest_outl(s, 0xcf8, 0x80001004); + qtest_outw(s, 0xcfc, 0x7); + qtest_writeb(s, 0x25, 0x86); + qtest_writeb(s, 0x26, 0xdd); + qtest_writeb(s, 0x4f, 0x2b); + + qtest_writel(s, 0xe1020030, 0x190002e1); + qtest_writew(s, 0xe102003a, 0x0807); + qtest_writel(s, 0xe1020048, 0x12077cdd); + qtest_writel(s, 0xe1020400, 0xba077cdd); + qtest_writel(s, 0xe1020420, 0x190002e1); + qtest_writel(s, 0xe1020428, 0x3509d807); + qtest_writeb(s, 0xe1020438, 0xe2); + qtest_writeb(s, 0x4f, 0x2b); + qtest_quit(s); +} + +int main(int argc, char **argv) +{ + const char *arch = qtest_get_arch(); + + g_test_init(&argc, &argv, NULL); + + if (strcmp(arch, "i386") == 0 || strcmp(arch, "x86_64") == 0) { + qtest_add_func("fuzz/test_lp1879531_eth_get_rss_ex_dst_addr", + test_lp1879531_eth_get_rss_ex_dst_addr); + } + + return g_test_run(); +} diff --git a/tests/qtest/meson.build b/tests/qtest/meson.build index 9731606c31..902cfef7cb 100644 --- a/tests/qtest/meson.build +++ b/tests/qtest/meson.build @@ -67,6 +67,7 @@ qtests_i386 = \ (config_all_devices.has_key('CONFIG_TPM_TIS_ISA') ? ['tpm-tis-test'] : []) + \ (config_all_devices.has_key('CONFIG_TPM_TIS_ISA') ? ['tpm-tis-swtpm-test'] : []) + \ (config_all_devices.has_key('CONFIG_RTL8139_PCI') ? ['rtl8139-test'] : []) + \ + (config_all_devices.has_key('CONFIG_E1000E_PCI_EXPRESS') ? ['fuzz-e1000e-test'] : []) + \ qtests_pci + \ ['fdc-test', 'ide-test', |