Intel 8254x Receive Problems
Posted: Tue Jan 02, 2018 12:16 am
I'm having some trouble receiving packets. I am trying to use the 82540EM network adapter on Virtualbox. The emulated adapter is bridged to the adapter on the host machine, so there are packets to be received. It seems to receive packets for a few seconds, but quits. There are also lots of dropped packets with 0 length, without an EOP flag set in the status, or status = 1. I adapted my driver partially from an example linked on the wiki.
I've only included the functions which I believe to not be functioning correctly. I know the interrupt processing is working, and I know the MMIO is working. I've tried setting the head and tail registers to different values and different ways of polling RX. With some values it seems to be signalling an interrupt when there's a packet, but the descriptors say otherwise. With others, it will recognize packets, but after about 20 to 30 seconds, it stops. Is the emulation of the chip just that bad, or is my code bad?
Here is the relevant code:
I've only included the functions which I believe to not be functioning correctly. I know the interrupt processing is working, and I know the MMIO is working. I've tried setting the head and tail registers to different values and different ways of polling RX. With some values it seems to be signalling an interrupt when there's a packet, but the descriptors say otherwise. With others, it will recognize packets, but after about 20 to 30 seconds, it stops. Is the emulation of the chip just that bad, or is my code bad?
Here is the relevant code:
Code: Select all
void i8254x_init_card(int i) { // i is an index to an the array of adapters
pci_register_irq_handler_idx(i8254x_cards[i].pci_idx,i8254x_irq_handler);
pci_write_config_dword_idx(i8254x_cards[i].pci_idx,PCI_CONFIG_COMMAND,(pci_read_config_dword_idx(i8254x_cards[i].pci_idx,PCI_CONFIG_COMMAND) | (1<<2))); // enable pci busmastering, if it helps
i8254x_mmio_write32(i,I8254X_REG_IMS,0x1f6dc); // set interrupt mask; should be 0x1ffff, 0x17fff or maybe 0x1f6dc?
i8254x_init_mta(i); // init multicast table
i8254x_set_link_up(i); // set CTRL.SLU, CTRL.ASDE and clear CTRL.LRST
i8254x_init_rx(i); // init receive
i8254x_init_tx(i); // init transmit
}
void i8254x_init_rx(int i) {
u8 mac[6];
i8254x_cards[i].rx_descs = pfa_alloc(I8254X_RXDESC_PGCOUNT); // allocate memory (in pages) for rx descriptors
for (int j=0;j<(256*I8254X_RXDESC_PGCOUNT);j++) { // iterate through each rx descriptor
i8254x_cards[i].rx_descs[i].address = (u32)PG_V2P(pfa_alloc(2)); // allocate 8192 byte packet, and write physical address
i8254x_cards[i].rx_descs[i].status = 0; // clear status so the controller knows it can be used
}
i8254x_read_mac(i, &mac); // read mac from eeprom
i8254x_mmio_write32(i, I8254X_REG_RAL + (i*8), ((mac[3]<<24) | (mac[2]<<16) | (mac[1]<<8) | mac[0])); // set up MAC filter
i8254x_mmio_write32(i, I8254X_REG_RAH + (i*8), (0x80000000 | (mac[5]<<8) | mac[4]));
i8254x_mmio_write32(i, I8254X_REG_RDBAH, 0); // no 64 bit here
i8254x_mmio_write32(i, I8254X_REG_RDBAL, (u32) PG_V2P(i8254x_cards[i].rx_descs)); // set pointer to descriptor array
i8254x_mmio_write32(i, I8254X_REG_RDLEN, I8254X_RXDESC_COUNT * 16); //I8254X_RXDESC_PGCOUNT * I8254X_RXDESC_PER_PAGE * 16
i8254x_mmio_write32(i, I8254X_REG_RDH, 0); // set head to 0, as the manual says
i8254x_mmio_write32(i, I8254X_REG_RDT, I8254X_RXDESC_COUNT); // set tail to the index after the last descriptor, as the manual says
i8254x_cards[i].rx_tail = 0; // set our tail to 0, because that works
i8254x_mmio_write32(i, I8254X_REG_RCTL, (I8254X_RCTL_SBP | I8254X_RCTL_UPE | I8254X_RCTL_MPE | I8254X_RDMTS_HALF | I8254X_RCTL_SECRC | I8254X_RCTL_LPE | I8254X_RCTL_BAM | I8254X_RCTL_BSIZE_8192)); // set the receive control register (promisc, 8K pkt size)
}
int i8254x_irq_handler(u8 irq) {
int status = 0;
for (int i=0; i<i8254x_count; i++) {
if (i8254x_cards[i].pci_irq == irq) status = i8254x_irq_handler2(i);
if (status != 0) break;
}
return status;
}
int i8254x_irq_handler2(int idx) {
u32 icr = i8254x_mmio_read32(idx,I8254X_REG_ICR); // read interrupt code
u32 sicr = icr; // save icr for later
if (!icr) return 0; // return 0 (irq not processed) if no interrupt flags set
if (icr & I8254X_ICR_LSC) { // link status change
printf("i8254x_irq_handler2: link status change\n");
icr &= ~(I8254X_ICR_LSC); // clear bit
i8254x_enable_rx(idx);
}
if ((icr & I8254X_ICR_RXT0) || (icr & I8254X_ICR_RXDMT0)) { // packet rx'd
icr &= ~(I8254X_ICR_RXT0 | I8254X_ICR_RXDMT0); // clear bits
i8254x_rx_poll(idx);
}
printf("i8254x_irq_handler2: rxfifo status: h=%u t=%u(%u)\n",i8254x_mmio_read32(idx,I8254X_REG_RDH),i8254x_mmio_read32(idx,I8254X_REG_RDT),i8254x_cards[idx].rx_tail);
printf("i8254x_irq_handler2: %u (%u)\n",(unsigned int)icr,(unsigned int)sicr);
i8254x_mmio_read32(idx,I8254X_REG_ICR); // clear interrupts; is this necessary?
return 1; // tell caller the irq was handled
}
void i8254x_enable_rx(int i) {
i8254x_mmio_write32(i, I8254X_REG_RCTL, i8254x_mmio_read32(i, I8254X_REG_RCTL) | (I8254X_RCTL_EN));
}
void i8254x_rx_poll(int idx) {
printf("i8254x_rx_poll: status=%u\n",i8254x_cards[idx].rx_descs[i8254x_cards[idx].rx_tail].status);
while ((i8254x_cards[idx].rx_descs[i8254x_cards[idx].rx_tail].status & I8254X_RXDSTS_DD)) { // while the tail packet has STATUS.DD set
u16 pkt_len = i8254x_cards[idx].rx_descs[i8254x_cards[idx].rx_tail].length; // packet length
bool dropflag = FALSE;
if (pkt_len < 60) { // small packet
//printf("i8254x_rx_poll: small packet, %u bytes; dropping\n",pkt_len);
printf("d");
dropflag = TRUE;
}
if (!(i8254x_cards[idx].rx_descs[i8254x_cards[idx].rx_tail].status & I8254X_RXDSTS_EOP)) { // eop not set
//printf("i8254x_rx_poll: eop not set? dropping\n");
printf("D");
dropflag = TRUE;
}
if (i8254x_cards[idx].rx_descs[i8254x_cards[idx].rx_tail].errors) { // packet had errors
printf("i8254x_rx_poll: packet had errors; dropping\n");
dropflag = TRUE;
}
if (!dropflag) { // packet has no reason to be dropped
printf("i8254x_rx_poll: good packet # %u, %u bytes\n",i8254x_cards[idx].rx_count,pkt_len);
i8254x_cards[idx].rx_count++;
}
i8254x_cards[idx].rx_descs[i8254x_cards[idx].rx_tail].status = 0; // inform hardware descriptor is ready to use
i8254x_cards[idx].rx_tail = (i8254x_cards[idx].rx_tail + 1) % I8254X_RXDESC_COUNT; // set tail to next descriptor, wrap if necessary
i8254x_mmio_write32(idx, I8254X_REG_RDT, i8254x_cards[idx].rx_tail); // inform hardware of new tail
i8254x_mmio_read32(idx, I8254X_REG_ICR); // clear interrupts?
}
}