diff --git a/hw/net/ftgmac100.c b/hw/net/ftgmac100.c index 280aa3d3a1..7c9fa720df 100644 --- a/hw/net/ftgmac100.c +++ b/hw/net/ftgmac100.c @@ -481,6 +481,37 @@ static int ftgmac100_write_bd(FTGMAC100Desc *bd, dma_addr_t addr) return 0; } +static int ftgmac100_insert_vlan(FTGMAC100State *s, int frame_size, + uint8_t vlan_tci) +{ + uint8_t *vlan_hdr = s->frame + (ETH_ALEN * 2); + uint8_t *payload = vlan_hdr + sizeof(struct vlan_header); + + if (frame_size < sizeof(struct eth_header)) { + qemu_log_mask(LOG_GUEST_ERROR, + "%s: frame too small for VLAN insertion : %d bytes\n", + __func__, frame_size); + s->isr |= FTGMAC100_INT_XPKT_LOST; + goto out; + } + + if (frame_size + sizeof(struct vlan_header) > sizeof(s->frame)) { + qemu_log_mask(LOG_GUEST_ERROR, + "%s: frame too big : %d bytes\n", + __func__, frame_size); + s->isr |= FTGMAC100_INT_XPKT_LOST; + frame_size -= sizeof(struct vlan_header); + } + + memmove(payload, vlan_hdr, frame_size - (ETH_ALEN * 2)); + stw_be_p(vlan_hdr, ETH_P_VLAN); + stw_be_p(vlan_hdr + 2, vlan_tci); + frame_size += sizeof(struct vlan_header); + +out: + return frame_size; +} + static void ftgmac100_do_tx(FTGMAC100State *s, uint32_t tx_ring, uint32_t tx_descriptor) { @@ -530,25 +561,17 @@ static void ftgmac100_do_tx(FTGMAC100State *s, uint32_t tx_ring, break; } - /* Check for VLAN */ - if (bd.des0 & FTGMAC100_TXDES0_FTS && - bd.des1 & FTGMAC100_TXDES1_INS_VLANTAG && - be16_to_cpu(PKT_GET_ETH_HDR(ptr)->h_proto) != ETH_P_VLAN) { - if (frame_size + len + 4 > sizeof(s->frame)) { - qemu_log_mask(LOG_GUEST_ERROR, "%s: frame too big : %d bytes\n", - __func__, len); - s->isr |= FTGMAC100_INT_XPKT_LOST; - len = sizeof(s->frame) - frame_size - 4; - } - memmove(ptr + 16, ptr + 12, len - 12); - stw_be_p(ptr + 12, ETH_P_VLAN); - stw_be_p(ptr + 14, bd.des1); - len += 4; - } - ptr += len; frame_size += len; if (bd.des0 & FTGMAC100_TXDES0_LTS) { + + /* Check for VLAN */ + if (flags & FTGMAC100_TXDES1_INS_VLANTAG && + be16_to_cpu(PKT_GET_ETH_HDR(s->frame)->h_proto) != ETH_P_VLAN) { + frame_size = ftgmac100_insert_vlan(s, frame_size, + FTGMAC100_TXDES1_VLANTAG_CI(flags)); + } + if (flags & FTGMAC100_TXDES1_IP_CHKSUM) { net_checksum_calculate(s->frame, frame_size); }