Page 1 of 1

My NE2000 send doesn't work

Posted: Sat Oct 03, 2015 11:15 am
by mariuszp
I have attempted to write a driver for the NE2000 NIC:

Code: Select all

static void ne2k_setremaddr(NeInterface *nif, uint32_t val)
{
	outb(nif->iobase + 0x08, (uint8_t) (val & 0xFF));
	outb(nif->iobase + 0x09, (uint8_t) ((val >> 8) & 0xFF));
};

static void ne2k_setremcount(NeInterface *nif, size_t val)
{
	outb(nif->iobase + 0x0A, (uint8_t) (val & 0xFF));
	outb(nif->iobase + 0x0B, (uint8_t) ((val >> 8) & 0xFF));
};

static void ne2k_write(NeInterface *nif, uint32_t addr, const void *buffer, size_t size)
{
	ne2k_setremaddr(nif, addr);
	ne2k_setremcount(nif, size);

	outb(nif->iobase + 0x0E, 0x48);		// set byte-wide access
	const uint8_t *scan = (const uint8_t*) buffer;

	outb(nif->iobase, 0x12);		// write DMA + start
	while (size--)
	{
		outb(nif->iobase + 0x10, *scan++);
	};
	
	while ((inb(nif->iobase + 0x07) & 0x40) == 0);
};

static void ne2k_settxcount(NeInterface *nif, size_t count)
{
	outb(nif->iobase + 0x05, (uint8_t) (count & 0xFF));
	outb(nif->iobase + 0x06, (uint8_t) ((count >> 8) & 0xFF));
};

static void ne2k_send(NetIf *netif, const void *frame, size_t framelen)
{
	(void)netif;
	(void)frame;
	(void)framelen;

	kprintf_debug("ne2k_send called (size=%d):\n", (int) framelen);
	EthernetHeader *head = (EthernetHeader*) frame;
	kprintf_debug("\tDST: %x:%x:%x:%x:%x:%x\n", head->dest.addr[0], head->dest.addr[1], head->dest.addr[2],
						head->dest.addr[3], head->dest.addr[4], head->dest.addr[5]);
	kprintf_debug("\tSRC: %x:%x:%x:%x:%x:%x\n", head->src.addr[0], head->src.addr[1], head->src.addr[2],
						head->src.addr[3], head->src.addr[4], head->src.addr[5]);
	kprintf_debug("\tTYP: 0x%x\n", __builtin_bswap16(head->type));
	kprintf_debug("\tCHK: 0x%x\n", ether_checksum(frame, framelen));

	NeInterface *nif = (NeInterface*) netif->drvdata;
	spinlockAcquire(&nif->lock);
	
	// write the frame to the transmit buffer
	ne2k_write(nif, (16*1024)/256, frame, framelen);
	
	// specify the location of transmission buffer in NIC memory
	outb(nif->iobase + 0x04, (16*1024)/256);
	
	// specify the size of the frame
	ne2k_settxcount(nif, framelen);

	// transmit
	outb(nif->iobase, 0x26);
	
	// wait for transmit to complete
	while ((inb(nif->iobase) & 0x04) == 0);
	
	spinlockRelease(&nif->lock);
	kprintf_debug("PACKET SENT\n");
};
When I attempt to send an ICMP Echo Request to 255.255.255.255 (which the OS automatically maps to MAC ff:ff:ff:ff:ff:ff) just as a test, I get the following in the debug console (from the kprintf_debug()s):

Code: Select all

PING 255.255.255.255 (255.255.255.255)
ne2k_send called (size=64):
	DST: ff:ff:ff:ff:ff:ff
	SRC: b0:c4:20:0:0:0
	TYP: 0x800
	CHK: 0x2144df1c
PACKET SENT
But in the ne2k-pktlog file from Bochs I get:

Code: Select all

vnet packetmover readable log file
TFTP root = xl0
host MAC address = b0:c4:20:00:00:03
guest MAC address = b0:c4:20:00:00:00
--
a packet from guest to host, length 64
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
--
What this code is trying to do is write the frame to NIC memory (using the "DMA Write") and then I transmit from that buffer, but clearly it's sending all zeroes for some reason.

Does anyone know why this is happening?

Re: My NE2000 send doesn't work

Posted: Sun Oct 04, 2015 3:35 am
by mariuszp
Bump

Re: My NE2000 send doesn't work

Posted: Sun Oct 04, 2015 12:03 pm
by SpyderTL
This code is a little hard for me to follow, but everything appears to be right at first glance. So the problem is probably with one of your assumptions.

Add the buffer address and the first 64 bytes to your debug output.

Also, try a different OS using this exact setup and make sure the network works properly. Let us know what you find out.

Re: My NE2000 send doesn't work

Posted: Sun Oct 04, 2015 2:18 pm
by mariuszp
I have added what you have requested to the debug output:

Code: Select all

PING 255.255.255.255 (255.255.255.255)
ne2k_send called (size=64):
	DST: ff:ff:ff:ff:ff:ff
	SRC: b0:c4:20:0:0:0
	TYP: 0x800
	CRC: 0x2144df1c
	Buffer address: 0xFFFF8100017CB2D0
ff ff ff ff ff ff b0 c4 20 00 00 00 08 00 45 00 
00 1c 01 00 00 00 40 01 79 e2 00 00 00 00 ff ff 
ff ff 08 00 ef ff 08 00 00 00 00 00 00 00 00 00 
00 00 00 00 00 00 00 00 00 00 00 00 84 da c8 85 
PACKET SENT
"Buffer address" is the address of 'frame'. The address it shows is on the kernel heap. Again it sends only zeroes.

My bochsrc file is as follows:

Code: Select all

ata0-master: type=disk, mode=flat, path=hdd.bin, cylinders=306, heads=4, spt=17
ata0-slave: type=cdrom, path=out/glidix.iso, status=inserted
megs: 512
magic_break: enabled=1
boot: cdrom
port_e9_hack: enabled=1
pci: enabled=1, chipset=i440fx, slot1=pcivga, slot2=ne2k
cpuid: apic=xapic
ne2k: ioaddr=0x300, irq=10, mac=b0:c4:20:00:00:00, ethmod=vnet
(Using "vnet" for easy debugging).

Re: My NE2000 send doesn't work

Posted: Sun Oct 04, 2015 2:55 pm
by mariuszp
OK guys i figured it out.

Code: Select all

ne2k_write(nif, (16*1024)/256, frame, framelen);
Should actually be:

Code: Select all

ne2k_write(nif, 16*1024, frame, framelen);
Because ne2k_write() accepts the actual ring buffer address, not the page number.

Re: My NE2000 send doesn't work

Posted: Mon Oct 12, 2015 12:59 pm
by Ready4Dis
Because ne2k_write() accepts the actual ring buffer address, not the page number.
Thanks for the heads up, always nice when someone comes back and posts the solution when they find it!