3

I have a custom, DOS-like OS built in NASM completely (no C code). It is very modest (it does have a FAT file system, few apps, is in real mode, etc.) I want to write a command that will list all the network devices (network cards) that are currently connected.

My assumptions go like this: I will need to write a driver for the network card (I'd put it manually inside kernel for simplicity, so dynamic loading would NOT exist), but it would be enough for that driver to just provide the name of the card, the network card wouldn't actually need to work. How do I tell the OS to connect that function to precisely that one network card? This is what I'm in the blue about, I have no idea how the OS usually matches a piece of hardware to code (its driver(s)).

Michael Petch
  • 46,082
  • 8
  • 107
  • 198
Sasha
  • 43
  • 6
  • Are these devices on the PCI bus? If so you could get the Device and Vendor ID and match them to a driver. http://wiki.osdev.org/PCI . If using old Legacy ISA type devices you usually had to query specific ports (different for most vendors) to determine if the device responded a specific way and then matched that to a driver. – Michael Petch Aug 14 '15 at 17:03
  • @MichaelPetch I'm using a Macbook Air 13" from 2014, and it says **This computer doesn't contain any PCI cards or devices. If you installed or connected, a PCI card or device, make sure they’re properly installed.** – Sasha Aug 14 '15 at 17:19
  • @MichaelPetch What I do have though: `Interfaces: en0: Card Type: AirPort Extreme (0x14E4, 0x117) Firmware Version: Broadcom BCM43xx 1.0 (7.15.159.13.12) MAC Address: 9c:f3:87:bb:08:70 Locale: ETSI Country Code: GB Supported PHY Modes: 802.11 a/b/g/n/ac Supported Channels: ... Wake On Wireless: Supported AirDrop: Supported Status: Connected` – Sasha Aug 14 '15 at 17:20
  • Are you running your OS under OS/X or do you boot directly to your OS? – Michael Petch Aug 14 '15 at 17:21
  • @MichaelPetch I'm actually coding it in Windows in VirtualBox, but I boot my OS in DOSBox in that Windows virtual machine – Sasha Aug 14 '15 at 17:25
  • Ah, okay. Dosbox creates virtual devices. When it comes to networking it emulates an NE2000 network card (Which was an early ISA device). To detect whether it is present you'll need to look up NE2000 specific documentation to properly detect and use the device (DMA/Irq/port information). When you use Dosbox you generally aren't talking directly to the real hardware of the host. – Michael Petch Aug 14 '15 at 17:32
  • One thing you might do is look at some existing NE2000 (aka NE2k) drivers for other OSes. For example there is some C code here http://www.jbox.dk/sanos/source/sys/dev/ne2000.c.html. In particular you can look at the code in function `ne_probe` to find out how to to detect an NE2000 device – Michael Petch Aug 14 '15 at 17:43
  • With ISA devices usually each OS driver had a probe function of some sort. Your OS would call the probe functions for each hardware driver. If the device responds to a probe correctly you assume the device is present and then you utilize its hardware. Most ISA peripheral vendors had completely different hardware, so the code to detect and utilize each was quite varied. That meant finding the development sheets and manuals for a device and developing to that specification – Michael Petch Aug 14 '15 at 17:52
  • 1
    @MichaelPetch Wow, that's some amazing info, thank you so much! I see that there are many sources for NE2000, I'm sure I'll find something useful. – Sasha Aug 14 '15 at 17:57
  • No problem. You'll find that almost every virtual environment (Dosbox, QEMU, VmWare, Parallels) has support for emulating NE2000 (it has almost become the default network device to emulate). You'll probably find a lot of code out there. I am sure there is NE2000 development documentation if one Google's for it. Good luck! – Michael Petch Aug 14 '15 at 18:01
  • @MichaelPetch So I managed to get dosbox to emulate a ne2000 (on mac, had some problems, solved them) and I have this now: `Packet driver software interrupt is 0x60 (96) Interrupt number 0x3 (3) I/O port 0x300 (768) My Ethernet address is FF:FF:FF:FF:FF:FF` This is using NE2000 v11.4.3 from Crynwr. However, I've looked through tens of sources for ne2k driver, and I can't figure out how to probe the device. Also tried googling documentation, but can't find any newbie-friendly docs. Any suggestions? – Sasha Sep 02 '15 at 13:48
  • Settings of dosbox: `nicbase=300 nicirq=3 macaddr=AC:DE:48:88:99:AA realnic=en0` I said in the previous comment that mac address is FF:FF:FF:FF:FF:FF, but I forgot to edit something, so now it's actually `AC:DE:48:88:99:AA` – Sasha Sep 02 '15 at 14:00

1 Answers1

2

Since it appears from your comments that you have Dosbox supporting an NE2000 card then the code below should detect the presence of an NE2000 card (A port base of 0x300 is assumed). The code is a DOS COM program I wrote and should be compilable with a command like nasm ne2kchk.asm -fbin -o ne2kchk.com

NS_DATAPORT    EQU    0x10    ; NatSemi-defined port window offset.
NE_DATAPORT    EQU    0x10    ; NatSemi-defined port window offset.
NS_RESET       EQU    0x1f    ; Issue a read to reset, a write to clear.

NE1SM_START_PG EQU    0x20    ; First page of TX buffer
NE1SM_STOP_PG  EQU    0x40    ; Last page +1 of RX ring
NESM_START_PG  EQU    0x40    ; First page of TX buffer
NESM_STOP_PG   EQU    0x80    ; Last page +1 of RX ring

E8390_CMD      EQU    0x00    ; The command register (for all pages)
E8390_STOP     EQU    0x01    ; Stop and reset the chip
E8390_START    EQU    0x02    ; Start the chip, clear reset
E8390_RREAD    EQU    0x08    ; Remote read
E8390_NODMA    EQU    0x20    ; Remote DMA
E8390_PAGE0    EQU    0x00    ; Select page chip registers
E8390_PAGE1    EQU    0x40    ; using the two high-order bits
E8390_PAGE2    EQU    0x80
E8390_PAGE3    EQU    0xC0    ; Page 3 is invalid on the real 8390.

E8390_RXOFF    EQU    0x20    ; EN0_RXCR: Accept no packets
E8390_TXOFF    EQU    0x02    ; EN0_TXCR: Transmitter off

               ; Page 0 register offsets.
EN0_CLDALO     EQU    0x01    ; Low byte of current local dma addr  RD
EN0_STARTPG    EQU    0x01    ; Starting page of ring bfr WR
EN0_CLDAHI     EQU    0x02    ; High byte of current local dma addr     RD
EN0_STOPPG     EQU    0x02    ; Ending page +1 of ring bfr WR
EN0_BOUNDARY   EQU    0x03    ; Boundary page of ring bfr RD WR
EN0_TSR        EQU    0x04    ; Transmit status reg RD
EN0_TPSR       EQU    0x04    ; Transmit starting page WR
EN0_NCR        EQU    0x05    ; Number of collision reg RD
EN0_TCNTLO     EQU    0x05    ; Low     byte of tx byte count WR
EN0_FIFO       EQU    0x06    ; FIFO RD
EN0_TCNTHI     EQU    0x06    ; High byte of tx byte count WR
EN0_ISR        EQU    0x07    ; Interrupt status reg RD WR
EN0_CRDALO     EQU    0x08    ; low byte of current remote dma address RD
EN0_RSARLO     EQU    0x08    ; Remote start address reg 0
EN0_CRDAHI     EQU    0x09    ; high byte, current remote dma address RD
EN0_RSARHI     EQU    0x09    ; Remote start address reg 1
EN0_RCNTLO     EQU    0x0a    ; Remote byte count reg WR
EN0_RCNTHI     EQU    0x0b    ; Remote byte count reg WR
EN0_RSR        EQU    0x0c    ; rx status reg RD
EN0_RXCR       EQU    0x0c    ; RX configuration reg WR
EN0_TXCR       EQU    0x0d    ; TX configuration reg WR
EN0_COUNTER0   EQU    0x0d    ; Rcv alignment error counter RD
EN0_DCFG       EQU    0x0e    ; Data configuration reg WR
EN0_COUNTER1   EQU    0x0e    ; Rcv CRC error counter RD
EN0_IMR        EQU    0x0f    ; Interrupt mask reg WR
EN0_COUNTER2   EQU    0x0f    ; Rcv missed frame error counter RD

PORT_BASE      EQU    0x300   ; Default base port

[BITS 16]

    org 0x100

section .text

start:
    push   cs
    pop    ds

    ; Probe for NE2000 card

    ; Try non destructive test first
    mov    dx, PORT_BASE+E8390_CMD
    in     al, dx
    cmp    al, 0xff
    jz     .s_notfound

    ; Attempt potentially destuctive tests
    mov    al, E8390_NODMA | E8390_PAGE1 | E8390_STOP
    mov    dx, PORT_BASE+E8390_CMD
    out    dx, al    ; Receive alignment error counter
    mov    dx, PORT_BASE+EN0_COUNTER0
    in     al, dx
    mov    cl, al    ; Save to REGD (CL)
    mov    al, 0xff
    out    dx, al
    mov    al, E8390_NODMA | E8390_PAGE0
    mov    dx, PORT_BASE+E8390_CMD
    out    dx, al
    mov    dx, PORT_BASE+EN0_COUNTER0
    in     al, dx    ; Clear the counter by reading.
    test   al, al
    jz     .s_found  ; If al is clear then card was found

    ; Card not found
.s_notfound:
    xchg   al, cl    ; Temporarily save al to avoid clobber
    out    dx, al
    mov    ah, 0x09
    mov    dx, notfound_str
    int    0x21
    xchg   al, cl    ; Restore al. al = error value to return
    jmp    .s_exit

    ; Card found
.s_found:
    mov    ah, 0x09
    mov    dx, found_str
    int    0x21
    xor    al, al    ; Clear the error code

    ; exit with al = errcode
.s_exit:
    mov    ah, 0x4C
    int    0x21

notfound_str db "NE2000 not found", 0x0a, 0x0d, "$"
found_str    db "NE2000 found", 0x0a, 0x0d, "$"

The code above is an adaptation of "C" code that I found in the Debian nictool code available here . It can be found in the file ne2k-diags.c. It seems more information (datasheets) are available for the rtl8019 which is a clone of the NE2000. The 8390NIC that is at the heart of these devices is documented here. The 8390 documentation discusses how to send and receive data. An excerpt of the "C" code that I based mine on was:

printf("Checking the ethercard at %#3x.\n", port_base);
{   int regd;
    long ioaddr = port_base;

    outb_p(E8390_NODMA+E8390_PAGE1+E8390_STOP, ioaddr + E8390_CMD);
    regd = inb_p(ioaddr + 0x0d);
    printk("  Receive alignment error counter (%#lx) is %2.2x\n",
           ioaddr + 0x0d, regd);
    outb_p(0xff, ioaddr + 0x0d);
    outb_p(E8390_NODMA+E8390_PAGE0, ioaddr + E8390_CMD);
    inb_p(ioaddr + EN0_COUNTER0); /* Clear the counter by reading. */
    if (inb_p(ioaddr + EN0_COUNTER0) != 0) {
        outb(regd, ioaddr + 0x0d);  /* Restore the old values. */
        printk("  Failed initial NE2000 probe, value %2.2x.\n",
               inb(ioaddr + EN0_COUNTER0));
    } else
        printk("  Passed initial NE2000 probe, value %2.2x.\n",
               inb(ioaddr + EN0_COUNTER0));

}

The code above is the initial probe but there is more "C" code in the same file that tries to detect some of the specific variants and query the card signature.

Michael Petch
  • 46,082
  • 8
  • 107
  • 198
  • 1
    After editing some stuff to make it fit my OS framework, it works perfectly! Thanks a bunch! Btw, why are those called destructive tests? Is it because you use `out` instruction (which could possibly affect the card in an unwanted way)? – Sasha Sep 03 '15 at 11:20
  • The main reason I wrote it as a COM program was that it was runnable inside Dosbox for test purposes so you could easily test if it functioned, however I did it with all 16 bit code so it would be reasonably straight forward to adapt to your OS. As for the destructive test, correct it starts with a an `in` now (reading a port less likely to cause problems). If a NE2k card exists it should result in a non `0xff` value being returned. It is possible for other devices (probably not issue in DosBox) to be mapped to 0x300 and they might behave badly (hang etc) if written to. – Michael Petch Sep 03 '15 at 13:20
  • In your kernel probe you could try to amend it to loop this code and check these addresses (0x300 first because it is more standard) 0x300, 0x280, 0x320, 0x340, 0x360 by making `PORT_BASE` a variable . These are the most likely places a card would be found. It is also possible (not necessarily in DOSBox) where there are 2 or more cards installed. – Michael Petch Sep 03 '15 at 13:27