QEMU RTL8139 Driver Issues (Buffer is zero)
Posted: Sat Jan 17, 2015 9:46 am
Hello. I recently (not really that recent) rewrote a large part of my OS, and in the process left out some non-important things in the rewrite, specifically my whole network stack.
Unfortunately, when it came time to reassemble the pieces, I discovered a problem in the RTL8139 driver (I only have drivers for that one card). Note that it has worked before, and did not change in the year in which i did not touch it. (that's no indication of much though)
Specifically, the problem is that the NIC will issue an IRQ with an Rx OK, but when I check the buffer, it's only zeroes (verified by checking memory in QEMU's monitor). This is the very first packet received, so most of the things below like ReadOffset and SeenOfs are zero. Therefore I'm just reading from the beginning of the buffer for 0x248 bytes (which is the value of RxBufAddr). 0x248 bytes of zeroes, that is...
I can send packets just fine -- I sent a DHCP Discover broadcast, and using wireshark I see that indeed I get a DHCP offer, but that I can't actually 'receive' it due to this problem.
And here is the initialisation code:
Help is much appreciated, thanks!
Unfortunately, when it came time to reassemble the pieces, I discovered a problem in the RTL8139 driver (I only have drivers for that one card). Note that it has worked before, and did not change in the year in which i did not touch it. (that's no indication of much though)
Specifically, the problem is that the NIC will issue an IRQ with an Rx OK, but when I check the buffer, it's only zeroes (verified by checking memory in QEMU's monitor). This is the very first packet received, so most of the things below like ReadOffset and SeenOfs are zero. Therefore I'm just reading from the beginning of the buffer for 0x248 bytes (which is the value of RxBufAddr). 0x248 bytes of zeroes, that is...
I can send packets just fine -- I sent a DHCP Discover broadcast, and using wireshark I see that indeed I get a DHCP offer, but that I can't actually 'receive' it due to this problem.
Code: Select all
void RTL8139::HandlePacket()
{
uint64_t ReadOffset = 0;
uint64_t EndOffset = 0;
uint64_t PacketCount = 0;
uint64_t length = 0;
EndOffset = IOPort::Read16(this->ioaddr + Registers::RxBufAddr);
ReadOffset = this->SeenOfs;
if(ReadOffset > EndOffset)
{
while(ReadOffset < RxBufferSize)
{
PacketCount++;
length = *(uint16_t*) &this->ReceiveBuffer[ReadOffset + 2];
Ethernet::HandlePacket(this, &this->ReceiveBuffer[ReadOffset + 4], length - 4);
if(length > 2000)
HALT("What");
ReadOffset += length + 4;
ReadOffset = (ReadOffset + 3) & ~3; // align to 4 bytes
}
ReadOffset -= RxBufferSize;
}
while(ReadOffset < EndOffset)
{
PacketCount++;
Ethernet::HandlePacket(this, &this->ReceiveBuffer[ReadOffset + 4], *(uint16_t*) &this->ReceiveBuffer[ReadOffset + 2] - 4);
ReadOffset += *(uint16_t*) &this->ReceiveBuffer[ReadOffset + 2] + 4;
ReadOffset = (ReadOffset + 3) & ~3; // align to 4 bytes
}
this->SeenOfs = ReadOffset;
}
And here is the initialisation code:
Code: Select all
// enable bus mastering
uint32_t f = this->pcidev->GetRegisterData(0x4, 0, 2);
this->pcidev->WriteRegisterData(0x4, 0, 2, (f | 0x4) & ~0x400);
uint32_t mmio = (uint32_t) this->pcidev->GetBAR(0);
assert(this->pcidev->IsBARIOPort(0));
Log("Initialised Busmastering RTL8139 NIC with BaseIO %x", mmio);
this->ioaddr = (uint16_t) mmio;
Interrupts::InstallIRQHandler(this->pcidev->GetInterruptLine(), StaticHandleInterrupt, this);
// power on
IOPort::WriteByte(this->ioaddr + Registers::Config1, 0x0);
this->Reset();
Log("RTL8139 Software reset complete.");
// get mac address
Memory::Set(this->MAC, 0, sizeof(this->MAC));
for(uint8_t i = 0; i < 6; i++)
{
this->MAC[i] = IOPort::ReadByte(this->ioaddr + Registers::MAC0 + i);
}
Log("Initialised RTL8139 NIC with MAC address %#02x:%#02x:%#02x:%#02x:%#02x:%#02x", this->MAC[0], this->MAC[1], this->MAC[2], this->MAC[3], this->MAC[4], this->MAC[5]);
// init rx buffer.
// setup 8K + 1500 + 16
// 3 pages contiguous
this->ReceiveBuffer = (uint8_t*) MemoryManager::Physical::AllocateDMA(3);
Log("Configured 12kb buffer for RX at %x", this->ReceiveBuffer);
IOPort::Write32(this->ioaddr + Registers::RxBuf, (uint32_t) (uint64_t) this->ReceiveBuffer);
IOPort::Write16(this->ioaddr + Registers::RxBufPtr, 0);
IOPort::Write16(this->ioaddr + Registers::RxBufAddr, 0);
IOPort::Write16(this->ioaddr + Registers::IntrMask, 0xF);
// setup transmit buffers.
// 1500 bytes each, 4 buffers
// 2 pages, non contiguous.
this->TransmitBuffers[0] = MemoryManager::Physical::AllocateDMA(1);
this->TransmitBuffers[1] = this->TransmitBuffers[0] + 0x800;
this->TransmitBuffers[2] = MemoryManager::Physical::AllocateDMA(1);
this->TransmitBuffers[3] = this->TransmitBuffers[2] + 0x800;
// send data to the card
IOPort::Write32(this->ioaddr + Registers::TxAddr0, (uint32_t) this->TransmitBuffers[0]);
IOPort::Write32(this->ioaddr + Registers::TxAddr1, (uint32_t) this->TransmitBuffers[1]);
IOPort::Write32(this->ioaddr + Registers::TxAddr2, (uint32_t) this->TransmitBuffers[2]);
IOPort::Write32(this->ioaddr + Registers::TxAddr3, (uint32_t) this->TransmitBuffers[3]);
// set size (max, 8K + 15 + WRAP = 1)
IOPort::Write32(this->ioaddr + Registers::RxConfig, 0x8F);
// enable things.
IOPort::WriteByte(this->ioaddr + Registers::Command, 0x0C);
Help is much appreciated, thanks!