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