1286212Smarcel#!/usr/bin/env python 2286212Smarcel# 3286212Smarcel# Copyright (c) 2014 Marcel Moolenaar 4286212Smarcel# All rights reserved. 5286212Smarcel# 6286212Smarcel# Redistribution and use in source and binary forms, with or without 7286212Smarcel# modification, are permitted provided that the following conditions 8286212Smarcel# are met: 9286212Smarcel# 10286212Smarcel# 1. Redistributions of source code must retain the above copyright 11286212Smarcel# notice, this list of conditions and the following disclaimer. 12286212Smarcel# 2. Redistributions in binary form must reproduce the above copyright 13286212Smarcel# notice, this list of conditions and the following disclaimer in the 14286212Smarcel# documentation and/or other materials provided with the distribution. 15286212Smarcel# 16286212Smarcel# THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 17286212Smarcel# IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 18286212Smarcel# OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 19286212Smarcel# IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 20286212Smarcel# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 21286212Smarcel# NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 22286212Smarcel# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 23286212Smarcel# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 24286212Smarcel# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 25286212Smarcel# THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 26286212Smarcel# 27286212Smarcel# $FreeBSD$ 28286212Smarcel# 29286212Smarcel 30286212Smarcel''' 31286212SmarcelSimple diagnostics program fo the AMD Am89c900 series ILACC. 32286212SmarcelThis ethernet controller is emulated by VMware Fusion among 33286212Smarcelpossibly other virtualization platforms. 34286212Smarcel 35286212SmarcelThe datasheet can be found here: 36286212Smarcel http://support.amd.com/TechDocs/18219.pdf 37286212Smarcel 38286212SmarcelThis example program sends a single DHCP discovery packet, 39286212Smarcelwaits 2 seconds and then iterates over the receive ring for 40286212Smarcela targeted packet. 41286212Smarcel 42286212SmarcelFor this program to function, connect the network interface 43286212Smarcelto a network with a DHCP server. In VMware Fusion this can 44286212Smarcelbest be done by configuring the interface as a NAT interface 45286212Smarcelusing the "Share with my Mac" setting. 46286212Smarcel''' 47286212Smarcel 48286212Smarcelimport ctypes 49286212Smarcelimport logging 50286212Smarcelimport os 51286212Smarcelimport sys 52286212Smarcelimport time 53286212Smarcel 54286212Smarcelsys.path.append('/usr/lib') 55286212Smarcel 56286212Smarcelimport bus 57286212Smarcelimport busdma 58286212Smarcel 59286212Smarcel 60286212Smarcel# ILACC initialization block definition 61286212Smarcelclass initblock(ctypes.LittleEndianStructure): 62286212Smarcel _fields_ = [('mode', ctypes.c_uint32), 63286212Smarcel ('hwaddr', ctypes.c_uint8 * 6), 64286212Smarcel ('_pad1_', ctypes.c_uint16), 65286212Smarcel ('filter', ctypes.c_uint16 * 4), 66286212Smarcel ('rxdesc', ctypes.c_uint32), 67286212Smarcel ('txdesc', ctypes.c_uint32), 68286212Smarcel ('_pad2_', ctypes.c_uint32)] 69286212Smarcel 70286212Smarcel 71286212Smarcel# ILACC ring buffer descriptor 72286212Smarcelclass bufdesc(ctypes.LittleEndianStructure): 73286212Smarcel _fields_ = [('buffer', ctypes.c_uint32), 74286212Smarcel ('flags', ctypes.c_uint32), 75286212Smarcel ('length', ctypes.c_uint32), 76286212Smarcel ('_pad_', ctypes.c_uint32)] 77286212Smarcel 78286212Smarcel 79286212Smarcel# The DHCP packet definition (incl. all headers) 80286212Smarcelclass packet(ctypes.BigEndianStructure): 81286212Smarcel _pack_ = 1 82286212Smarcel _fields_ = [('eth_dest', ctypes.c_uint8 * 6), 83286212Smarcel ('eth_src', ctypes.c_uint8 * 6), 84286212Smarcel ('eth_type', ctypes.c_uint16), 85286212Smarcel ('ip_vl', ctypes.c_uint8), 86286212Smarcel ('ip_de', ctypes.c_uint8), 87286212Smarcel ('ip_len', ctypes.c_uint16), 88286212Smarcel ('ip_id', ctypes.c_uint16), 89286212Smarcel ('ip_ff', ctypes.c_uint16), 90286212Smarcel ('ip_ttl', ctypes.c_uint8), 91286212Smarcel ('ip_proto', ctypes.c_uint8), 92286212Smarcel ('ip_cksum', ctypes.c_uint16), 93286212Smarcel ('ip_src', ctypes.c_uint32), 94286212Smarcel ('ip_dest', ctypes.c_uint32), 95286212Smarcel ('udp_src', ctypes.c_uint16), 96286212Smarcel ('udp_dest', ctypes.c_uint16), 97286212Smarcel ('udp_len', ctypes.c_uint16), 98286212Smarcel ('udp_cksum', ctypes.c_uint16), 99286212Smarcel ('bootp_op', ctypes.c_uint8), 100286212Smarcel ('bootp_htype', ctypes.c_uint8), 101286212Smarcel ('bootp_hlen', ctypes.c_uint8), 102286212Smarcel ('bootp_hops', ctypes.c_uint8), 103286212Smarcel ('bootp_xid', ctypes.c_uint32), 104286212Smarcel ('bootp_secs', ctypes.c_uint16), 105286212Smarcel ('bootp_flags', ctypes.c_uint16), 106286212Smarcel ('bootp_ciaddr', ctypes.c_uint32), 107286212Smarcel ('bootp_yiaddr', ctypes.c_uint32), 108286212Smarcel ('bootp_siaddr', ctypes.c_uint32), 109286212Smarcel ('bootp_giaddr', ctypes.c_uint32), 110286212Smarcel ('bootp_chaddr', ctypes.c_uint8 * 16), 111286212Smarcel ('bootp_sname', ctypes.c_uint8 * 64), 112286212Smarcel ('bootp_file', ctypes.c_uint8 * 128), 113286212Smarcel ('dhcp_magic', ctypes.c_uint32), 114286212Smarcel ('dhcp_options', ctypes.c_uint8 * 60)] 115286212Smarcel 116286212SmarcelMACFMT = '%02x:%02x:%02x:%02x:%02x:%02x' 117286212Smarcel 118286212Smarceldev = 'pci0:2:1:0' 119286212Smarcel 120286212Smarcellogging.basicConfig(level=logging.DEBUG) 121286212Smarcel 122286212Smarcelpcicfg = bus.map(dev, 'pcicfg') 123286212Smarcellogging.debug('pcicfg=%s (%s)' % (pcicfg, dev)) 124286212Smarcel 125286212Smarcelvendor = bus.read_2(pcicfg, 0) 126286212Smarceldevice = bus.read_2(pcicfg, 2) 127286212Smarcelif vendor != 0x1022 or device != 0x2000: 128286212Smarcel logging.error('Not an AMD PCnet-PCI (vendor=%x, device=%x)' % 129286212Smarcel (vendor, device)) 130286212Smarcel sys.exit(1) 131286212Smarcel 132286212Smarcelcommand = bus.read_2(pcicfg, 4) 133286212Smarcelif not (command & 1): 134286212Smarcel logging.info('enabling I/O port decoding') 135286212Smarcel command |= 1 136286212Smarcel bus.write_2(pcicfg, 4, command) 137286212Smarcel 138286212Smarcelif not (command & 4): 139286212Smarcel logging.info('enabling bus mastering') 140286212Smarcel command |= 4 141286212Smarcel bus.write_2(pcicfg, 4, command) 142286212Smarcel 143286212Smarcelbus.unmap(pcicfg) 144286212Smarcel 145286212Smarcelio = bus.map(dev, '10.io') 146286212Smarcellogging.debug('io=%s (%s)' % (io, dev)) 147286212Smarcel 148286212Smarcel 149286212Smarceldef delay(msec): 150286212Smarcel time.sleep(msec / 1000.0) 151286212Smarcel 152286212Smarcel 153286212Smarceldef ffs(x): 154286212Smarcel y = (1 + (x ^ (x-1))) >> 1 155286212Smarcel return y.bit_length() 156286212Smarcel 157286212Smarcel 158286212Smarceldef ip_str(a): 159286212Smarcel return '%d.%d.%d.%d' % ((a >> 24) & 255, (a >> 16) & 255, (a >> 8) & 255, 160286212Smarcel a & 255) 161286212Smarcel 162286212Smarcel 163286212Smarceldef mac_is(l, r): 164286212Smarcel for i in xrange(6): 165286212Smarcel if l[i] != r[i]: 166286212Smarcel return False 167286212Smarcel return True 168286212Smarcel 169286212Smarcel 170286212Smarceldef mac_str(m): 171286212Smarcel return MACFMT % (m[0], m[1], m[2], m[3], m[4], m[5]) 172286212Smarcel 173286212Smarcel 174286212Smarceldef rdbcr(reg): 175286212Smarcel bus.write_2(io, 0x12, reg & 0xffff) 176286212Smarcel return bus.read_2(io, 0x16) 177286212Smarcel 178286212Smarcel 179286212Smarceldef wrbcr(reg, val): 180286212Smarcel bus.write_2(io, 0x12, reg & 0xffff) 181286212Smarcel bus.write_2(io, 0x16, val & 0xffff) 182286212Smarcel 183286212Smarcel 184286212Smarceldef rdcsr(reg): 185286212Smarcel bus.write_2(io, 0x12, reg & 0xffff) 186286212Smarcel return bus.read_2(io, 0x10) 187286212Smarcel 188286212Smarcel 189286212Smarceldef wrcsr(reg, val): 190286212Smarcel bus.write_2(io, 0x12, reg & 0xffff) 191286212Smarcel bus.write_2(io, 0x10, val & 0xffff) 192286212Smarcel 193286212Smarcel 194286212Smarceldef start(): 195286212Smarcel wrcsr(0, 0x42) 196286212Smarcel delay(100) 197286212Smarcel 198286212Smarcel 199286212Smarceldef stop(): 200286212Smarcel wrcsr(0, 4) 201286212Smarcel delay(100) 202286212Smarcel 203286212Smarcel 204286212Smarcelmac = () 205286212Smarcelbcast = () 206286212Smarcelfor o in xrange(6): 207286212Smarcel mac += (bus.read_1(io, o),) 208286212Smarcel bcast += (0xff,) 209286212Smarcellogging.info('ethernet address = ' + MACFMT % mac) 210286212Smarcel 211286212Smarcelstop() 212286212Smarcelwrbcr(20, 2) # reset 213286212Smarcelwrcsr(3, 0) # byte swapping mode 214286212Smarcelwrbcr(2, rdbcr(2) | 2) # Autoneg 215286212Smarcel 216286212Smarcelmemsize = 32*1024 217286212Smarcelbufsize = 1536 218286212Smarcelnrxbufs = 16 219286212Smarcelntxbufs = 4 220286212Smarcellogging.debug("DMA memory: size = %#x (TX buffers: %u, RX buffers: %u)" % 221286212Smarcel (memsize, ntxbufs, nrxbufs)) 222286212Smarcel 223286212Smarcelmem_tag = busdma.tag_create(dev, 16, 0, 0xffffffff, memsize, 1, memsize, 0, 0) 224286212Smarceldmamem = busdma.mem_alloc(mem_tag, 0) 225286212Smarcelbusseg = busdma.md_first_seg(dmamem, busdma.MD_BUS_SPACE) 226286212Smarcelcpuseg = busdma.md_first_seg(dmamem, busdma.MD_VIRT_SPACE) 227286212Smarcelbusaddr = busdma.seg_get_addr(busseg) 228286212Smarcelcpuaddr = busdma.seg_get_addr(cpuseg) 229286212Smarcellogging.debug("DMA memory: CPU address: %#x, device address: %#x" % 230286212Smarcel (cpuaddr, busaddr)) 231286212Smarcel 232286212Smarceladdr_initblock = cpuaddr 233286212Smarceladdr_rxdesc = addr_initblock + ctypes.sizeof(initblock) 234286212Smarceladdr_txdesc = addr_rxdesc + ctypes.sizeof(bufdesc) * nrxbufs 235286212Smarceladdr_rxbufs = addr_txdesc + ctypes.sizeof(bufdesc) * ntxbufs 236286212Smarceladdr_txbufs = addr_rxbufs + bufsize * nrxbufs 237286212Smarcel 238286212Smarcelib = initblock.from_address(addr_initblock) 239286212Smarcelib.mode = ((ffs(ntxbufs) - 1) << 28) | ((ffs(nrxbufs) - 1) << 20) 240286212Smarcelfor i in xrange(len(mac)): 241286212Smarcel ib.hwaddr[i] = mac[i] 242286212Smarcelfor i in xrange(4): 243286212Smarcel ib.filter[i] = 0xffff 244286212Smarcelib.rxdesc = busaddr + (addr_rxdesc - cpuaddr) 245286212Smarcelib.txdesc = busaddr + (addr_txdesc - cpuaddr) 246286212Smarcelib._pad1_ = 0 247286212Smarcelib._pad2_ = 0 248286212Smarcel 249286212Smarcelfor i in xrange(nrxbufs): 250286212Smarcel bd = bufdesc.from_address(addr_rxdesc + ctypes.sizeof(bufdesc) * i) 251286212Smarcel bd.buffer = busaddr + (addr_rxbufs - cpuaddr) + bufsize * i 252286212Smarcel bd.flags = (1 << 31) | (15 << 12) | (-bufsize & 0xfff) 253286212Smarcel bd.length = 0 254286212Smarcel bd._pad_ = 0 255286212Smarcel 256286212Smarcelfor i in xrange(ntxbufs): 257286212Smarcel bd = bufdesc.from_address(addr_txdesc + ctypes.sizeof(bufdesc) * i) 258286212Smarcel bd.buffer = busaddr + (addr_txbufs - cpuaddr) + bufsize * i 259286212Smarcel bd.flags = (15 << 12) 260286212Smarcel bd.length = 0 261286212Smarcel bd._pad_ = 0 262286212Smarcel 263286212Smarcelbusdma.sync_range(dmamem, busdma.SYNC_PREWRITE, 0, addr_rxbufs - cpuaddr) 264286212Smarcel 265286212Smarcel# Program address of DMA memory 266286212Smarcelwrcsr(1, busaddr) 267286212Smarcelwrcsr(2, busaddr >> 16) 268286212Smarceldelay(100) 269286212Smarcel 270286212Smarcel# Initialize hardware 271286212Smarcelwrcsr(0, 1) 272286212Smarcellogging.debug('Waiting for initialization to complete') 273286212Smarcelcsr = rdcsr(0) 274286212Smarcelwhile (csr & 0x100) == 0: 275286212Smarcel logging.debug('CSR=%#x' % (csr)) 276286212Smarcel csr = rdcsr(0) 277286212Smarcel 278286212Smarcelstart() 279286212Smarcel 280286212Smarcelpkt = packet.from_address(addr_txbufs) 281286212Smarcelctypes.memset(addr_txbufs, 0, ctypes.sizeof(pkt)) 282286212Smarceloptions = [53, 1, 1] 283286212Smarcelfor i in xrange(len(options)): 284286212Smarcel pkt.dhcp_options[i] = options[i] 285286212Smarcelpkt.dhcp_magic = 0x63825363 286286212Smarcelfor i in xrange(6): 287286212Smarcel pkt.bootp_chaddr[i] = mac[i] 288286212Smarcelpkt.bootp_hlen = 6 289286212Smarcelpkt.bootp_htype = 1 290286212Smarcelpkt.bootp_op = 1 291286212Smarcelpkt.udp_len = ctypes.sizeof(pkt) - 34 292286212Smarcelpkt.udp_dest = 67 293286212Smarcelpkt.udp_src = 68 294286212Smarcelpkt.ip_dest = 0xffffffff 295286212Smarcelpkt.ip_cksum = 0x79a6 296286212Smarcelpkt.ip_proto = 17 297286212Smarcelpkt.ip_ttl = 64 298286212Smarcelpkt.ip_len = ctypes.sizeof(pkt) - 14 299286212Smarcelpkt.ip_vl = 0x45 300286212Smarcelpkt.eth_type = 0x0800 301286212Smarcelfor i in xrange(6): 302286212Smarcel pkt.eth_src[i] = mac[i] 303286212Smarcel pkt.eth_dest[i] = bcast[i] 304286212Smarcelpktlen = ctypes.sizeof(pkt) 305286212Smarcel 306286212Smarcelbusdma.sync_range(dmamem, busdma.SYNC_PREWRITE, addr_txbufs - cpuaddr, bufsize) 307286212Smarcel 308286212Smarcelbd = bufdesc.from_address(addr_txdesc) 309286212Smarcelbd.length = 0 310286212Smarcelbd.flags = (1 << 31) | (1 << 25) | (1 << 24) | (0xf << 12) | (-pktlen & 0xfff) 311286212Smarcel 312286212Smarcelbusdma.sync_range(dmamem, busdma.SYNC_PREWRITE, addr_txdesc - cpuaddr, 313286212Smarcel ctypes.sizeof(bufdesc)) 314286212Smarcel 315286212Smarcelwrcsr(0, 0x48) 316286212Smarcel 317286212Smarcellogging.info('DHCP discovery packet sent') 318286212Smarcel 319286212Smarcel# Now wait 2 seconds for a DHCP offer to be received. 320286212Smarcellogging.debug('Waiting 2 seconds for an offer to be received') 321286212Smarceltime.sleep(2) 322286212Smarcel 323286212Smarcelstop() 324286212Smarcel 325286212Smarcelbusdma.sync_range(dmamem, busdma.SYNC_PREWRITE, addr_rxdesc - cpuaddr, 326286212Smarcel ctypes.sizeof(bufdesc) * nrxbufs) 327286212Smarcel 328286212Smarcelfor i in xrange(nrxbufs): 329286212Smarcel bd = bufdesc.from_address(addr_rxdesc + ctypes.sizeof(bufdesc) * i) 330286212Smarcel if (bd.flags & 0x80000000): 331286212Smarcel continue 332286212Smarcel pkt = packet.from_address(addr_rxbufs + i * bufsize) 333286212Smarcel if mac_is(pkt.eth_dest, bcast): 334286212Smarcel logging.debug('RX #%d: broadcast packet: length %u' % (i, bd.length)) 335286212Smarcel continue 336286212Smarcel if not mac_is(pkt.eth_dest, mac): 337286212Smarcel logging.debug('RX #%d: packet for %s?' % (i, mac_str(pkt.eth_dest))) 338286212Smarcel continue 339286212Smarcel logging.debug('RX %d: packet from %s!' % (i, mac_str(pkt.eth_src))) 340286212Smarcel logging.info('Our IP address = %s' % (ip_str(pkt.ip_dest))) 341286212Smarcel 342286212Smarcelbusdma.mem_free(dmamem) 343286212Smarcelbusdma.tag_destroy(mem_tag) 344286212Smarcelbus.unmap(io) 345