;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; ;; ;; ETHERNET.INC ;; ;; ;; ;; Ethernet network layer for Menuet OS ;; ;; ;; ;; Version 0.4 22 September 2003 ;; ;; ;; ;; This file contains the following: ;; ;; PCI bus scanning for valid devices ;; ;; Table of supported ethernet drivers ;; ;; Code to identify and activate a supported driver ;; ;; ARP handler ;; ;; Driver interface to the IP layer ;; ;; Gateway support ;; ;; ;; ;; Individual driver files are included here ;; ;; ;; ;; The PCI bus scanning code was ported from the etherboot ;; ;; 5.0.6 project. The copyright statement for that code is ;; ;; ;; ;; GNU GENERAL PUBLIC LICENSE ;; ;; Version 2, June 1991 ;; ;; ;; ;; remaining parts Copyright 2002 Mike Hibbett ;; ;; mikeh@oceanfree.net ;; ;; ;; ;; See file COPYING for details ;; ;; ;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;******************************************************************** ; Interface ; ethernet_driver called by stack_handler in stack.inc ; eth_probe called by app_stack_handler in stack.inc ; ;******************************************************************** ; Some useful information on data structures ; Ethernet Packet - ARP Request example ; ; 0 1 2 3 ; 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 ; ; +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ ; | Dest H/W Address | ; | ( 14 byte header ) | ; +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ ; | | Source H/W Address | ; +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ ; | | ; +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ ; | Protocol - ARP 08 06 | ; +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ ; +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ ; | H/W Type 00 01 | Protocol Type 08 00 | ; | ( ARP Request packet ) | ; +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ ; | HLen 0x06 | PLen 0x04 | OpCode 00 01 | ; | ( 0001 for request, 0002 for reply ) | ; +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ ; | Source Hardware Address ( MAC Address ) | ; +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ ; | | Source IP Address | ; +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ ; | | Destination Hardware Address | ; +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ ; | | ; +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ ; | Destination IP Address | ; +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ ; Include individual drivers source files at this point. ; If you create a new driver, include it below. include "rtl8029.inc" include "i8255x.inc" include "rtl8139.inc" include "3c59x.inc" include "sis900.inc" include "pcnet32.inc" ; DEBUGGING_STATE enables or disables output of received and transmitted ; data over the serial port DEBUGGING_ENABLED equ 1 DEBUGGING_DISABLED equ 0 DEBUGGING_STATE equ DEBUGGING_DISABLED ; PCICards ; ======== ; PCI vendor and hardware types for hardware supported by the above drivers ; If you add a driver, ensure you update this datastructure, otherwise the ; card will not be probed. ; Each driver is defined by 4 double words. These are ; PCIVendorDevice probeFunction ResetFunction PollFunction transmitFunction ; The last entry must be kept at all zeros, to indicate the end of the list ; As a PCI driver may support more than one hardware implementation, there may ; be several lines which refer to the same functions. ; The first driver found on the PCI bus will be the one used. PCICARDS_ENTRY_SIZE equ 20 ; Size of each PCICARDS entry iglobal PCICards: dd 0x12098086, I8255x_probe, I8255x_reset, I8255x_poll, I8255x_transmit dd 0x10298086, I8255x_probe, I8255x_reset, I8255x_poll, I8255x_transmit dd 0x12298086, I8255x_probe, I8255x_reset, I8255x_poll, I8255x_transmit dd 0x10308086, I8255x_probe, I8255x_reset, I8255x_poll, I8255x_transmit dd 0x24498086, I8255x_probe, I8255x_reset, I8255x_poll, I8255x_transmit dd 0x802910ec, rtl8029_probe, rtl8029_reset, rtl8029_poll, rtl8029_transmit dd 0x12111113, rtl8029_probe, rtl8029_reset, rtl8029_poll, rtl8029_transmit dd 0x813910ec, rtl8139_probe, rtl8139_reset, rtl8139_poll, rtl8139_transmit dd 0x590010b7, e3c59x_probe, e3c59x_reset, e3c59x_poll, e3c59x_transmit dd 0x592010b7, e3c59x_probe, e3c59x_reset, e3c59x_poll, e3c59x_transmit dd 0x597010b7, e3c59x_probe, e3c59x_reset, e3c59x_poll, e3c59x_transmit dd 0x595010b7, e3c59x_probe, e3c59x_reset, e3c59x_poll, e3c59x_transmit dd 0x595110b7, e3c59x_probe, e3c59x_reset, e3c59x_poll, e3c59x_transmit dd 0x595210b7, e3c59x_probe, e3c59x_reset, e3c59x_poll, e3c59x_transmit dd 0x900010b7, e3c59x_probe, e3c59x_reset, e3c59x_poll, e3c59x_transmit dd 0x900110b7, e3c59x_probe, e3c59x_reset, e3c59x_poll, e3c59x_transmit dd 0x900410b7, e3c59x_probe, e3c59x_reset, e3c59x_poll, e3c59x_transmit dd 0x900510b7, e3c59x_probe, e3c59x_reset, e3c59x_poll, e3c59x_transmit dd 0x900610b7, e3c59x_probe, e3c59x_reset, e3c59x_poll, e3c59x_transmit dd 0x900A10b7, e3c59x_probe, e3c59x_reset, e3c59x_poll, e3c59x_transmit dd 0x905010b7, e3c59x_probe, e3c59x_reset, e3c59x_poll, e3c59x_transmit dd 0x905110b7, e3c59x_probe, e3c59x_reset, e3c59x_poll, e3c59x_transmit dd 0x905510b7, e3c59x_probe, e3c59x_reset, e3c59x_poll, e3c59x_transmit dd 0x905810b7, e3c59x_probe, e3c59x_reset, e3c59x_poll, e3c59x_transmit dd 0x905A10b7, e3c59x_probe, e3c59x_reset, e3c59x_poll, e3c59x_transmit dd 0x920010b7, e3c59x_probe, e3c59x_reset, e3c59x_poll, e3c59x_transmit dd 0x980010b7, e3c59x_probe, e3c59x_reset, e3c59x_poll, e3c59x_transmit dd 0x980510b7, e3c59x_probe, e3c59x_reset, e3c59x_poll, e3c59x_transmit dd 0x764610b7, e3c59x_probe, e3c59x_reset, e3c59x_poll, e3c59x_transmit dd 0x505510b7, e3c59x_probe, e3c59x_reset, e3c59x_poll, e3c59x_transmit dd 0x605510b7, e3c59x_probe, e3c59x_reset, e3c59x_poll, e3c59x_transmit dd 0x605610b7, e3c59x_probe, e3c59x_reset, e3c59x_poll, e3c59x_transmit dd 0x5b5710b7, e3c59x_probe, e3c59x_reset, e3c59x_poll, e3c59x_transmit dd 0x505710b7, e3c59x_probe, e3c59x_reset, e3c59x_poll, e3c59x_transmit dd 0x515710b7, e3c59x_probe, e3c59x_reset, e3c59x_poll, e3c59x_transmit dd 0x525710b7, e3c59x_probe, e3c59x_reset, e3c59x_poll, e3c59x_transmit dd 0x656010b7, e3c59x_probe, e3c59x_reset, e3c59x_poll, e3c59x_transmit dd 0x656210b7, e3c59x_probe, e3c59x_reset, e3c59x_poll, e3c59x_transmit dd 0x656410b7, e3c59x_probe, e3c59x_reset, e3c59x_poll, e3c59x_transmit dd 0x450010b7, e3c59x_probe, e3c59x_reset, e3c59x_poll, e3c59x_transmit dd 0x09001039, SIS900_probe, SIS900_reset, SIS900_poll, SIS900_transmit dd 0x20001022, pcnet32_probe, pcnet32_reset, pcnet32_poll, pcnet32_xmit dd 0x26251022, pcnet32_probe, pcnet32_reset, pcnet32_poll, pcnet32_xmit dd 0x20011022, pcnet32_probe, pcnet32_reset, pcnet32_poll, pcnet32_xmit ; following card is untested dd 0x70161039, SIS900_probe, SIS900_reset, SIS900_poll, SIS900_transmit dd 0,0,0,0,0 ; end of list marker, do not remove endg ; PCI Bus defines PCI_HEADER_TYPE equ 0x0e ;8 bit PCI_BASE_ADDRESS_0 equ 0x10 ;32 bit PCI_BASE_ADDRESS_5 equ 0x24 ;32 bits PCI_BASE_ADDRESS_SPACE_IO equ 0x01 PCI_VENDOR_ID equ 0x00 ;16 bit PCI_BASE_ADDRESS_IO_MASK equ 0xFFFFFFFC ETHER_IP equ 0x0008 ; Reversed from 0800 for intel ETHER_ARP equ 0x0608 ; Reversed from 0806 for intel ETHER_RARP equ 0x3580 ARP_REQ_OPCODE equ 0x0100 ARP_REP_OPCODE equ 0x0200 uglobal arp_rx_count: dd 0 ip_rx_count: dd 0 dumped_rx_count: dd 0 ip_tx_count: dd 0 node_addr: db 0,0,0,0,0,0 eth_rx_data_len: dw 0 eth_status: dd 0 io_addr: dd 0 hdrtype: db 0 vendor_device: dd 0 pci_data: dd 0 pci_dev: dd 0 pci_bus: dd 0 ; These will hold pointers to the selected driver functions drvr_probe: dd 0 drvr_reset: dd 0 drvr_poll: dd 0 drvr_transmit: dd 0 ; These hold the destination Host identity for ARP responses remote_ip_add: dd 0 remote_hw_add: db 0, 0, 0, 0, 0, 0 endg iglobal broadcast_add: db 0xff,0xff,0xff,0xff,0xff,0xff subnet_mask: dd 0x00ffffff endg uglobal ; This is used by getMACfromIP MACAddress: db 0,0,0,0,0,0 gateway_ip: db 0, 0, 0, 0 dns_ip: dd 0 endg ; The follow is the ARP Table. ; This table must be manually updated and the kernel recompilied if ; changes are made to it. ; ARP_TABLE_SIZE defines the size of the table ; ARP_TABLE_ENTRIES defines the number of entries in the table ; Each entry is 10 bytes: 4 Byte IP address, 6 byte MAC Address, ; 2 bytes status, 2 bytes TTL ( in seconds ) ; Empty entries are filled with zeros ; The TTL field is decremented every second, and is deleted when it ; reaches 0. It is refreshed every time a packet is received ; If the TTL field is 0xFFFF it is a permanent entry and is never deleted ; The status field can be the following values ; 0x0000 entry not used ; 0x0001 entry holds a valid mapping ; 0x0002 entry contains an IP address, awaiting ARP response ; 0x0003 No response received to ARP request. ; The last status value is provided to allow the network layer to delete ; a packet that is queued awaiting an ARP response ARP_NO_ENTRY equ 0 ARP_VALID_MAPPING equ 1 ARP_AWAITING_RESPONSE equ 2 ARP_RESPONSE_TIMEOUT equ 3 ARP_ENTRY_SIZE equ 14 ; Number of bytes per entry ARP_TABLE_SIZE equ 20 ; Size of table ARP_TABLE_ENTRIES equ 0 ; Inital, hardcoded entries uglobal ARPTable: times ( ARP_TABLE_SIZE - ARP_TABLE_ENTRIES ) * ARP_ENTRY_SIZE db 0 endg iglobal NumARP: db ARP_TABLE_ENTRIES endg ;*************************************************************************** ; Function ; eth_probe ; Description ; Searches for an ethernet card. If found, the card is enabled and ; the ethernet -> IP link established ; ; This function scans the PCI bus looking for a supported device. ; ISA bus is currently not supported. ; ; eax is 0 if no hardware found ;*************************************************************************** eth_probe: ; Find a card on the PCI bus, and get it's address call scan_bus ; Find the ethernet cards PIC address xor eax, eax cmp [io_addr], eax je ep_00x ; Return 0 in eax if no cards found call dword [drvr_probe] ; Call the drivers probe function mov eax, [io_addr] ; return a non zero value ep_00x: ret ;*************************************************************************** ; Function ; ethernet_driver ; ; Description ; The ethernet RX and TX handler ; This is a kernel function, called by stack_handler ; ;*************************************************************************** ethernet_driver: ; Do nothing if the driver is inactive cmp [ethernet_active], byte 0 je eth_exit call eth_rx call eth_tx eth_exit: ret ;*************************************************************************** ; Function ; eth_rx ; ; Description ; Polls the ethernet card for received data. Extracts if present ; Depending on the Protocol within the packet: ; ARP : Pass to ARP_handler. This may result in an ARP reply ; being tx'ed ; IP : Store in an IP buffer ; ;*************************************************************************** eth_rx: xor ax, ax mov [eth_rx_data_len], ax call dword [drvr_poll] ; Call the drivers poll function mov ax, [eth_rx_data_len] cmp ax, 0 je erx_exit if DEBUGGING_STATE = DEBUGGING_ENABLED pusha mov eax, 0 ;Indicate that this is a received packet mov cx, [eth_rx_data_len] mov esi, Ether_buffer cmp word [esi + 12], ETHER_IP jnz erxd_done ; cmp byte [esi + 14 + 9], 0x06 ; TCP ; jnz erxd_done call eth_dump erxd_done: popa end if ; Check the protocol. Call appropriate handler mov eax, Ether_buffer add eax, 12 ; The address of the protocol word mov ax, [eax] cmp ax, ETHER_ARP je erx_001 ; It is ARP cmp ax, ETHER_IP je erx_002 ; It's IP ; inc dword [dumped_rx_count] jmp erx_exit ; If not IP or ARP, ignore erx_001: mov eax, [arp_rx_count] inc eax mov [arp_rx_count], eax ; At this point, the packet is still in the Ether_buffer call arp_handler jmp erx_exit erx_002: mov eax, [ip_rx_count] inc eax mov [ip_rx_count], eax ; Check to see if the MAC address is in our arp table ; refresh the arp ttl if so mov esi, Ether_buffer add esi, 6 call refreshARP call ether_IP_handler jmp erx_exit erx_exit: ret ;*************************************************************************** ; Function ; eth_tx ; ; Description ; Looks at the NET1OUT_QUEUE for data to send. ; Stores that destination IP in a location used by the tx routine ; Looks up the MAC address in the ARP table; stores that where ; the tx routine can get it ; Get the length of the data. Store that where the tx routine wants it ; Call tx ; Places buffer on empty queue when the tx routine finished ; ;*************************************************************************** eth_tx: ; Look for a buffer to tx mov eax, NET1OUT_QUEUE call dequeue cmp ax, NO_BUFFER je eth_exit ; Exit if no buffer available push eax ; convert buffer pointer eax to the absolute address mov ecx, IPBUFFSIZE mul ecx add eax, IPbuffs ; Extract the destination IP ; find the destination IP in the ARP table, get MAC ; store this MAC in 'MACAddress' mov ebx, eax ; Save buffer address mov edx, [ebx + 16] ; get destination address ; If the destination address is 255.255.255.255, ; set the MACAddress to all ones ( broadcast ) mov [MACAddress], dword 0xffffffff mov [MACAddress + 4], word 0xffff cmp edx, 0xffffffff je etx_send ; If it is broadcast, just send call getMACfromIP ; Get the MAC address. cmp eax, ARP_VALID_MAPPING jz etx_send ; No valid entry. Are we waiting for a response? cmp eax, ARP_AWAITING_RESPONSE jne etx_001 ; Re-queue the packet, and exit pop ebx mov eax, NET1OUT_QUEUE call queue jmp etx_exit etx_001: ; HAs the request been sent, but timed out? cmp eax, ARP_RESPONSE_TIMEOUT jne etx_002 pop eax call freeBuff jmp etx_exit etx_002: ; There is no entry. Re queue the request, and ask ARP to send a request ; IP address is in edx push edx call arp_request pop ebx ; Add an entry in the ARP table, awaiting response cmp byte [NumARP], ARP_TABLE_SIZE je etx_003 ; We cannot add a new entry in the table inc byte [NumARP] movzx eax, byte [NumARP] mov ecx, ARP_ENTRY_SIZE mul ecx sub eax, ARP_ENTRY_SIZE mov [eax + ARPTable], ebx xor ebx, ebx mov [eax + ARPTable + 4], ebx mov [eax + ARPTable + 8], bx ; set the status field up - awaiting response mov cl, 0x00 mov [eax + ARPTable + 10], cl mov cl, 0x02 mov [eax + ARPTable + 11], cl ; Initialise the time to live field - 10s mov cx, 0x000A mov [eax + ARPTable + 12], cx etx_003: pop ebx ; Get the buffer back mov eax, NET1OUT_QUEUE call queue jmp etx_exit etx_send: xor ecx, ecx mov ch, [ebx+2] mov cl, [ebx+3] ; ; Size of IP packet to send mov esi, ebx mov edi, MACAddress if DEBUGGING_STATE = DEBUGGING_ENABLED pusha mov cx, 42 mov eax, 1 ; Indicate that this is a tx packet call eth_dump popa end if mov bx, ETHER_IP call dword [drvr_transmit] ; Call the drivers transmit function ; OK, we have sent a packet, so increment the count inc dword [ip_tx_count] ; And finally, return the buffer to the free queue pop eax call freeBuff etx_exit: ret ;*************************************************************************** ; Function ; ether_IP_handler ; ; Description ; Called when an IP ethernet packet is received on the ethernet ; Header + Data is in Ether_buffer[] ; We just need to get a buffer from the 'free' queue, and ; store the packet in it, then insert the packet number into the ; IPRX queue. ; If no queue entry is available, the packet is silently discarded ; All registers may be destroyed ; ;*************************************************************************** ether_IP_handler: mov eax, EMPTY_QUEUE call dequeue cmp ax, NO_BUFFER je eiph00x ; convert buffer pointer eax to the absolute address push eax mov ecx, IPBUFFSIZE mul ecx add eax, IPbuffs mov edi, eax ; get a pointer to the start of the DATA mov esi, Ether_buffer + 14 ; Now store it all away mov ecx, IPBUFFSIZE / 4 ; Copy all of the available ; data across - worse case cld rep movsd ; And finally, place the buffer in the IPRX queue pop ebx mov eax, IPIN_QUEUE call queue eiph00x: ret ;*************************************************************************** ; ; ARP CODE FOLLOWS ; ; The ARP code is used by ethernet drivers to translate an destination ; IP address into an ethernet hardware address. Functions to broadcast ; requests and handle response are (or will be) here. ; The IP layer has no knowledge of ARP, as this is a network interface ; issue ; ;*************************************************************************** ;*************************************************************************** ; Function ; arp_timer ; ; Description ; Called every 1s ; It is responsible for removing expired routes ; All registers may be destroyed ; ;*************************************************************************** arp_timer: ; loop through all the ARP entries, decrementing each one ; that doesn't have a TTL of 0xFFFF movzx eax, byte [NumARP] arp_001: cmp eax, 0 je arp_003 push eax dec eax mov ecx, ARP_ENTRY_SIZE mul ecx cmp word [ eax + ARPTable + 12], 0xFFFF je arp_002 cmp word [ eax + ARPTable + 12], 0 je arp_002 dec word [eax + ARPTable + 12] arp_002: pop eax dec eax jmp arp_001 ; Now, look for entries with a TTL of 0 ; Valid entries and response timeout entries get removed ; awaiting response gets converted into a response timeout, with a ; short life time - this allows queued packets to be flushed arp_003: movzx edx, byte [NumARP] cmp edx, 0 je arp_exit ; EDX holds the # of entries to search through mov eax, 0 arp_005: cmp word [ eax + ARPTable + 12], 0 jne arp_004 ; If it's status code is 0001 or 0003, delete the entry cmp word [eax + ARPTable + 10], 0x0100 je arp_007 cmp word [eax + ARPTable + 10], 0x0300 je arp_007 ; The only other valid code is 0002 - indicating a ; timeout while waiting for a response. Change the ; entry to response timed out mov [eax + ARPTable + 10], word 0x0300 mov [eax + ARPTable + 12], word 0x000A jmp arp_004 arp_007: ; Delete this entry mov edi, ARPTable add edi, eax mov esi, edi add esi, ARP_ENTRY_SIZE mov ecx, (ARP_TABLE_SIZE - 1) * ARP_ENTRY_SIZE sub ecx, eax rep movsb dec byte [NumARP] jmp arp_006 arp_004: add eax, ARP_ENTRY_SIZE arp_006: dec edx cmp edx, 0 jne arp_005 arp_exit: ret ;*************************************************************************** ; Function ; arp_request ; ; Description ; Sends an ARP request on the ethernet ; The requested IP address is in edx ; All registers may be destroyed ; ;*************************************************************************** arp_request: mov ebx, Ether_buffer mov ax, 0x0100 mov [ebx], ax add ebx, 2 mov ax, 0x0008 mov [ebx], ax add ebx, 2 mov ax, 0x0406 mov [ebx], ax add ebx, 2 mov ax, 0x0100 mov [ebx], ax add ebx, 2 mov ecx, node_addr mov eax, [ecx] mov [ebx], eax add ecx, 4 add ebx, 4 mov ax, [ecx] mov [ebx], ax add ebx, 2 mov eax, [stack_ip] mov [ebx], eax add ebx, 4 xor eax, eax mov [ebx], eax add ebx, 4 mov [ebx], ax add ebx, 2 mov [ebx], edx ; Now, send it! ; Pointer to 48 bit destination address in edi ; Type of packet in bx ; size of packet in ecx ; pointer to packet data in esi mov edi, broadcast_add ;if DEBUGGING_STATE = DEBUGGING_ENABLED ; pusha ; mov eax, 1 ; Indicate that this is a tx packet ; mov ecx, 28 ; mov esi, Ether_buffer ; call eth_dump ; popa ;end if mov bx, ETHER_ARP mov ecx, 28 mov esi, Ether_buffer call dword [drvr_transmit] ; Call the drivers transmit function ret ;*************************************************************************** ; Function ; arp_handler ; ; Description ; Called when an ARP packet is received on the ethernet ; Header + Data is in Ether_buffer[] ; It looks to see if the packet is a request to resolve this Hosts ; IP address. If it is, send the ARP reply packet. ; This Hosts IP address is in dword [stack_ip] ( in network format ) ; This Hosts MAC address is in node_addr[6] ; All registers may be destroyed ; ;*************************************************************************** arp_handler: ; Is this a REQUEST? ; Is this a request for My Host IP ; Yes - So construct a response message. ; Send this message to the ethernet card for transmission mov ebx, Ether_buffer mov edx, ebx add edx, 20 mov ax, [edx] cmp ax, ARP_REQ_OPCODE ; Is this a request packet? jne arph_resp ; No - so test for response mov edx, ebx add edx, 38 mov eax, [edx] cmp eax, [stack_ip] ; Is it looking for my IP address? jne arph_exit ; No - so quit now ; OK, it is a request for my MAC address. Build the frame and send it ; Save the important data from the original packet ; remote MAC address first mov ecx, remote_hw_add mov edx, ebx add edx, 22 ; edx points to Source h/w address mov eax, [edx] mov [ecx], eax add edx, 4 add ecx, 4 mov ax, [edx] mov [ecx],ax ; and also the remote IP address add edx, 2 mov eax,[edx] mov [remote_ip_add], eax ; So now we can reuse the packet. ebx still holds the address of ; the header + packet ; We dont need the header ( first 14 bytes ) mov edx, ebx add edx, 20 mov ax, ARP_REP_OPCODE mov [edx], ax add edx, 2 mov ecx, node_addr mov eax, [ecx] mov [edx], eax add ecx, 4 add edx, 4 mov ax, [ecx] mov [edx], ax add edx, 2 mov eax, [stack_ip] mov [edx], eax add edx, 4 mov ecx, remote_hw_add mov eax, [ecx] mov [edx], eax add ecx, 4 add edx, 4 mov ax, [ecx] mov [edx], ax add edx, 2 mov eax, [remote_ip_add] mov [edx], eax ; Now, send it! ; Pointer to 48 bit destination address in edi ; Type of packet in bx ; size of packet in ecx ; pointer to packet data in esi mov edi, remote_hw_add ;if DEBUGGING_STATE = DEBUGGING_ENABLED ; pusha ; mov eax, 1 ; Indicate that this is a tx packet ; mov ecx, 28 ; mov esi, Ether_buffer + 14 ; call eth_dump ; popa ;end if mov bx, ETHER_ARP mov ecx, 28 mov esi, Ether_buffer + 14 call dword [drvr_transmit] ; Call the drivers transmit function jmp arph_exit arph_resp: cmp ax, ARP_REP_OPCODE ; Is this a replypacket? jne arph_resp ; No - so quit ; This was a reply, probably directed at me. ; save the remotes MAC & IP mov ecx, remote_hw_add mov edx, ebx add edx, 22 ; edx points to Source h/w address mov eax, [edx] mov [ecx], eax add edx, 4 add ecx, 4 mov ax, [edx] mov [ecx],ax ; and also the remote IP address add edx, 2 mov eax,[edx] mov [remote_ip_add], eax ; Now, add an entry in the table for this IP address if it doesn't exist push eax movzx eax, byte [NumARP] mov ecx, ARP_ENTRY_SIZE mul ecx pop edx movzx ecx, byte [NumARP] cmp ecx, 0 je arph_002 arph_001: sub eax, ARP_ENTRY_SIZE cmp [eax + ARPTable], edx loopnz arph_001 ; Return back if non match jnz arph_002 ; None found, add to end mov ecx, [remote_hw_add] mov [eax + ARPTable + 4], ecx mov cx, [remote_hw_add+4] mov [eax + ARPTable + 8], cx ; specify the type - a valid entry mov cl, 0x00 mov [eax + ARPTable + 10], cl mov cl, 0x01 mov [eax + ARPTable + 11], cl ; Initialise the time to live field - 1 hour mov cx, 0x0E10 mov [eax + ARPTable + 12], cx jmp arph_exit arph_002: cmp byte [NumARP], ARP_TABLE_SIZE je arph_exit inc byte [NumARP] movzx eax, byte [NumARP] mov ecx, ARP_ENTRY_SIZE mul ecx sub eax, ARP_ENTRY_SIZE mov ecx, [remote_ip_add] mov [eax + ARPTable], ecx mov ecx, [remote_hw_add] mov [eax + ARPTable + 4], ecx mov cx, [remote_hw_add+4] mov [eax + ARPTable + 8], cx mov cl, 0x00 mov [eax + ARPTable + 10], cl mov cl, 0x01 mov [eax + ARPTable + 11], cl ; Initialise the time to live field - 1 hour mov cx, 0x0E10 mov [eax + ARPTable + 12], cx arph_exit: ret ; pointer to MAC in esi refreshARP: mov ebx, [esi] mov dx, [esi+4] push edx movzx eax, byte [NumARP] mov ecx, ARP_ENTRY_SIZE mul ecx pop edx movzx ecx, byte [NumARP] cmp ecx, 0 je rf_exit rf_001: sub eax, ARP_ENTRY_SIZE cmp [eax + ARPTable+4], ebx je rf_002 loop rf_001 jmp rf_exit rf_002: cmp [eax + ARPTable+8], dx je rf_gotone loop rf_001 jmp rf_exit rf_gotone: ; Initialise the time to live field - 1 hour mov cx, 0x0E10 mov [eax + ARPTable + 12], cx rf_exit: ret ;*************************************************************************** ; Function ; getMACfromIP ; ; Description ; Takes an IP address in edx and scans the ARP table for ; a matching entry ; If a match is found, it's MAC address is stored in MACAddress. ; Otherwise the value 0 is writen to MACAddress ; eax holds ARP table entry status code ( ARP_ ) ; ebx unchanged ; ;*************************************************************************** getMACfromIP: ; first, check destination IP to see if it is on 'this' network. ; The test is: ; if ( destIP & subnet_mask == stack_ip & subnet_mask ) ; desitnation is local ; else ; destination is remote, so pass to gateway mov eax, edx and eax, [subnet_mask] mov ecx, [stack_ip] and ecx, [subnet_mask] cmp eax, ecx je gm0 mov edx, [gateway_ip] gm0: push edx xor eax, eax mov [MACAddress], eax mov [MACAddress + 4], ax movzx eax, byte [NumARP] mov ecx, ARP_ENTRY_SIZE mul ecx pop edx movzx ecx, byte [NumARP] cmp ecx, 0 je gm_none gm1: sub eax, ARP_ENTRY_SIZE cmp [eax + ARPTable], edx loopnz gm1 ; Return back if non match jnz gm_none ; Quit if none found ; eax holds index mov ecx, [eax + ARPTable + 4] mov [MACAddress], ecx mov cx, [eax + ARPTable + 8] mov [MACAddress+4], cx ; Return the entry status in eax mov ch, [eax + ARPTable + 10] mov cl, [eax + ARPTable + 11] movzx eax, cx jmp gm_exit gm_none: mov eax, ARP_NO_ENTRY gm_exit: ret ;*************************************************************************** ; ; PCI CODE FOLLOWS ; ; the following functions provide access to the PCI interface. ; These functions are used by scan_bus, and also some ethernet drivers ; ;*************************************************************************** ;*************************************************************************** ; Function ; config_cmd ; ; Description ; creates a command dword for use with the PCI bus ; bus # in ebx ; devfn in ecx ; where in edx ; ; command dword returned in eax ; Only eax destroyed ;*************************************************************************** config_cmd: push ecx mov eax, ebx shl eax, 16 or eax, 0x80000000 shl ecx, 8 or eax, ecx pop ecx or eax, edx and eax, 0xFFFFFFFC ret ;*************************************************************************** ; Function ; pcibios_read_config_byte ; ; Description ; reads a byte from the PCI config space ; bus # in ebx ; devfn in ecx ; where in edx ( ls 16 bits significant ) ; ; byte returned in al ( rest of eax zero ) ; Only eax/edx destroyed ;*************************************************************************** pcibios_read_config_byte: call config_cmd push dx mov dx, 0xCF8 out dx, eax pop dx xor eax, eax and dx, 0x03 add dx, 0xCFC ; and dx, 0xFFC in al, dx ret ;*************************************************************************** ; Function ; pcibios_read_config_word ; ; Description ; reads a word from the PCI config space ; bus # in ebx ; devfn in ecx ; where in edx ( ls 16 bits significant ) ; ; word returned in ax ( rest of eax zero ) ; Only eax/edx destroyed ;*************************************************************************** pcibios_read_config_word: call config_cmd push dx mov dx, 0xCF8 out dx, eax pop dx xor eax, eax and dx, 0x02 add dx, 0xCFC ; and dx, 0xFFC in ax, dx ret ;*************************************************************************** ; Function ; pcibios_read_config_dword ; ; Description ; reads a dword from the PCI config space ; bus # in ebx ; devfn in ecx ; where in edx ( ls 16 bits significant ) ; ; dword returned in eax ; Only eax/edx destroyed ;*************************************************************************** pcibios_read_config_dword: push edx call config_cmd push dx mov dx, 0xCF8 out dx, eax pop dx xor eax, eax mov dx, 0xCFC in eax, dx pop edx ret ;*************************************************************************** ; Function ; pcibios_write_config_byte ; ; Description ; write a byte in al to the PCI config space ; bus # in ebx ; devfn in ecx ; where in edx ( ls 16 bits significant ) ; ; Only eax/edx destroyed ;*************************************************************************** pcibios_write_config_byte: push ax call config_cmd push dx mov dx, 0xCF8 out dx, eax pop dx pop ax and dx, 0x03 add dx, 0xCFC out dx, al ret ;*************************************************************************** ; Function ; pcibios_write_config_word ; ; Description ; write a word in ax to the PCI config space ; bus # in ebx ; devfn in ecx ; where in edx ( ls 16 bits significant ) ; ; Only eax/edx destroyed ;*************************************************************************** pcibios_write_config_word: push ax call config_cmd push dx mov dx, 0xCF8 out dx, eax pop dx pop ax and dx, 0x02 add dx, 0xCFC out dx, ax ret ;*************************************************************************** ; Function ; delay_us ; ; Description ; delays for 30 to 60 us ; ; I would prefer this routine to be able to delay for ; a selectable number of microseconds, but this works for now. ; ; If you know a better way to do 2us delay, pleae tell me! ;*************************************************************************** delay_us: push eax push ecx mov ecx,2 in al,0x61 and al,0x10 mov ah,al cld dcnt1: in al,0x61 and al,0x10 cmp al,ah jz dcnt1 mov ah,al loop dcnt1 pop ecx pop eax ret ;*************************************************************************** ; Function ; scan_bus ; ; Description ; Scans the PCI bus for a supported device ; If a supported device is found, the drvr_ variables are initialised ; to that drivers functions ( as defined in the PCICards table) ; ; io_addr holds card I/O space. 32 bit, but only LS 16 bits valid ; pci_data holds the PCI vendor + device code ; pci_dev holds PCI bus dev # ; pci_bus holds PCI bus # ; ; io_addr will be zero if no card found ; ;*************************************************************************** scan_bus: xor eax, eax mov [hdrtype], al mov [pci_data], eax xor ebx, ebx ; ebx = bus# 0 .. 255 sb_bus_loop: xor ecx, ecx ; ecx = devfn# 0 .. 254 ( not 255? ) sb_devf_loop: mov eax, ecx and eax, 0x07 cmp eax, 0 jne sb_001 mov edx, PCI_HEADER_TYPE call pcibios_read_config_byte mov [hdrtype], al jmp sb_002 sb_001: mov al, [hdrtype] and al, 0x80 cmp al, 0x80 jne sb_inc_devf sb_002: mov edx, PCI_VENDOR_ID call pcibios_read_config_dword mov [vendor_device], eax cmp eax, 0xffffffff je sb_empty cmp eax, 0 jne sb_check_vendor sb_empty: mov [hdrtype], byte 0 jmp sb_inc_devf sb_check_vendor: ; iterate though PCICards until end or match found mov esi, PCICards sb_check: cmp [esi], dword 0 je sb_inc_devf ; Quit if at last entry cmp eax, [esi] je sb_got_card add esi, PCICARDS_ENTRY_SIZE jmp sb_check sb_got_card: ; indicate that we have found the card mov [pci_data], eax mov [pci_dev], ecx mov [pci_bus], ebx ; Define the driver functions push eax mov eax, [esi+4] mov [drvr_probe], eax mov eax, [esi+8] mov [drvr_reset], eax mov eax, [esi+12] mov [drvr_poll], eax mov eax, [esi+16] mov [drvr_transmit], eax pop eax mov edx, PCI_BASE_ADDRESS_0 sb_reg_check: call pcibios_read_config_dword mov [io_addr], eax and eax, PCI_BASE_ADDRESS_IO_MASK cmp eax, 0 je sb_inc_reg mov eax, [io_addr] and eax, PCI_BASE_ADDRESS_SPACE_IO cmp eax, 0 je sb_inc_reg mov eax, [io_addr] and eax, PCI_BASE_ADDRESS_IO_MASK mov [io_addr], eax sb_exit1: ret sb_inc_reg: add edx, 4 cmp edx, PCI_BASE_ADDRESS_5 jbe sb_reg_check sb_inc_devf: inc ecx cmp ecx, 255 jb sb_devf_loop inc ebx cmp ebx, 256 jb sb_bus_loop ; We get here if we didn't find our card ; set io_addr to 0 as an indication xor eax, eax mov [io_addr], eax sb_exit2: ret ;*************************************************************************** ; ; DEBUGGING CODE FOLLOWS ; ; If debugging data output is not required, ALL code & data below may ; be removed. ; ;*************************************************************************** if DEBUGGING_STATE = DEBUGGING_ENABLED ;*************************************************************************** ; Function ; eth_dump ; ; Description ; Dumps a tx or rx ethernet packet over the rs232 link ; This is a debugging routine that seriously slows down the stack. ; Use with caution. ; ; Baud rate is 57600, 8n1 com1 ; eax : type (0 == rx, 1 == tx ) ; cx : # of bytes in buffer ; esi : address of buffer start ; edi : pointer to MACAddress ( tx only ) ; ;*************************************************************************** eth_dump: pusha ; Set the port to the desired speed mov ebx, 0x3f8 ; combase mov edx, ebx add edx, 3 ; data format register mov al, 0x80 ; enable access to divisor latch out dx, al mov edx, ebx add edx, 1 ; interrupt enable register mov al, 0x00 ; No interruts enabled out dx, al mov edx, ebx mov al, 0x20 / 16 ; set baud rate to 57600 0x10 =115200 out dx, al mov edx, ebx add edx, 3 ; data format register mov al, 0x03 ; 8 data bits out dx, al mov edx, ebx add edx, 4 ; Modem control register mov al, 0x08 ; out2 enabled. No handshaking. out dx, al mov edx, ebx add edx, 1 ; interrupt enable register mov al, 0x01 ; Receive data interrupt enabled, out dx, al popa ; First, display the type of the buffer. ; If it is a tx buffer, display the macaddress pusha cmp eax, 0 jne dd001 mov bl, 0x0a call tx_byted mov bl, 0x0d call tx_byted ; Output "RX:" mov bl, 'R' call tx_byted mov bl, 'X' call tx_byted mov bl, ':' call tx_byted jmp dump_data dd001: mov bl, 0x0a call tx_byted mov bl, 0x0d call tx_byted ; Output TX: xxxxxxxxxxxx mov bl, 'T' call tx_byted mov bl, 'X' call tx_byted mov bl, ':' call tx_byted mov bl, ' ' call tx_byted ; Display MAC address xor eax, eax mov al, [edi] shr al, 4 mov bl, [eax + hexchars] call tx_byted ; byte in bl eax ebx edx destroyed xor eax, eax mov al, [edi] and al, 0x0f mov bl, [eax + hexchars] call tx_byted ; byte in bl eax ebx edx destroyed inc edi xor eax, eax mov al, [edi] shr al, 4 mov bl, [eax + hexchars] call tx_byted ; byte in bl eax ebx edx destroyed xor eax, eax mov al, [edi] and al, 0x0f mov bl, [eax + hexchars] call tx_byted ; byte in bl eax ebx edx destroyed inc edi xor eax, eax mov al, [edi] shr al, 4 mov bl, [eax + hexchars] call tx_byted ; byte in bl eax ebx edx destroyed xor eax, eax mov al, [edi] and al, 0x0f mov bl, [eax + hexchars] call tx_byted ; byte in bl eax ebx edx destroyed inc edi xor eax, eax mov al, [edi] shr al, 4 mov bl, [eax + hexchars] call tx_byted ; byte in bl eax ebx edx destroyed xor eax, eax mov al, [edi] and al, 0x0f mov bl, [eax + hexchars] call tx_byted ; byte in bl eax ebx edx destroyed inc edi xor eax, eax mov al, [edi] shr al, 4 mov bl, [eax + hexchars] call tx_byted ; byte in bl eax ebx edx destroyed xor eax, eax mov al, [edi] and al, 0x0f mov bl, [eax + hexchars] call tx_byted ; byte in bl eax ebx edx destroyed inc edi xor eax, eax mov al, [edi] shr al, 4 mov bl, [eax + hexchars] call tx_byted ; byte in bl eax ebx edx destroyed xor eax, eax mov al, [edi] and al, 0x0f mov bl, [eax + hexchars] call tx_byted ; byte in bl eax ebx edx destroyed dump_data: popa ; OK, we come in here with ; cx == number of byte to send ; esi == buffer start ; dd_000: mov bl, 0x0a call tx_byted mov bl, 0x0d call tx_byted mov eax, 16 ; Number of characters on the line mov edi, esi ; Save first byte position for later push ecx dd_001: push eax ; Print a byte, and a space xor eax, eax mov al, [esi] shr al, 4 mov bl, [eax + hexchars] call tx_byted ; byte in bl eax ebx edx destroyed xor eax, eax mov al, [esi] and al, 0x0f mov bl, [eax + hexchars] call tx_byted ; byte in bl eax ebx edx destroyed mov bl, ' ' call tx_byted pop eax inc esi dec ecx cmp ecx, 0 je dd_0011 ; Print the ASCII format dec eax cmp eax, 0 je dd_002 ; Print the ASCII format jmp dd_001 ; Print rest of line dd_0011: ; First, complete the 16 bytes of data, by printing spaces dec eax cmp eax, 0 je dd_002 push eax mov bl, ' ' call tx_byted mov bl, ' ' call tx_byted mov bl, ' ' call tx_byted pop eax jmp dd_0011 dd_002: pop ecx mov esi, edi ; Go back to the start of the line data mov eax, 16 outLineAscii: push eax xor eax, eax mov al, [esi] mov bl, '.' cmp al, 0x1F jle outAscii cmp al, 0x7e jge outAscii mov bl, al outAscii: call tx_byted ; byte in bl eax ebx edx destroyed pop eax dec ecx inc esi cmp ecx, 0 je dd_003 dec eax cmp eax, 0 je dd_003 jmp outLineAscii dd_003: cmp ecx, 0 je dd_004 jmp dd_000 dd_004: ret ;*************************************************************************** ; Function ; tx_byte ; ; Description ; Send a byte in bl out of the com port 1 ; destroys eax, edx ; ;*************************************************************************** tx_byted: push ebx ; Save the byte mov ebx, 0x3f8 ; get the com port address ; Wait for transmit buffer to empty. This could take 1ms @ 9600baud mov edx, ebx add edx, 5 wait_txd: in al, dx ; read uart serialisation status and al, 0x40 cmp al, 0 jz wait_txd ; loop until free mov edx, ebx pop eax ; restore the byte to send out dx, al ret iglobal ; This is used for translating hex to ASCII for display or output hexchars db '0','1','2','3','4','5','6','7','8','9','A','B','C','D','E','F' endg end if