1#!/usr/bin/env python 2# 3# Copyright (c) 2014 Marcel Moolenaar 4# All rights reserved. 5# 6# Redistribution and use in source and binary forms, with or without 7# modification, are permitted provided that the following conditions 8# are met: 9# 10# 1. Redistributions of source code must retain the above copyright 11# notice, this list of conditions and the following disclaimer. 12# 2. Redistributions in binary form must reproduce the above copyright 13# notice, this list of conditions and the following disclaimer in the 14# documentation and/or other materials provided with the distribution. 15# 16# THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 17# IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 18# OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 19# IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 20# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 21# NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 22# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 23# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 24# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 25# THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 26# 27# 28 29''' 30Simple diagnostics program fo the AMD Am89c900 series ILACC. 31This ethernet controller is emulated by VMware Fusion among 32possibly other virtualization platforms. 33 34The datasheet can be found here: 35 http://support.amd.com/TechDocs/18219.pdf 36 37This example program sends a single DHCP discovery packet, 38waits 2 seconds and then iterates over the receive ring for 39a targeted packet. 40 41For this program to function, connect the network interface 42to a network with a DHCP server. In VMware Fusion this can 43best be done by configuring the interface as a NAT interface 44using the "Share with my Mac" setting. 45''' 46 47import ctypes 48import logging 49import os 50import sys 51import time 52 53sys.path.append('/usr/lib') 54 55import bus 56from bus import dma as busdma 57 58 59# ILACC initialization block definition 60class initblock(ctypes.LittleEndianStructure): 61 _fields_ = [('mode', ctypes.c_uint32), 62 ('hwaddr', ctypes.c_uint8 * 6), 63 ('_pad1_', ctypes.c_uint16), 64 ('filter', ctypes.c_uint16 * 4), 65 ('rxdesc', ctypes.c_uint32), 66 ('txdesc', ctypes.c_uint32), 67 ('_pad2_', ctypes.c_uint32)] 68 69 70# ILACC ring buffer descriptor 71class bufdesc(ctypes.LittleEndianStructure): 72 _fields_ = [('buffer', ctypes.c_uint32), 73 ('flags', ctypes.c_uint32), 74 ('length', ctypes.c_uint32), 75 ('_pad_', ctypes.c_uint32)] 76 77 78# The DHCP packet definition (incl. all headers) 79class packet(ctypes.BigEndianStructure): 80 _pack_ = 1 81 _fields_ = [('eth_dest', ctypes.c_uint8 * 6), 82 ('eth_src', ctypes.c_uint8 * 6), 83 ('eth_type', ctypes.c_uint16), 84 ('ip_vl', ctypes.c_uint8), 85 ('ip_de', ctypes.c_uint8), 86 ('ip_len', ctypes.c_uint16), 87 ('ip_id', ctypes.c_uint16), 88 ('ip_ff', ctypes.c_uint16), 89 ('ip_ttl', ctypes.c_uint8), 90 ('ip_proto', ctypes.c_uint8), 91 ('ip_cksum', ctypes.c_uint16), 92 ('ip_src', ctypes.c_uint32), 93 ('ip_dest', ctypes.c_uint32), 94 ('udp_src', ctypes.c_uint16), 95 ('udp_dest', ctypes.c_uint16), 96 ('udp_len', ctypes.c_uint16), 97 ('udp_cksum', ctypes.c_uint16), 98 ('bootp_op', ctypes.c_uint8), 99 ('bootp_htype', ctypes.c_uint8), 100 ('bootp_hlen', ctypes.c_uint8), 101 ('bootp_hops', ctypes.c_uint8), 102 ('bootp_xid', ctypes.c_uint32), 103 ('bootp_secs', ctypes.c_uint16), 104 ('bootp_flags', ctypes.c_uint16), 105 ('bootp_ciaddr', ctypes.c_uint32), 106 ('bootp_yiaddr', ctypes.c_uint32), 107 ('bootp_siaddr', ctypes.c_uint32), 108 ('bootp_giaddr', ctypes.c_uint32), 109 ('bootp_chaddr', ctypes.c_uint8 * 16), 110 ('bootp_sname', ctypes.c_uint8 * 64), 111 ('bootp_file', ctypes.c_uint8 * 128), 112 ('dhcp_magic', ctypes.c_uint32), 113 ('dhcp_options', ctypes.c_uint8 * 60)] 114 115MACFMT = '%02x:%02x:%02x:%02x:%02x:%02x' 116 117dev = 'pci0:2:1:0' 118 119logging.basicConfig(level=logging.DEBUG) 120 121pcicfg = bus.map(dev, 'pcicfg') 122logging.debug('pcicfg=%s (%s)' % (pcicfg, dev)) 123 124vendor = bus.read_2(pcicfg, 0) 125device = bus.read_2(pcicfg, 2) 126if vendor != 0x1022 or device != 0x2000: 127 logging.error('Not an AMD PCnet-PCI (vendor=%x, device=%x)' % 128 (vendor, device)) 129 sys.exit(1) 130 131command = bus.read_2(pcicfg, 4) 132if not (command & 1): 133 logging.info('enabling I/O port decoding') 134 command |= 1 135 bus.write_2(pcicfg, 4, command) 136 137if not (command & 4): 138 logging.info('enabling bus mastering') 139 command |= 4 140 bus.write_2(pcicfg, 4, command) 141 142bus.unmap(pcicfg) 143 144io = bus.map(dev, '10.io') 145logging.debug('io=%s (%s)' % (io, dev)) 146 147 148def delay(msec): 149 time.sleep(msec / 1000.0) 150 151 152def ffs(x): 153 y = (1 + (x ^ (x-1))) >> 1 154 return y.bit_length() 155 156 157def ip_str(a): 158 return '%d.%d.%d.%d' % ((a >> 24) & 255, (a >> 16) & 255, (a >> 8) & 255, 159 a & 255) 160 161 162def mac_is(l, r): 163 for i in range(6): 164 if l[i] != r[i]: 165 return False 166 return True 167 168 169def mac_str(m): 170 return MACFMT % (m[0], m[1], m[2], m[3], m[4], m[5]) 171 172 173def rdbcr(reg): 174 bus.write_2(io, 0x12, reg & 0xffff) 175 return bus.read_2(io, 0x16) 176 177 178def wrbcr(reg, val): 179 bus.write_2(io, 0x12, reg & 0xffff) 180 bus.write_2(io, 0x16, val & 0xffff) 181 182 183def rdcsr(reg): 184 bus.write_2(io, 0x12, reg & 0xffff) 185 return bus.read_2(io, 0x10) 186 187 188def wrcsr(reg, val): 189 bus.write_2(io, 0x12, reg & 0xffff) 190 bus.write_2(io, 0x10, val & 0xffff) 191 192 193def start(): 194 wrcsr(0, 0x42) 195 delay(100) 196 197 198def stop(): 199 wrcsr(0, 4) 200 delay(100) 201 202 203mac = () 204bcast = () 205for o in range(6): 206 mac += (bus.read_1(io, o),) 207 bcast += (0xff,) 208logging.info('ethernet address = ' + MACFMT % mac) 209 210stop() 211wrbcr(20, 2) # reset 212wrcsr(3, 0) # byte swapping mode 213wrbcr(2, rdbcr(2) | 2) # Autoneg 214 215memsize = 32*1024 216bufsize = 1536 217nrxbufs = 16 218ntxbufs = 4 219logging.debug("DMA memory: size = %#x (TX buffers: %u, RX buffers: %u)" % 220 (memsize, ntxbufs, nrxbufs)) 221 222mem_tag = busdma.tag_create(dev, 16, 0, 0xffffffff, memsize, 1, memsize, 0, 0) 223dmamem = busdma.mem_alloc(mem_tag, 0) 224busseg = busdma.md_first_seg(dmamem, busdma.MD_BUS_SPACE) 225cpuseg = busdma.md_first_seg(dmamem, busdma.MD_VIRT_SPACE) 226busaddr = busdma.seg_get_addr(busseg) 227cpuaddr = busdma.seg_get_addr(cpuseg) 228logging.debug("DMA memory: CPU address: %#x, device address: %#x" % 229 (cpuaddr, busaddr)) 230 231addr_initblock = cpuaddr 232addr_rxdesc = addr_initblock + ctypes.sizeof(initblock) 233addr_txdesc = addr_rxdesc + ctypes.sizeof(bufdesc) * nrxbufs 234addr_rxbufs = addr_txdesc + ctypes.sizeof(bufdesc) * ntxbufs 235addr_txbufs = addr_rxbufs + bufsize * nrxbufs 236 237ib = initblock.from_address(addr_initblock) 238ib.mode = ((ffs(ntxbufs) - 1) << 28) | ((ffs(nrxbufs) - 1) << 20) 239for i in range(len(mac)): 240 ib.hwaddr[i] = mac[i] 241for i in range(4): 242 ib.filter[i] = 0xffff 243ib.rxdesc = busaddr + (addr_rxdesc - cpuaddr) 244ib.txdesc = busaddr + (addr_txdesc - cpuaddr) 245ib._pad1_ = 0 246ib._pad2_ = 0 247 248for i in range(nrxbufs): 249 bd = bufdesc.from_address(addr_rxdesc + ctypes.sizeof(bufdesc) * i) 250 bd.buffer = busaddr + (addr_rxbufs - cpuaddr) + bufsize * i 251 bd.flags = (1 << 31) | (15 << 12) | (-bufsize & 0xfff) 252 bd.length = 0 253 bd._pad_ = 0 254 255for i in range(ntxbufs): 256 bd = bufdesc.from_address(addr_txdesc + ctypes.sizeof(bufdesc) * i) 257 bd.buffer = busaddr + (addr_txbufs - cpuaddr) + bufsize * i 258 bd.flags = (15 << 12) 259 bd.length = 0 260 bd._pad_ = 0 261 262busdma.sync_range(dmamem, busdma.SYNC_PREWRITE, 0, addr_rxbufs - cpuaddr) 263 264# Program address of DMA memory 265wrcsr(1, busaddr) 266wrcsr(2, busaddr >> 16) 267delay(100) 268 269# Initialize hardware 270wrcsr(0, 1) 271logging.debug('Waiting for initialization to complete') 272csr = rdcsr(0) 273while (csr & 0x100) == 0: 274 logging.debug('CSR=%#x' % (csr)) 275 csr = rdcsr(0) 276 277start() 278 279pkt = packet.from_address(addr_txbufs) 280ctypes.memset(addr_txbufs, 0, ctypes.sizeof(pkt)) 281options = [53, 1, 1] 282for i in range(len(options)): 283 pkt.dhcp_options[i] = options[i] 284pkt.dhcp_magic = 0x63825363 285for i in range(6): 286 pkt.bootp_chaddr[i] = mac[i] 287pkt.bootp_hlen = 6 288pkt.bootp_htype = 1 289pkt.bootp_op = 1 290pkt.udp_len = ctypes.sizeof(pkt) - 34 291pkt.udp_dest = 67 292pkt.udp_src = 68 293pkt.ip_dest = 0xffffffff 294pkt.ip_cksum = 0x79a6 295pkt.ip_proto = 17 296pkt.ip_ttl = 64 297pkt.ip_len = ctypes.sizeof(pkt) - 14 298pkt.ip_vl = 0x45 299pkt.eth_type = 0x0800 300for i in range(6): 301 pkt.eth_src[i] = mac[i] 302 pkt.eth_dest[i] = bcast[i] 303pktlen = ctypes.sizeof(pkt) 304 305busdma.sync_range(dmamem, busdma.SYNC_PREWRITE, addr_txbufs - cpuaddr, bufsize) 306 307bd = bufdesc.from_address(addr_txdesc) 308bd.length = 0 309bd.flags = (1 << 31) | (1 << 25) | (1 << 24) | (0xf << 12) | (-pktlen & 0xfff) 310 311busdma.sync_range(dmamem, busdma.SYNC_PREWRITE, addr_txdesc - cpuaddr, 312 ctypes.sizeof(bufdesc)) 313 314wrcsr(0, 0x48) 315 316logging.info('DHCP discovery packet sent') 317 318# Now wait 2 seconds for a DHCP offer to be received. 319logging.debug('Waiting 2 seconds for an offer to be received') 320time.sleep(2) 321 322stop() 323 324busdma.sync_range(dmamem, busdma.SYNC_PREWRITE, addr_rxdesc - cpuaddr, 325 ctypes.sizeof(bufdesc) * nrxbufs) 326 327for i in range(nrxbufs): 328 bd = bufdesc.from_address(addr_rxdesc + ctypes.sizeof(bufdesc) * i) 329 if (bd.flags & 0x80000000): 330 continue 331 pkt = packet.from_address(addr_rxbufs + i * bufsize) 332 if mac_is(pkt.eth_dest, bcast): 333 logging.debug('RX #%d: broadcast packet: length %u' % (i, bd.length)) 334 continue 335 if not mac_is(pkt.eth_dest, mac): 336 logging.debug('RX #%d: packet for %s?' % (i, mac_str(pkt.eth_dest))) 337 continue 338 logging.debug('RX %d: packet from %s!' % (i, mac_str(pkt.eth_src))) 339 logging.info('Our IP address = %s' % (ip_str(pkt.ip_dest))) 340 341busdma.mem_free(dmamem) 342busdma.tag_destroy(mem_tag) 343bus.unmap(io) 344