aboutsummaryrefslogtreecommitdiff
path: root/hw/net/ftgmac100.c
diff options
context:
space:
mode:
Diffstat (limited to 'hw/net/ftgmac100.c')
-rw-r--r--hw/net/ftgmac100.c64
1 files changed, 44 insertions, 20 deletions
diff --git a/hw/net/ftgmac100.c b/hw/net/ftgmac100.c
index 3300e8ef4a..909c1182ee 100644
--- a/hw/net/ftgmac100.c
+++ b/hw/net/ftgmac100.c
@@ -207,16 +207,18 @@ typedef struct {
/*
* Max frame size for the receiving buffer
*/
-#define FTGMAC100_MAX_FRAME_SIZE 10240
+#define FTGMAC100_MAX_FRAME_SIZE 9220
/* Limits depending on the type of the frame
*
* 9216 for Jumbo frames (+ 4 for VLAN)
* 1518 for other frames (+ 4 for VLAN)
*/
-static int ftgmac100_max_frame_size(FTGMAC100State *s)
+static int ftgmac100_max_frame_size(FTGMAC100State *s, uint16_t proto)
{
- return (s->maccr & FTGMAC100_MACCR_JUMBO_LF ? 9216 : 1518) + 4;
+ int max = (s->maccr & FTGMAC100_MACCR_JUMBO_LF ? 9216 : 1518);
+
+ return max + (proto == ETH_P_VLAN ? 4 : 0);
}
static void ftgmac100_update_irq(FTGMAC100State *s)
@@ -408,7 +410,6 @@ static void ftgmac100_do_tx(FTGMAC100State *s, uint32_t tx_ring,
uint8_t *ptr = s->frame;
uint32_t addr = tx_descriptor;
uint32_t flags = 0;
- int max_frame_size = ftgmac100_max_frame_size(s);
while (1) {
FTGMAC100Desc bd;
@@ -427,11 +428,12 @@ static void ftgmac100_do_tx(FTGMAC100State *s, uint32_t tx_ring,
flags = bd.des1;
}
- len = bd.des0 & 0x3FFF;
- if (frame_size + len > max_frame_size) {
+ len = FTGMAC100_TXDES0_TXBUF_SIZE(bd.des0);
+ if (frame_size + len > sizeof(s->frame)) {
qemu_log_mask(LOG_GUEST_ERROR, "%s: frame too big : %d bytes\n",
__func__, len);
- len = max_frame_size - frame_size;
+ s->isr |= FTGMAC100_INT_XPKT_LOST;
+ len = sizeof(s->frame) - frame_size;
}
if (dma_memory_read(&address_space_memory, bd.des3, ptr, len)) {
@@ -441,6 +443,22 @@ 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) {
@@ -758,8 +776,8 @@ static int ftgmac100_filter(FTGMAC100State *s, const uint8_t *buf, size_t len)
return 0;
}
- /* TODO: this does not seem to work for ftgmac100 */
- mcast_idx = net_crc32(buf, ETH_ALEN) >> 26;
+ mcast_idx = net_crc32_le(buf, ETH_ALEN);
+ mcast_idx = (~(mcast_idx >> 2)) & 0x3f;
if (!(s->math[mcast_idx / 32] & (1 << (mcast_idx % 32)))) {
return 0;
}
@@ -788,7 +806,8 @@ static ssize_t ftgmac100_receive(NetClientState *nc, const uint8_t *buf,
uint32_t buf_len;
size_t size = len;
uint32_t first = FTGMAC100_RXDES0_FRS;
- int max_frame_size = ftgmac100_max_frame_size(s);
+ uint16_t proto = be16_to_cpu(PKT_GET_ETH_HDR(buf)->h_proto);
+ int max_frame_size = ftgmac100_max_frame_size(s, proto);
if ((s->maccr & (FTGMAC100_MACCR_RXDMA_EN | FTGMAC100_MACCR_RXMAC_EN))
!= (FTGMAC100_MACCR_RXDMA_EN | FTGMAC100_MACCR_RXMAC_EN)) {
@@ -803,12 +822,6 @@ static ssize_t ftgmac100_receive(NetClientState *nc, const uint8_t *buf,
return size;
}
- if (size < 64 && !(s->maccr & FTGMAC100_MACCR_RX_RUNT)) {
- qemu_log_mask(LOG_GUEST_ERROR, "%s: dropped runt frame of %zd bytes\n",
- __func__, size);
- return size;
- }
-
if (!ftgmac100_filter(s, buf, size)) {
return size;
}
@@ -820,9 +833,9 @@ static ssize_t ftgmac100_receive(NetClientState *nc, const uint8_t *buf,
/* Huge frames are truncated. */
if (size > max_frame_size) {
- size = max_frame_size;
qemu_log_mask(LOG_GUEST_ERROR, "%s: frame too big : %zd bytes\n",
__func__, size);
+ size = max_frame_size;
flags |= FTGMAC100_RXDES0_FTL;
}
@@ -861,7 +874,20 @@ static ssize_t ftgmac100_receive(NetClientState *nc, const uint8_t *buf,
buf_len += size - 4;
}
buf_addr = bd.des3;
- dma_memory_write(&address_space_memory, buf_addr, buf, buf_len);
+ if (first && proto == ETH_P_VLAN && buf_len >= 18) {
+ bd.des1 = lduw_be_p(buf + 14) | FTGMAC100_RXDES1_VLANTAG_AVAIL;
+
+ if (s->maccr & FTGMAC100_MACCR_RM_VLAN) {
+ dma_memory_write(&address_space_memory, buf_addr, buf, 12);
+ dma_memory_write(&address_space_memory, buf_addr + 12, buf + 16,
+ buf_len - 16);
+ } else {
+ dma_memory_write(&address_space_memory, buf_addr, buf, buf_len);
+ }
+ } else {
+ bd.des1 = 0;
+ dma_memory_write(&address_space_memory, buf_addr, buf, buf_len);
+ }
buf += buf_len;
if (size < 4) {
dma_memory_write(&address_space_memory, buf_addr + buf_len,
@@ -940,8 +966,6 @@ static void ftgmac100_realize(DeviceState *dev, Error **errp)
object_get_typename(OBJECT(dev)), DEVICE(dev)->id,
s);
qemu_format_nic_info_str(qemu_get_queue(s->nic), s->conf.macaddr.a);
-
- s->frame = g_malloc(FTGMAC100_MAX_FRAME_SIZE);
}
static const VMStateDescription vmstate_ftgmac100 = {